SpringBoot 拦截器和自定义注解判断请求是否合法

应用场景举例:
当不同身份的用户请求一个接口时,用来校验用户某些身份,这样可以对单个字段数据进行精确权限控制,具体看代码注释

自定义注解

/**
 * 对比请求的用户身份是否符合
 * @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);
    }
}

效果

只有特定身份人员才可以删除操作

posted @ 2020-12-09 17:25  rm-rf*  阅读(916)  评论(0编辑  收藏  举报