Java开发经验——阿里巴巴编码规范实践解析5
摘要
这篇文章主要介绍了阿里巴巴Java开发规范中关于安全和性能优化的实践解析。内容涵盖了配置文件密码加密、用户输入内容风控、SQL注入防护、参数有效性验证、XSS攻击防护、CSRF安全验证、文件上传安全检查、防重放机制等多个方面,通过正反示例和推荐做法,为Java开发者提供了详细的安全开发指导。
1. 【强制】隶属于用户个人的页面或者功能必须进行权限控制校验。
说明:防止没有做水平权限校验就可随意访问、修改、删除别人的数据,比如查看他人的私信内容。
这是一个涉及系统安全和用户隐私保护的强制性安全规范,
1.1. 什么是水平权限校验?
水平权限控制是指:同一角色的用户只能访问属于自己权限范围的数据。
例如:用户 A 登录系统后,只能访问自己的订单、地址、消息等;不能看到或操作用户 B 的内容。
1.2. 为什么必须校验?
如果不做水平权限控制:
- 用户 A 可以通过修改 URL 参数或请求体中的
userId=123
来访问或修改 用户 B 的数据; - 这是严重的越权访问漏洞,可能导致信息泄露、数据篡改,甚至被黑产利用。
1.3. ✅ 正例(正确做法)
以 Spring Boot + MyBatis 为例,查询用户订单时加上当前登录用户 ID:
// Controller
@GetMapping("/order/{orderId}")
public Order getOrder(@PathVariable Long orderId, @AuthenticationPrincipal UserDetails user) {Long userId = Long.parseLong(user.getUsername());return orderService.getOrderForUser(orderId, userId);
}// Service
public Order getOrderForUser(Long orderId, Long userId) {Order order = orderMapper.findByIdAndUserId(orderId, userId);if (order == null) {throw new BusinessException("无权访问该订单");}return order;
}
数据库 SQL 示例(MyBatis):
SELECT * FROM orders WHERE id = #{orderId} AND user_id = #{userId}
1.4. ❌ 反例(错误做法)
// 未验证 userId 是否为当前登录用户
public Order getOrder(Long orderId) {return orderMapper.findById(orderId); // 任何用户可访问任意订单
}
1.5. ✅ 推荐校验方法
场景 | 校验方式 |
URL 中含 userId、orderId 等 | 使用登录用户的 ID 与传参比对,或直接从 Token 中获取 |
查询/删除数据库记录 | SQL 中加上 |
操作数据表单 | 后端验证该数据是否属于当前用户 |
接口返回列表 | 查询条件中加上用户 ID 限制 |
2. 【强制】用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
正例:中国大陆个人手机号码显示:139****1219,隐藏中间 4 位,防止隐私泄露。
2.1. ✅ 正例代码示例
User user = getUser();// 脱敏处理
String maskedPhone = DesensitizationUtil.maskPhone(user.getPhone());
String maskedIdCard = DesensitizationUtil.maskIdCard(user.getIdCard());logger.info("用户信息 - userId: {}, phone: {}, idCard: {}", user.getId(), maskedPhone, maskedIdCard);
推荐在日志中使用 userId
、orderId
、uuid
等非敏感的唯一标识进行问题定位。
2.2. ❌ 反例代码(绝对禁止)
java复制编辑
logger.info("用户信息 - 姓名: {}, 身份证: {}, 手机号: {}", user.getName(), user.getIdCard(), user.getPhone());
// 泄露完整敏感信息,严重违规
2.3. ✅ 推荐脱敏工具类 DesensitizationUtil
public class DesensitizationUtil {public static String maskPhone(String phone) {if (phone == null || phone.length() != 11) return phone;return phone.substring(0, 3) + "****" + phone.substring(7);}public static String maskIdCard(String idCard) {if (idCard == null || idCard.length() < 8) return idCard;return idCard.substring(0, 3) + "***********" + idCard.substring(idCard.length() - 4);}public static String maskName(String name) {if (name == null || name.length() < 2) return "*";return name.charAt(0) + "*".repeat(name.length() - 1);}
}
3. 【强制】用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入,禁止字符串拼接 SQL 访问数据库。
反例:某系统签名大量被恶意修改,即是因为对于危险字符#--没有进行转义,导致数据库更新时,where 后边的信息被注释掉,对全库进行更新。
3.1. 什么是 SQL 注入?
SQL 注入是指:攻击者通过构造恶意 SQL 片段注入到应用程序的参数中,最终执行了非预期的数据库操作。
例如,攻击者输入:
1 OR 1=1
可能变成:
SELECT * FROM users WHERE id = 1 OR 1=1
这样会返回所有用户,甚至更严重。
3.2. 为什么不能拼接 SQL?
- 字符串拼接 SQL 是最常见的安全漏洞;
- 没有过滤
--
、;
、'
、"
、or
等关键词,会导致注入; - 攻击者可以:
-
- 绕过登录;
- 窃取数据;
- 删除表结构;
- 执行后台命令(部分数据库支持);
3.3. ❌ 反例(错误示范)
// 错误示例:拼接 SQL 字符串(极其危险)
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
如果用户输入:
username: admin' --
password: xxx
SQL 变为:
SELECT * FROM users WHERE username = 'admin' -- ' AND password = 'xxx'
--
将后半部分注释掉,绕过密码校验,直接登录。
3.4. ✅ 正例(参数绑定)
3.4.1. 使用 PreparedStatement(JDBC):
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();
3.4.2. 使用 MyBatis:
<select id="getUserByUsernameAndPassword" resultType="User">SELECT * FROM users WHERE username = #{username} AND password = #{password}
</select>
MyBatis 会自动使用 PreparedStatement
参数绑定,防止注入。
3.5. ❗️元数据字段限制(METADATA字段约束)
在某些系统中,还可以通过字段白名单、枚举映射来限制参数取值:
// 限定只允许操作指定字段
List<String> allowedFields = Arrays.asList("name", "email", "phone");
if (!allowedFields.contains(inputField)) {throw new IllegalArgumentException("非法字段参数");
}
4. 【强制】用户请求传入的任何参数必须做有效性验证。
说明:忽略参数校验可能导致:
- 页面 page size 过大导致内存溢出
- 恶意 order by 导致数据库慢查询
- 缓存击穿
- SSRF
- 任意重定向
- SQL 注入, Shell 注入, 反序列化注入
- 正则输入源串拒绝服务 ReDoS
扩展: Java 代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使
用的是特殊构造的字符串来验证,有可能导致死循环的结果。
所有用户传入的参数(URL、Header、Body、Query 等)都必须进行有效性验证,否则可能被恶意利用,造成安全漏洞或系统稳定性问题。
4.1. 不校验参数的后果
问题类型 | 举例说明 |
内存溢出 | 用户传入 pageSize=1000000,系统一次性加载百万数据,直接 OOM |
数据库慢查询 | 用户传入 |
缓存击穿 | 用户不断请求不存在的数据 ID,频繁绕过缓存,打到数据库 |
SSRF(服务端请求伪造) | 用户传入图片 URL 为 |
任意重定向 | 用户传入 redirectUrl=恶意网站,跳转诱导点击 |
SQL注入 / Shell注入 / 反序列化注入 | 拼接参数时未校验引号、分号、命令符等危险字符 |
ReDoS(正则拒绝服务) | 用户传入字符串导致 Java 正则表达式陷入指数型回溯,CPU 满载 |
4.2. ❌ 反例(没有参数校验)
@GetMapping("/search")
public List<User> search(String keyword, Integer pageSize) {// 用户传入 keyword = ".*(a+)+.*" pageSize = 1000000List<User> users = userService.search(keyword, pageSize); return users;
}
问题:
- 正则 ReDoS;
- pageSize 太大导致内存泄漏;
- keyword 可能造成数据库慢查或注入。
4.3. ✅ 正例(有效参数校验)
@GetMapping("/search")
public List<User> search(@RequestParam @Size(max = 100) String keyword,@RequestParam(defaultValue = "10") @Min(1) @Max(100) Integer pageSize) {if (!Pattern.matches("[a-zA-Z0-9]+", keyword)) {throw new IllegalArgumentException("非法关键字");}return userService.search(keyword, pageSize);
}
4.4. 📌 校验建议分类
类型 | 校验方式 |
文本 | 长度、正则、白名单 |
数字 | 最大/最小值限制,分页页码控制 |
列表 | 限定最大长度,内容去重 |
枚举 | 检查是否是定义好的值 |
文件 | 限定大小、类型(MIME) |
URL | 限定白名单前缀,不能是本地地址 |
重定向 | 使用服务端安全跳转逻辑,避免直接 redirect 用户输入地址 |
4.5. 🔐 安全推荐
- 使用 JSR-303(如
@Valid
、@NotNull
、@Size
等注解)+ 全局异常处理; - 所有
order by
、group by
字段都使用白名单校验; - 对上传的 URL / 文件内容做严格限制(防止 SSRF / 木马);
- 参数校验统一抽象在服务层或工具类,保持一致性;
- 对于外部调用(第三方接口)输入输出也要加参数验证。
4.6. 示例工具组合推荐:
框架/工具 | 用途 |
| Spring Boot 参数校验 |
| 字符串内容检查 |
| URL、邮箱、IP 等验证 |
| 限流防刷 |
spring-security-example/
├── controller/
│ └── UserController.java
├── dto/
│ └── UserRegisterDTO.java
├── validator/
│ └── EmailValidator.java
├── config/
│ └── RateLimiterConfig.java
├── rate/
│ └── RateLimitAspect.java
├── annotation/
│ └── RateLimited.java
4.6.1. 核心依赖(pom.xml
)
<dependencies><!-- 参数校验 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!-- commons-validator --><dependency><groupId>commons-validator</groupId><artifactId>commons-validator</artifactId><version>1.7</version></dependency><!-- Guava RateLimiter --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>32.0.1-jre</version></dependency><!-- AOP 支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
</dependencies>
4.6.2. ✅ DTO 参数验证示例(UserRegisterDTO.java)
@Data
public class UserRegisterDTO {@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "密码不能为空")@Size(min = 8, max = 20, message = "密码长度必须在8-20之间")private String password;@Email(message = "邮箱格式不正确")private String email;@Pattern(regexp = "^[0-9]{6}$", message = "验证码必须是6位数字")private String verifyCode;
}
4.6.3. ✅ Controller 示例(UserController.java)
@RestController
@RequestMapping("/api/user")
@Validated
public class UserController {@PostMapping("/register")@RateLimitedpublic String register(@Valid @RequestBody UserRegisterDTO dto) {return "注册成功";}
}
4.6.4. ✅ Guava 限流(RateLimiterConfig.java)
@Configuration
public class RateLimiterConfig {@Beanpublic RateLimiter rateLimiter() {// 每秒最多允许 5 个请求return RateLimiter.create(5.0);}
}
4.6.5. ✅ 限流注解 + AOP 拦截(RateLimited.java + RateLimitAspect.java)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimited {
}
@Aspect
@Component
public class RateLimitAspect {@Autowiredprivate RateLimiter rateLimiter;@Around("@annotation(com.example.annotation.RateLimited)")public Object limit(ProceedingJoinPoint pjp) throws Throwable {if (!rateLimiter.tryAcquire()) {throw new RuntimeException("请求过于频繁,请稍后再试");}return pjp.proceed();}
}
4.6.6. ✅ 使用 commons-validator 检查邮箱(可扩展)
public class EmailValidatorUtil {public static boolean isValidEmail(String email) {return org.apache.commons.validator.routines.EmailValidator.getInstance().isValid(email);}
}
4.6.7. 🧪 示例异常返回格式(统一处理)
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<String> handleValidException(MethodArgumentNotValidException e) {return ResponseEntity.badRequest().body(e.getBindingResult().getFieldError().getDefaultMessage());}@ExceptionHandler(Exception.class)public ResponseEntity<String> handle(Exception e) {return ResponseEntity.status(500).body("服务器错误: " + e.getMessage());}
}
5. 【强制】禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。
说明: XSS 跨站脚本攻击。它指的是恶意攻击者往 Web 页面里插入恶意 html 代码,当用户浏览时,嵌入其中 Web 里面的 html 代码会被执行,造成获取用户 cookie、钓鱼、获取用户页面数据、蠕虫、挂马等危害。
禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据,目的是防止 XSS(Cross Site Scripting)跨站脚本攻击。
XSS 攻击原理:攻击者通过在输入中插入恶意脚本(如 <script>
标签、onerror
属性等),使其在其他用户浏览网页时被浏览器执行,进而执行恶意操作,例如:
- 窃取用户 cookie;
- 执行钓鱼攻击;
- 操作 DOM 页面;
- 利用浏览器漏洞传播蠕虫病毒。
5.1. 🚨 危险示例(XSS 攻击演示):
场景:在页面显示用户昵称
后端代码:
// 用户昵称从请求中读取,未做处理直接输出
String nickname = request.getParameter("nickname");
out.println("<div>欢迎你," + nickname + "</div>");
攻击请求:
http://example.com/page?nickname=<script>alert('XSS')</script>
最终页面渲染结果:
<div>欢迎你,<script>alert('XSS')</script></div>
✅ 结果:用户打开页面时,脚本被执行,弹出 alert。
5.2. ✅ 安全做法(防止 XSS):
对输出进行 HTML 转义
将特殊字符进行转义,如:
<
→<
>
→>
"
→"
'
→'
&
→&
后端 Java 处理:
String nickname = StringEscapeUtils.escapeHtml4(request.getParameter("nickname"));
out.println("<div>欢迎你," + nickname + "</div>");
使用 Apache Commons Text 的:
import org.apache.commons.text.StringEscapeUtils;
结果页面:
<div>欢迎你,<script>alert('XSS')</script></div>
浏览器只会显示这段文本,不会执行脚本。
使用模板引擎自动转义
如 Thymeleaf、FreeMarker 等模板引擎通常默认输出会自动转义。
Thymeleaf 示例:
<p th:text="${nickname}">昵称</p> <!-- 自动 HTML 转义 -->
设置 CSP(Content Security Policy)
通过 HTTP 响应头限制页面执行外部或内联脚本,如:
Content-Security-Policy: script-src 'self'
6. 【强制】表单、AJAX 提交必须执行 CSRF 安全验证。
说明: CSRF (Cross-site request forgery) 跨站请求伪造是一类常见编程漏洞。 对于存在 CSRF 漏洞的应用/网站,攻击者可以事先构造好 URL,只要受害者用户一访问,后台便在用户不知情的情况下对数据库中用户参数进行相应修改。
7. 【强制】URL 外部重定向传入的目标地址必须执行白名单过滤。
说明: 攻击者通过恶意构造跳转的链接,可以向受害者发起钓鱼攻击。
8. 【强制】在使用平台资源, 譬如短信、 邮件、 电话、 下单、 支付, 必须实现正确的防重放的机制,如数量限制、疲劳度控制、验证码校验,避免被滥刷而导致资损。
说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其它用户,并造成短信平台资源浪费。
8.1. 规则内容:
使用平台资源(短信、邮件、电话、下单、支付等)必须实现防重放机制,避免被滥刷。
8.2. ⚠️ 背后风险:
风险场景 | 风险后果 |
注册接口无限制发短信 | 骚扰他人,短信平台费用急剧上升 |
邮件接口无限制发验证码 | 被滥用为发垃圾邮件平台,系统被封域名/IP |
支付或下单接口可重复提交 | 重复扣款、资损、库存异常 |
滑动验证、验证码绕过 | 恶意注册、批量盗号、接口刷爆 |
8.3. ❌ 错误做法(无限制发送短信验证码)
@PostMapping("/sendSms")
public ResponseEntity<String> sendSms(@RequestParam String phone) {smsService.sendVerificationCode(phone);return ResponseEntity.ok("验证码已发送");
}
- ❌ 没有限制调用频率;
- ❌ 没有疲劳度判断(如1分钟内连续请求);
- ❌ 没有验证码/Token机制防重放;
- ✅ 攻击者可短时间请求几千次,造成严重资损。
8.4. ✅ 正确示例(多重防护)
@PostMapping("/sendSms")
public ResponseEntity<String> sendSms(@RequestParam String phone, HttpServletRequest request) {
String ip = request.getRemoteAddr();// 1. 限频防刷(基于IP+手机号)
if (!rateLimiter.tryAcquire(ip + ":" + phone)) {return ResponseEntity.status(429).body("发送频繁,请稍后再试");
}// 2. 疲劳度控制(每小时最多发送3次)
if (smsService.countToday(phone) >= 3) {return ResponseEntity.badRequest().body("发送次数过多");
}// 3. 验证码校验机制(如图形验证码、行为验证)
if (!captchaService.verify(request)) {return ResponseEntity.badRequest().body("图形验证码错误");
}// 4. 加入 Token 防重放(可选)
String requestToken = request.getHeader("X-Request-Token");
if (tokenService.isReplay(requestToken)) {return ResponseEntity.status(403).body("请求重复,请刷新页面重试");
}// 执行发送
smsService.sendVerificationCode(phone);
return ResponseEntity.ok("验证码发送成功");
}
8.5. ✅ 建议防重放措施汇总
类型 | 说明 |
✅ 接口限频 | Guava RateLimiter / Redis + Lua 限流脚本 |
✅ 疲劳度控制 | 每个手机号/IP 每小时最多几次,避免持续骚扰 |
✅ 图形验证码 | 有效防止机器自动请求 |
✅ 滑动验证码 | 腾讯云、极验等行为验证 |
✅ Token 防重放 | 前端生成一次性 token,请求后立即失效 |
✅ 请求签名校验 | 常用于支付等敏感接口,防止参数被篡改 |
9. 【强制】对于文件上传功能,需要对于文件大小、类型进行严格检查和控制。
说明:攻击者可以利用上传漏洞,上传恶意文件到服务器,并且远程执行,达到控制网站服务器的目的。这条规则是Web 安全中的核心防护要求之一,特别是应对文件上传攻击(File Upload Attack)。
9.1. 规则内容:
文件上传功能必须对文件大小、类型进行严格检查和控制。
9.2. ⚠️ 背后风险:
攻击者可能上传以下恶意文件:
文件类型 | 危害 |
| 一旦被服务器执行,可远程控制服务器(WebShell) |
| 本地下载后诱导用户执行 |
| Zip 炸弹攻击,占用系统资源 |
超大文件 | 消耗服务器带宽和磁盘资源,造成 DoS 攻击 |
文件伪装 |
|
9.3. ❌ 危险代码(未做检查)
@PostMapping("/upload")
public String uploadFile(@RequestParam MultipartFile file) throws IOException {
String fileName = file.getOriginalFilename();
File targetFile = new File("/upload/" + fileName);
file.transferTo(targetFile);
return "上传成功";
}
- ❌ 没有限制文件大小;
- ❌ 没有限制上传类型;
- ❌ 直接保存在 Web 可访问目录;
- ✅ 攻击者可上传
.jsp
文件并远程执行命令。
9.4. ✅ 安全示例(类型 + 大小校验)
@PostMapping("/upload")
public ResponseEntity<String> upload(@RequestParam MultipartFile file) throws IOException {// 1. 文件大小限制(最大5MB)if (file.getSize() > 5 * 1024 * 1024) {return ResponseEntity.badRequest().body("文件过大,限制为5MB以内");}// 2. 文件类型限制(只允许图片)String contentType = file.getContentType();List<String> allowedTypes = List.of("image/jpeg", "image/png", "image/gif");if (!allowedTypes.contains(contentType)) {return ResponseEntity.badRequest().body("仅支持上传 JPG/PNG/GIF 文件");}// 3. 校验文件后缀名(防伪装)String originalName = file.getOriginalFilename();if (originalName == null || !originalName.matches(".*\\.(jpg|jpeg|png|gif)$")) {return ResponseEntity.badRequest().body("文件扩展名非法");}// 4. 重命名 + 存储到非 web 目录String newFileName = UUID.randomUUID() + originalName.substring(originalName.lastIndexOf("."));File target = new File("/data/uploads/" + newFileName);file.transferTo(target);return ResponseEntity.ok("上传成功");
}
9.5. ✅ 安全增强建议
项目 | 建议 |
文件大小限制 | 上传接口应限制最大文件大小(如 5MB) |
文件类型校验 | 使用 MIME 类型 + 文件扩展名双重校验 |
文件内容校验(可选) | 对上传文件内容头进行 magic number 检查,防止伪装 |
上传路径隔离 | 不应保存在 Web 可访问路径下,如 |
文件重命名 | 使用 UUID、雪花ID 生成唯一文件名,避免路径穿越攻击 |
文件扫描 | 可选接入第三方病毒扫描(如 clamAV) |
白名单机制 | 强烈推荐:只允许固定类型上传(白名单),拒绝其他 |
10. 【强制】配置文件中的密码需要加密。
10.1. 🚫 规则内容:
配置文件中的密码需要加密,不能明文写入,例如数据库、Redis、MQ、第三方服务的认证信息等。
10.2. ⚠️ 背后原因:
- 安全风险大:如果配置文件泄露,明文密码会直接暴露;
- 开发测试人员变动频繁,明文密码可能被滥用或传播;
- 合规要求:很多行业(如金融、政务)要求密码不能明文存储。
10.3. ❌ 错误示例(明文密码)
application.yml
spring:datasource:url: jdbc:mysql://localhost:3306/demousername: rootpassword: 123456 # ❌ 明文密码,存在泄露风险
10.4. ✅ 正确做法示例(加密 + 解密)
10.4.1. ✅ 使用加密工具对密码加密,启动时解密
10.4.1.1. 第一步:加密密码(使用 Jasypt)
命令加密(以 jasypt
为例):
java -cp jasypt.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI \input="123456" password="my-secret-key" algorithm=PBEWithMD5AndDES
输出加密结果如:
ENC(Rz5+1cREkP39EMuz0RVQPw==)
10.4.1.2. 第二步:配置文件中使用加密串
spring:datasource:url: jdbc:mysql://localhost:3306/demousername: rootpassword: ENC(Rz5+1cREkP39EMuz0RVQPw==)
10.4.1.3. 第三步:解密依赖
引入依赖:
<dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>3.0.4</version>
</dependency>
启动参数(传入密钥):
-Djasypt.encryptor.password=my-secret-key
10.4.2. ✅ 自己实现解密逻辑
custom:datasource:password: ENC(XXYYZZ)
配置类中读取时手动解密:
@Configuration
public class DataSourceConfig {@Value("${custom.datasource.password}")private String encryptedPassword;@Beanpublic DataSource dataSource() {String decrypted = CustomEncryptor.decrypt(encryptedPassword);// 使用解密后的密码配置连接池}
}
10.5. 🛡️ 总结
项目 | 推荐做法 |
密码 | 不可明文写入配置文件 |
加密方式 | 使用 Jasypt、SM4、AES 等对称加密算法 |
解密方式 | 启动时传密钥,或代码中解密 |
敏感配置项 | DB 密码、Redis 密码、MQ 密码、Token、API Key |
11. 【推荐】发贴、评论、发送等即时消息,需要用户输入内容的场景。必须实现防刷、内容违禁词过滤等风控策略。
在 发帖、评论、消息发送等允许用户输入内容的场景中,必须实现防刷和敏感词过滤机制。
11.1. 📌 背后原因:
- 防刷(反作弊)
-
- 防止用户或恶意程序在短时间内大量发送内容,造成系统资源浪费、刷屏或攻击。
- 常见手段:验证码、限频(RateLimiter)、IP 黑名单、用户行为评分等。
- 内容违禁词过滤
-
- 避免用户发布非法、色情、暴力、政治敏感、辱骂、诈骗等违规内容。
- 实现方式:关键词匹配(词库)、模型检测(NLP)、人工审核等。
11.1.1. ❌ 不合规实现(没有风控)
用户评论功能直接保存用户输入内容,没有任何风控措施:
@PostMapping("/comment")
public String postComment(@RequestParam String content) {commentService.save(content); // 无任何过滤或限制return "评论成功";
}
11.2. ✅ 推荐实现(带防刷 + 敏感词过滤)
步骤一:限频防刷(基于用户/IP)
// 使用 Google Guava 的 RateLimiter 限流
private final RateLimiter rateLimiter = RateLimiter.create(1.0); // 每秒1次@PostMapping("/comment")
public ResponseEntity<String> postComment(@RequestParam String content, HttpServletRequest request) {
String ip = request.getRemoteAddr();
if (!rateLimiter.tryAcquire()) {return ResponseEntity.status(429).body("操作过于频繁,请稍后重试");
}// 敏感词过滤
if (SensitiveFilter.containsSensitiveWord(content)) {return ResponseEntity.badRequest().body("内容含有违规词汇");
}commentService.save(content);
return ResponseEntity.ok("评论成功");
}
步骤二:敏感词过滤实现(简单关键词示例)
public class SensitiveFilter {private static final Set<String> sensitiveWords = Set.of("诈骗", "暴力", "政治", "涉黄");public static boolean containsSensitiveWord(String text) {for (String word : sensitiveWords) {if (text.contains(word)) return true;}return false;}
}
11.3. 🛡️ 高级防护建议:
功能 | 建议做法 |
防刷 | 使用令牌桶限流、图形验证码、滑动验证 |
内容审核 | 敏感词库 + 自然语言模型过滤(如接入阿里云内容安全、腾讯内容审核) |
黑产识别 | 结合用户行为日志(异常频率、IP、设备号)进行识别 |
审核策略 | 提交内容先进入“待审核”状态,通过后再展示 |