mid - 注册参数校验 & 登录

用户注册接口的参数合法性校验

注册参数校验 & 全局异常处理

由于用户输入未必是合法性字符, 所以我们需要加以约束和校验,
但是每次都手写验证代码太冗长且繁杂,
于是我们引入Spring Validation模块简化验证步骤
–> spring-boot-starter-validation 依赖

参数校验

常用注解

参数前加 @Pattern注解
Controller类前添加 @Validated

以下是常用属性

属性 含义
regexp regular expression,正则表达式规则
message 校验失败时返回的提示信息

Not类型

注解 作用 适用类型 特点
@NotNull 值不能为 null 任意对象类型 只校验是否为 null,不管内容是否为空
@NotEmpty 值不能为空 String、集合、数组、Map 不能为 null,长度或元素个数必须大于 0
@NotBlank 字符串不能为空白 String 不能为 null,且去掉首尾空格后长度必须大于 0
输入值 @NotNull @NotEmpty @NotBlank
null
""
" "
"abc"

长度数值

注解 作用 适用类型 常用属性 特点
@Size 限制长度或元素个数 String、集合、数组、Map minmax 通用性最强
@Min 最小值限制(≥) 数值类型 value 包含边界
@Max 最大值限制(≤) 数值类型 value 包含边界
@Range 数值范围限制 数值类型 minmax Hibernate 提供,等价 Min+Max

正则类

注解 作用 适用类型 常用属性 特点
@Pattern 用正则表达式校验格式 String regexpmessage 最通用,用户名密码手机号都能管
@Email 校验邮箱格式 String message 语义更直接,本质也是格式校验

valid & validated

@Valid

@Valid 是 Java Bean Validation 标准里的注解,
作用是:

👉 告诉 Spring:对这个对象执行校验

一般写在方法参数前:

1
public Result register(@RequestBody @Valid RegisterDTO dto)

意思是:

  • 接收到请求体
  • 封装成 RegisterDTO
  • 然后按 DTO 上的校验注解执行校验

@Validated

@Validated 是 Spring 提供的增强注解,
作用也是开启校验,但功能更强,支持分组校验

常见写法有两种:

写在类上

用于开启方法参数校验:

1
2
3
4
@Validated  
@RestController
public class UserController {
}
写在参数上

也可以直接写在对象参数前:

1
public Result register(@RequestBody @Validated RegisterDTO dto)

它们怎么搭配

这块你可以这样写:

场景 1:普通对象校验

一般直接这样:

1
public Result register(@RequestBody @Valid RegisterDTO dto)

场景 2:方法参数校验

比如直接校验普通参数:

1
2
3
4
5
6
7
8
9
10
11
@Validated
@RestController
public class UserController {

@PostMapping("/register")
public Result register(
@Pattern(regexp = "^\\S{5,16}$") String username,
@Pattern(regexp = "^\\S{5,16}$") String password) {
return Result.success();
}
}

这里通常要在类上加 @Validated,不然方法参数上的约束可能不生效。

全局异常处理

由于 @Validated 校验注解失败后不会走方法的逻辑语句,
所以我们必须添加全局异常处理器进行全局异常捕捉

原理

“参数校验失败时异常在 Controller 执行前被抛出,
随后由 Spring MVC 的全局异常处理机制
根据 @ExceptionHandler 的异常类型匹配规则分发到对应的处理方法”。

异常往上抛给 DispatcherServlet

异常抛出后,请求处理链中断,
Spring MVC 不会继续执行你的业务逻辑,而是进入:

👉 异常处理流程

Spring 会去找有没有合适的 异常解析器HandlerExceptionResolver 来处理这个异常。

@RestControllerAdvice 被注册成全局异常处理器, 这是一个 面向所有 @RestController 的全局异常处理增强类

ExceptionHandler(Exception.class) 表明: 如果系统里抛出了 Exception 及其子类异常,可以交给这个方法处理

1
2
3
4
5
6
7
8
9
@RestControlleAdvice //被注册为全局异常处理器
public class GlobalExceptionHandler{
@ExceptionHandler(Exception.class)//抛出此种异常交给此方法处理
public Result handlerException(Exception e){
e.printStackTrace();
return Result.error(
StringUtil.isNUllOrEmpty(e.getMessage()) ? "系统繁忙, 请稍后重试" : e.getMessage()));
}
}

登录认证

1
2
3
4
5
登录成功一次

后续请求不能反复查账号密码

需要一个可验证的身份凭证

令牌

常见做法是服务端生成一个令牌 Token 返回给前端,前端在后续请求中携带该令牌。 服务端只需要校验令牌是否合法,就能确认当前用户身份。

JWT

  • JWT = JSON Web Token
  • 为什么叫 JSON:因为载荷部分通常存的是 JSON 数据
  • 为什么叫 Token:因为它本质是令牌,代表一次身份凭证

组成

  • Header(头) : 记录令牌类型、签名算法等。
    例如:{“alg”:”HS256”, “type”:”JWT”}.

  • Payload(有效载荷) : 携带一些自定义信息、默认信息等。
    例如:{“id”:”1”,”username”:”Tom”}.

  • Signature(签名) : 防止Token被篡改、确保安全性。
    将header、payload,并加入指定秘钥,通过指定签名算法计算.

Pasted image 20260402185916

Base64:是一种基于64个可打印字符 A-Z a-z 0-9 + / 来表示二进制数据的编码方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static final String KEY = "QueSera";

//接收业务数据, 生成token并返回
public static String getToken(Map<String,Object> claims){
return JWT.create()
.withClaim("claims",claims)//往JWT的payload里放自定义声明字段
.withExpiresAt( //12个小时
new Date(System.currentTimeMills()+1000*60*60*12))
.sign(Algorithm.HMAC256(KEY));
}

//解析token, 验证token并返回业务数据
public static Map<String,Object> parseToken(String token){
return JWT.require(Algorithm.HMAC256(KEY))
.build()
.verify(tokeb)
.getClaim("claims")
.asMap();
}