ContentSecurityController.java 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. package com.zhentao.controller;
  2. import com.zhentao.service.YidunSecurityService;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import org.springframework.http.MediaType;
  6. import org.springframework.web.bind.annotation.PostMapping;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.ResponseBody;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import javax.servlet.http.HttpServletRequest;
  11. import java.io.*;
  12. import java.nio.charset.StandardCharsets;
  13. import java.util.Arrays;
  14. import java.util.List;
  15. import java.util.regex.Matcher;
  16. import java.util.regex.Pattern;
  17. /**
  18. * 内容安全检测控制器
  19. * 用于检测用户发布的文字、图片是否包含违规内容
  20. * 使用网易易盾内容安全API
  21. */
  22. @RestController
  23. @RequestMapping("/api/content-security")
  24. public class ContentSecurityController {
  25. private static final Logger logger = LoggerFactory.getLogger(ContentSecurityController.class);
  26. // 注入网易易盾内容安全服务
  27. private final YidunSecurityService yidunSecurityService;
  28. // 构造函数注入
  29. public ContentSecurityController(YidunSecurityService yidunSecurityService) {
  30. this.yidunSecurityService = yidunSecurityService;
  31. }
  32. private static final List<String> SENSITIVE_WORDS = Arrays.asList(
  33. "色情", "裸体", "性爱", "约炮", "一夜情", "援交", "卖淫", "嫖娼", "小姐服务",
  34. "做爱", "性交", "口交", "肛交", "自慰", "手淫",
  35. "傻逼", "操你", "草泥马", "妈的", "他妈的", "狗日的", "王八蛋", "贱人", "婊子",
  36. "滚蛋", "去死", "废物", "白痴", "智障",
  37. "赌博", "毒品", "枪支", "炸弹", "恐怖", "暴力", "杀人", "自杀",
  38. "刷单", "兼职日结", "高额返利", "免费领取", "中奖", "彩票内幕", "稳赚不赔",
  39. "法轮功", "邪教"
  40. );
  41. /**
  42. * 检测文字内容是否安全
  43. */
  44. @PostMapping(value = "/check-text", produces = MediaType.APPLICATION_JSON_VALUE)
  45. @ResponseBody
  46. public String checkText(HttpServletRequest request) {
  47. String content = null;
  48. try {
  49. String body = readRequestBody(request);
  50. content = getJsonString(body, "content");
  51. } catch (Exception e) {
  52. logger.error("读取文字检测请求体失败", e);
  53. return buildResponse(false, "请求参数解析失败");
  54. }
  55. if (content == null || content.trim().isEmpty()) {
  56. return buildResponse(true, "");
  57. }
  58. logger.info("开始检测文字内容,长度: {}", content.length());
  59. // 第一层:本地敏感词快速检测
  60. String localCheckResult = localTextCheck(content);
  61. if (localCheckResult != null) {
  62. logger.info("本地敏感词检测不通过: {}", localCheckResult);
  63. return buildResponse(false, "内容包含敏感词,请修改后重试");
  64. }
  65. // 第二层:网易易盾内容安全检测(AI智能检测)
  66. try {
  67. String yidunResult = yidunSecurityService.checkText(content);
  68. if (yidunResult != null) {
  69. logger.info("网易易盾内容安全检测不通过: {}", yidunResult);
  70. return buildResponse(false, yidunResult);
  71. }
  72. } catch (Exception e) {
  73. logger.error("网易易盾文字内容安全检测异常", e);
  74. }
  75. logger.info("文字内容检测通过(已通过本地+网易易盾检测)");
  76. return buildResponse(true, "");
  77. }
  78. /**
  79. * 检测图片是否安全
  80. * 使用网易易盾内容安全API进行检测
  81. */
  82. @PostMapping(value = "/check-image", produces = MediaType.APPLICATION_JSON_VALUE)
  83. @ResponseBody
  84. public String checkImage(HttpServletRequest request) {
  85. String imageUrl = null;
  86. try {
  87. String body = readRequestBody(request);
  88. logger.info("收到图片检测请求: {}", body);
  89. imageUrl = getJsonString(body, "imageUrl");
  90. } catch (Exception e) {
  91. logger.error("读取图片检测请求体失败", e);
  92. return buildResponse(false, "请求参数错误");
  93. }
  94. if (imageUrl == null || imageUrl.trim().isEmpty()) {
  95. logger.warn("图片URL为空,跳过检测");
  96. return buildResponse(true, "");
  97. }
  98. logger.info("开始检测图片: {}", imageUrl);
  99. // 使用网易易盾图片内容安全检测
  100. try {
  101. String yidunResult = yidunSecurityService.checkImage(imageUrl);
  102. if (yidunResult != null) {
  103. logger.info("网易易盾图片检测不通过: {}", yidunResult);
  104. return buildResponse(false, yidunResult);
  105. }
  106. } catch (Exception e) {
  107. logger.error("网易易盾图片内容安全检测异常", e);
  108. }
  109. logger.info("图片检测通过: {}", imageUrl);
  110. return buildResponse(true, "");
  111. }
  112. /**
  113. * 构建统一JSON响应体
  114. */
  115. private String buildResponse(boolean safe, String message) {
  116. return "{\"code\":200,\"message\":\"成功\",\"data\":{\"safe\":" + safe +
  117. ",\"message\":\"" + escapeJson(message) + "\"},\"timestamp\":" +
  118. System.currentTimeMillis() + "}";
  119. }
  120. /**
  121. * 读取request请求体
  122. */
  123. private String readRequestBody(HttpServletRequest request) throws IOException {
  124. StringBuilder sb = new StringBuilder();
  125. try (BufferedReader reader = request.getReader()) {
  126. String line;
  127. while ((line = reader.readLine()) != null) {
  128. sb.append(line);
  129. }
  130. }
  131. return sb.toString();
  132. }
  133. /**
  134. * 本地敏感词检测-忽略大小写
  135. */
  136. private String localTextCheck(String content) {
  137. String lowerContent = content.toLowerCase();
  138. for (String word : SENSITIVE_WORDS) {
  139. if (lowerContent.contains(word.toLowerCase())) {
  140. return word;
  141. }
  142. }
  143. return null;
  144. }
  145. // ====================== 通用工具方法 ======================
  146. /**
  147. * 标准JSON字符串转义,补全所有缺失的转义字符
  148. */
  149. private String escapeJson(String str) {
  150. if (str == null) return "";
  151. StringBuilder sb = new StringBuilder();
  152. for (char c : str.toCharArray()) {
  153. switch (c) {
  154. case '\"': sb.append("\\\""); break;
  155. case '\\': sb.append("\\\\"); break;
  156. case '/': sb.append("\\/"); break;
  157. case '\b': sb.append("\\b"); break;
  158. case '\f': sb.append("\\f"); break;
  159. case '\n': sb.append("\\n"); break;
  160. case '\r': sb.append("\\r"); break;
  161. case '\t': sb.append("\\t"); break;
  162. default: sb.append(c); break;
  163. }
  164. }
  165. return sb.toString();
  166. }
  167. /**
  168. * 正则解析JSON字符串中的字符串值,兼容空格/换行
  169. */
  170. private String getJsonString(String json, String key) {
  171. if (json == null || key == null) return null;
  172. Pattern pattern = Pattern.compile("\"" + key + "\"\\s*:\\s*\"([^\"]*)\"");
  173. Matcher matcher = pattern.matcher(json);
  174. return matcher.find() ? matcher.group(1) : null;
  175. }
  176. }