参数校验
方法步骤
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 {@NotNullprivate Integer id;//主键IDprivate String username;//用户名@JsonIgnore //让springmvc把当前对象转换成json字符串的时候,忽略password,最终的json字符串中就没有password这个属性了private String password;//密码@NotEmpty@Pattern(regexp = "^\\S{1,10}$")private String nickname;//昵称@NotEmpty@Emailprivate 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@NotEmptyprivate String categoryName;//分类名称@NotEmptyprivate 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
的校验是在更新的时候去判断传入的参数是否为空。
@PostMappingpublic 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> {@Overridepublic boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {if (value == null) return false;if (value.equals("已发布") || value.equals("草稿")) {return true;}return false;}
}
3.在需要校验的地方使用自定义注解
登录认证
令牌规范JWT
全称: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) {// 验证Tokentry {Map<String, Object> claims = JwtUtil.parseToken(token);return Result.success("所有文章数据");} catch (Exception e) {// 设置响应状态码 401response.setStatus(401);return Result.error("未登录");}}
设置拦截器进行登录验证
首先设置登录拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic 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) {// 设置响应状态码 401response.setStatus(401);// 不放行return false;}}
}
随后配置注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登录和注册接口不拦截registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login", "/user/register");}
}
ThreadLocal
用来存取数据:set()/get()
使用ThreadLocal存储的数据,线程安全
用完记得调用remove方法释放