應用場景舉例:
當不同身份的用戶請求一個接口時,用來校驗用戶某些身份,這樣可以對單個字段數據進行精確權限控制,具體看代碼注釋
自定義注解
/**
* 對比請求的用戶身份是否符合
* @author liuyalong
* @date 2020/9/25 16:03
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CompareUser {
/**
* The name of the request parameter to bind .
*/
@AliasFor("name") String value() default "";
@AliasFor("value") String name() default "";
}
給controller的字段添加注解
@ApiOperation(value = "刪除用戶", notes = "根據手機號來刪除用戶")
@PostMapping(value = "/delete_phone")
public BaseCommonResult<Integer> deletePhone(@CompareUser(value = "phone") String phone) {
int i = userService.deleteByPhone(phone);
return BaseCommonResult.success(i);
}
參數解析器
記得繼承后加@Component,這里是Base...所以不用
/**
* @author liuyalong
* @date 2020/9/25 15:56
*/
public class BaseCurrentUserInterceptor implements HandlerMethodArgumentResolver {
/**
* 用於判定是否需要處理該參數注解,返回true為需要,
* 並會去調用下面的方法resolveArgument。
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
//只處理CurrentUser注解修飾的參數
return parameter.hasParameterAnnotation(CompareUser.class);
}
/**
* 對比用戶信息
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
CompareUser parameterAnnotation = parameter.getParameterAnnotation(CompareUser.class);
Class<?> parameterType = parameter.getParameterType();
if (parameterAnnotation == null) {
throw new IllegalArgumentException("Unknown parameter type [" + parameterType.getName() + "]");
}
/*
* 獲取要驗證的字段名
*/
//檢查是否給字段取了別名
String paramName = "".equalsIgnoreCase(parameterAnnotation.name()) ? parameterAnnotation.value() : parameterAnnotation.name();
if ("".equalsIgnoreCase(parameterAnnotation.name())) {
//從參數中獲取定義的字段名
paramName = parameter.getParameter().getName();
}
//獲取請求字段的值
String paramValue = String.valueOf(webRequest.getParameter(paramName));
//從請求頭中獲取已經登錄的用戶
String userName = webRequest.getHeader(AuthConstant.USER_TOKEN_HEADER);
//對於root用戶,可以操作一切,所以直接返回
if (!AuthConstant.ROOT_USER.equals(userName)) {
//判斷身份是否一致,不一致就拋出異常,讓RestControllerAdvice處理
if (userName == null || !userName.equals(paramValue)) {
throw new NotSameAuthorException();
}
}
//將參數原封不動返回出去,需要還原回需要的類型
WebDataBinder binder = binderFactory.createBinder(webRequest, parameterType, paramName);
return binder.convertIfNecessary(paramValue, parameterType, parameter);
}
}
配置WebMvcConfigurer
注意這里提供了兩種方式加載,因為
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private HandlerInterceptor handlerInterceptor;
@Autowired
private HandlerMethodArgumentResolver currentUserInterceptor;
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(handlerInterceptor).addPathPatterns("/**");
}
//參數解析器,自定義的優先級最低,所以會失效,
// 解決方案是下面的 @PostConstruct ,把優先級調最高
// 但是這樣@PathParam @RequestParam就失效了,@CompareUser(value="xxx")可以完全替換@RequestParam功能
// @Override
// public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
// resolvers.add(currentUserInterceptor);
//
// }
/**
*參數解析器優先級調最高
*/
@PostConstruct
public void init() {
// 獲取當前 RequestMappingHandlerAdapter 所有的 Resolver 對象
List<HandlerMethodArgumentResolver> resolvers = requestMappingHandlerAdapter.getArgumentResolvers();
List<HandlerMethodArgumentResolver> newResolvers = new ArrayList<>(resolvers.size() + 1);
// 添加自定義參數解析器到集合首位
newResolvers.add(currentUserInterceptor);
// 添加 已注冊的 Resolver 對象集合
newResolvers.addAll(resolvers);
// 重新設置 Resolver 對象集合
requestMappingHandlerAdapter.setArgumentResolvers(newResolvers);
}
}
效果
只有特定身份人員才可以刪除操作