1.引入Spring Validation起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.在参数面前添加@Pattern
注解
@Pattern(regexp = "^\\S{5,16}$")
3.在Controller类上添加 @Vallidated
注解
@RestControllerAdvice
public class GlobalExceptionHandle {
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
e.printStackTrace();
return Result.error(StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "操作失败");
}
}
其中,Resul类是自定义的返回结果类
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {
private Integer code;//业务状态码 0-成功 1-失败
private String message;//提示信息
private T data;//响应数据
//快速返回操作成功响应结果(带响应数据)
public static <E> Result<E> success(E data) {
return new Result<>(0, "操作成功", data);
}
//快速返回操作成功响应结果
public static Result success() {
return new Result(0, "操作成功", null);
}
public static Result error(String message) {
return new Result(1, message, null);
}
}
以User
对象为例
Validation
对应注解@NotNull
值不能为空
@NotEmpty
不能为空且字符串不能为空字符串""
@Email
满足邮箱格式
@URL
对参数是否是url路径进行校验
@Data
public class User {
@NotNull
private Integer id;//主键ID
private String username;//用户名
@JsonIgnore //让springmvc把当前对象转换成json字符串的时候,忽略password,最终的json字符串中就没有password这个属性了
private String password;//密码
@NotEmpty
@Pattern(regexp = "^\\S{1,10}$")
private String nickname;//昵称
@NotEmpty
@Email
private String email;//邮箱
private String userPic;//用户头像地址
private LocalDateTime createTime;//创建时间
private LocalDateTime updateTime;//更新时间
}
@Validated
@PutMapping("/update")
public Result update(@RequestBody @Validated User user) {
userService.update(user);
return Result.success();
}
将校验项归类分组,校验指定组中的校验项。
在实体类内部定义接口,通过groups属性进行指定,给 @validated
注解的value属性赋值,默认为Default
@Data
public class Category {
@NotNull(groups = Update.class)
private Integer id;//主键ID
@NotEmpty
private String categoryName;//分类名称
@NotEmpty
private String categoryAlias;//分类别名
private Integer createUser;//创建人ID
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;//创建时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;//更新时间
//如果说某个校验项没有指定分组,默认属于Default分组
//分组之间可以继承, A extends B 那么A中拥有B中所有的校验项
public interface Add extends Default {
}
public interface Update extends Default{
}
}
这种情况下,id
的校验是在更新的时候去判断传入的参数是否为空。
@PostMapping
public Result add(@RequestBody @Validated(Category.Add.class) Category category) {
categoryService.add(category);
return Result.success();
}
Controller在参数中指明当前实体类校验的类型
已有的注解不能满足所有的校验需求,特殊的情况需要自定义校验(自定义校验注解)
1.自定义注解State
@Documented
// 指定校验规则由谁提供
@Constraint(
validatedBy = {StateValidation.class}
)
// 元注解 标明该注解的使用范围
/**
* ElementType.METHOD 方法体,
* ElementType.FIELD 属性,
* ElementType.ANNOTATION_TYPE 注释类,
* ElementType.CONSTRUCTOR 构造,
* ElementType.PARAMETER 参数,
* ElementType.TYPE_USE
*/
@Target({ElementType.FIELD})
// 注解保留阶段
@Retention(RetentionPolicy.RUNTIME)
public @interface State {
// 提供校验失败后的信息
String message() default "state 参数的值只能是已发布或草稿";
// 指定分组
Class<?>[] groups() default {};
// 负载 获取到 State 注解的附加信息
Class<? extends Payload>[] payload() default {};
}
2.自定义校验数据的类 StateValidation
实现 ConstraintValidator
接口
public class StateValidation implements ConstraintValidator<State, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (value == null) return false;
if (value.equals("已发布") || value.equals("草稿")) {
return true;
}
return false;
}
}
3.在需要校验的地方使用自定义注解
全称:JSON Web Token (https:/jwt.io/
)
组成:
Header(头),记录令牌类型和签名算法等
PayLoad(载荷),携带自定义的信息。注意不要承载私密数据。
Signature(签名),对头部和载荷进行加密计算得来
1.添加依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
2.调用API生成和校验令牌
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
private static final String KEY = "itheima"; // 令牌加密密钥字符串
//接收业务数据,生成token并返回
public static String genToken(Map<String, Object> claims) {
return JWT.create()
.withClaim("claims", claims) // 设置令牌承载数据
.withExpiresAt(new Date(System.currentTimeMillis() + 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(token)
.getClaim("claims")
.asMap();
}
}
3.解析令牌抛出异常,则令牌被篡改或过期
Token在请求头中的Authorization
字段,从请求头中获取。若未登录,则不存在该字段。
@GetMapping("/list")
public Result<String> list(@RequestHeader(name = "Authorization") String token, HttpServletResponse response) {
// 验证Token
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
return Result.success("所有文章数据");
} catch (Exception e) {
// 设置响应状态码 401
response.setStatus(401);
return Result.error("未登录");
}
}
首先设置登录拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 令牌验证
String token = request.getHeader("Authorization");
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
// 放行
return true;
} catch (Exception e) {
// 设置响应状态码 401
response.setStatus(401);
// 不放行
return false;
}
}
}
随后配置注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 登录和注册接口不拦截
registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login", "/user/register");
}
}
用来存取数据:set(0/get()
使用ThreadLocal存储的数据,线程安全
用完记得调用remove方法释放