• Spring中自定义依赖注入对象注入Controller中,优雅的解决用户鉴权问题(HandlerInterceptorAdapter)


    Spring中自定义依赖注入对象注入Controller中,解决用户鉴权问题

    通常我们在写Controller的时候对需要对调用接口的用户进行权限校验,并获取这个用户的信息,一般在用户登录之后的请求中都会携带一个token,我们会在Controller方法被调用前的拦截器中对用户进行鉴权,我们只需要实现HandlerInterceptorAdapter接口即可,并通过查缓存获取用户的信息,之后存到当前线程的对象中,通过LocalThread就可以拿到这个用户的信息,或者自己封装一个工具类,类似于:

    /**
     * 获取自己信息
     */
    @GetMapping({"/userInfo","/consumerInfomation"})
    public ResponseEntity<ChaUser> userInfo() {
        return ResponseEntity.ok(LocalThreadUtils.getUserInfo());
    }
    

    这样的好处是把统一的鉴权逻辑在拦截器中处理完毕了,业务层只需要关心业务即可

    但是这种方式不是很优雅,并且在实际开发中有的同学会在Service层或dao层也乱用LocalThreadUtils.getUserInfo()的这个方法,这就导致如果在controller或者Service层开启了一个异步操作,或者是通过RPC的方式调用其他模块的接口,就会导致拿不到这个用户的信息,所以我们希望能过通过依赖注入的方式将当前调用这个接口的对象注入到controller中,类似于:

    /**
     * 获取自己信息
     */
    @GetMapping({"/userInfo", "/consumerInfomation"})
    public ResponseEntity<ChaUser> userInfo(@User ChaUser user) {
        return ResponseEntity.ok(user);
    }
    

    那这个怎么实现呢?其实也很简单,因为我们能想到的其实Spring都已经给我们提供好了,有一个接口HandlerMethodArgumentResolver,我们通常把它称为:参数解析器,我们只需要实现这个接口,就能拥有一个自己的依赖注入能力,再将我们自己的解析器交给Spring容器即可

    我们先来看一下这个接口:

    public interface HandlerMethodArgumentResolver {
        // 判断Controller层中的参数,是否满足条件,满足条件则执行resolveArgument方法,不满足则跳过
        boolean supportsParameter(MethodParameter var1);
        // 用户自定义业务逻辑,并返回自己需要注入的实例,其中webRequest就是本次请求的servlet对象
        Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
    }
    

    详细参数可以在源码的注释中看的非常清楚,所以现在我们只需要实现一个自己的UserHandlerMethodArgumentResolver,当然Spring默认提供了26种参数解析器,比如我们最常用的参数注解 @RequestParam 就是由 RequestParamMethodArgumentResolver 解析的,PathVariableMethodArgumentResolver 用来解析 @PathVariable 注解

    我们先定义一个实体类ChaUser

    @Data
    @Accessors(chain = true)
    public class ChaUser {
        private String userName;
        private Integer age;
        private String emailNumber;
    }
    

    定义一个注解@User,用来标记需要解析的bean:

    /**
     * 用来标记当前对象应该从用户上下文中拿到,并注入到controller的方法参数中
     *
     * @author Eureka
     * @since  2022年9月22日19:43:07
     * @see ChaUser
     * @see HandlerMethodArgumentResolver
     * @see UserHandlerMethodArgumentResolver
     */
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface User {
    }
    

    再定义我们自己的参数解析器UserHandlerMethodArgumentResolver

    @Component
    public class UserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            // 当参数上有@comment时才使用该解析器解析
            return parameter.hasParameterAnnotation(User.class);
        }
    
        @Override
        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
            // 在这里注入当前用户的信息,webRequest中就可以取到用户的请求信息
            return new ChaUser().setUserName("张三").setAge(18).setEmailNumber("xxx@xxx.com");
        }
    }
    

    但是这样还不行,我们还需要将这个参数解析器加入到spring中,我们可以实现一个接口WebMvcConfigurer#addArgumentResolvers

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Autowired
        private UserHandlerMethodArgumentResolver userHandlerMethodArgumentResolver;
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            // 添加自己的拦截器
            resolvers.add(userHandlerMethodArgumentResolver);
        }
    }
    

    可以看到List里面存放的就是spring中所有的参数解析器了,我们只需要添加我们自己的就会在对应的容器生命周期过程中也处理我们自己的解析请求

    我们写一个测试类测试一下

    /**
     * 获取自己信息
     */
    @GetMapping({"/userInfo", "/consumerInfomation"})
    public ResponseEntity<ChaUser> userInfo(@User ChaUser user) {
        return ResponseEntity.ok(user);
    }
    

    image-20220925160647403可以说是非常的方便,这也是为什么Spring这么流行的原因,它留下了非常多的拓展点,只要我们能想到的,一般都会有对应的接口可以满足我们的需求

  • 相关阅读:
    【面试题】面试小技巧:如果有人问你 xxx 技术是什么?
    C语言第五章第3节用do...while语句实现循环学习导案
    ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
    Java Integer.numberOfLeadingZeros()
    冒泡排序和选择排序的小乐趣
    有向图的强连通分量
    Outlook屏蔽Jira AI提醒
    blender动画制作软件拓扑全流程
    实验室预约管理系统(Java+SSH+Web+MySQL+ofbiz系统)
    华为设备配置大型网络WLAN基本业务
  • 原文地址:https://blog.csdn.net/fengxiandada/article/details/127039354