当前位置: 首页 > news >正文

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<HandlerMethodArgumentResolver>里面存放的就是spring中所有的参数解析器了,我们只需要添加我们自己的就会在对应的容器生命周期过程中也处理我们自己的解析请求

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

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

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

相关文章:

  • opencv 车牌识别
  • [LeetCode周赛复盘] 第 312 场周赛20220925
  • 基于HTML+CSS+JavaScript的MIUI10官网网站设计与开发
  • Vue 新手期练手出现问题记录与解决方案——Vue练手项目“小问题“
  • 计算机组成原理-华科版本
  • 计算机网络原理 谢希仁(第8版)第五章习题答案
  • 记一次Netty堆外内存溢出OutOfDirectMemoryError
  • 设计模式详解:模式汇总与索引清单
  • SpringSecurity实战-第5章 自动登录和注销登录
  • Python基础内容训练9(文件操作)
  • 冰冰学习笔记:list的简单模拟
  • 基于鸽群优化算法的线性规划求解matlab程序
  • 【博客505】k8s Sig-scheduler Coscheduling调度器插件原理
  • 【Linux】I/O多路复用-SELECT/POLL/EPOLL
  • Python解释器路径寻找规则
  • JS 中的深拷贝与浅拷贝
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • crontab执行失败的多种原因
  • css属性的继承、初识值、计算值、当前值、应用值
  • ES6简单总结(搭配简单的讲解和小案例)
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • JavaScript HTML DOM
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • Leetcode 27 Remove Element
  • Puppeteer:浏览器控制器
  • Python 基础起步 (十) 什么叫函数?
  • RxJS: 简单入门
  • 产品三维模型在线预览
  • 对象引论
  • 基于遗传算法的优化问题求解
  • 警报:线上事故之CountDownLatch的威力
  • 区块链共识机制优缺点对比都是什么
  • 腾讯视频格式如何转换成mp4 将下载的qlv文件转换成mp4的方法
  • 听说你叫Java(二)–Servlet请求
  • 优秀架构师必须掌握的架构思维
  • 自动记录MySQL慢查询快照脚本
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • ​马来语翻译中文去哪比较好?
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #基础#使用Jupyter进行Notebook的转换 .ipynb文件导出为.md文件
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (论文阅读11/100)Fast R-CNN
  • (四)docker:为mysql和java jar运行环境创建同一网络,容器互联
  • (四)库存超卖案例实战——优化redis分布式锁
  • (转)c++ std::pair 与 std::make
  • . Flume面试题
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .Net 应用中使用dot trace进行性能诊断
  • .NET 中各种混淆(Obfuscation)的含义、原理、实际效果和不同级别的差异(使用 SmartAssembly)
  • .NET/C# 使窗口永不获得焦点
  • .NET程序员迈向卓越的必由之路
  • .NET基础篇——反射的奥妙