Spring Boot实现通用 Auth 认证的 4 种方式

news/2024/4/26 3:54:14/文章来源:https://blog.csdn.net/BASK2312/article/details/128117909

最近一直被无尽的业务需求淹没,没时间喘息,终于接到一个能让我突破代码舒适区的活儿,解决它的过程非常曲折,一度让我怀疑人生,不过收获也很大,代码方面不明显,但感觉自己抹掉了 java、Tomcat、Spring 一直挡在我眼前的一层纱。对它们的理解上了一个新的层次。

一、前言


好久没输出了,于是挑一个方面总结一下,希望在梳理过程中再了解一些其他的东西。由于 Java 繁荣的生态,下面每一个模块都有大量的文章专门讲述。所以我选了另外一个角度,从实际问题出发,将这些分散的知识串联起来,各位可以作为一个综述来看。各个模块的极致详细介绍,大家可以去翻官方文档或看网络上的其他博客。

需求很简单清晰,跟产品们提的妖艳需求一点也不一样:在我们的 web 框架里添加一个通用的 appkey 白名单校验功能,希望它的扩展性更好一些。

这个 web 框架是部门前驱者基于 spring-boot 实现的,介于业务和 Spring 框架之间,做一些偏向于业务的通用性功能,如 日志输出、功能开关、通用参数解析等。平常是对业务透明的,最近一直忙于把需求做好,代码写好,甚至从没注意过它的存在。

二、传统AOP


对于这种需求,首先想到的当然是 Spring-boot 提供的 AOP 接口,只需要在 Controller 方法前添加切点,然后再对切点进行处理即可。

▐ 实现

其使用步骤如下:

  • 使用 @Aspect 声明一下切面类 WhitelistAspect;
  • 在切面类内添加一个切点 whitelistPointcut(),为了实现此切点灵活可装配的能力,这里不使用 execution 全部拦截,而是添加一个注解 @Whitelist,被注解的方法才会校验白名单。
  • 在切面类中使用 spring 的 AOP 注解 @Before 声明一个通知方法 checkWhitelist() 在 Controller 方法被执行之前校验白名单。

切面类伪代码如下:

@Aspect
public class WhitelistAspect {@Before(value = "whitelistPointcut() && @annotation(whitelist)")
public void checkAppkeyWhitelist(JoinPoint joinPoint, Whitelist whitelist) {
checkWhitelist();
// 可使用 joinPoint.getArgs() 获取Controller方法的参数
// 可以使用 whitelist 变量获取注解参数
}@Pointcut("@annotation(com.zhenbianshu.Whitelist)")
public void whitelistPointCut() {
}
}
复制代码

在Controller方法上添加 @Whitelist 注解实现功能。

▐ 拓展

本例中使用了 注解 来声明切点,并且我实现了通过注解参数来声明要校验的白名单,如果之后还需要添加其他白名单的话,如通过 UID 来校验,则可以为此注解添加 uid() 等方法,实现自定义校验。

此外,spring 的 AOP 还支持 execution(执行方法) 、bean(匹配特定名称的 Bean 对象的执行方法)等切点声明方法和 @Around(在目标函数执行中执行) 、@After(方法执行后) 等通知方法。

如此,功能已经实现了,但领导并不满意=_=,原因是项目中 AOP 用得太多了,都用滥了,建议我换一种方式。嗯,只好搞起。

三、Interceptor


Spring 的 拦截器(Interceptor) 实现这个功能也非常合适。顾名思义,拦截器用于在 Controller 内 Action 被执行前通过一些参数判断是否要执行此方法,要实现一个拦截器,可以实现 Spring 的 HandlerInterceptor 接口。

▐ 实现

实现步骤如下:

  1. 定义拦截器类 AppkeyInterceptor 类并实现 HandlerInterceptor 接口。
  2. 实现其 preHandle() 方法;
  3. 在 preHandle 方法内通过注解和参数判断是否需要拦截请求,拦截请求时接口返回 false;
  4. 在自定义的 WebMvcConfigurerAdapter 类内注册此拦截器;

AppkeyInterceptor 类如下:

@Component
public class WhitelistInterceptor implements HandlerInterceptor {@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Whitelist whitelist = ((HandlerMethod) handler).getMethodAnnotation(Whitelist.class);
// whitelist.values(); 通过 request 获取请求参数,通过 whitelist 变量获取注解参数
return true;
}@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 方法在Controller方法执行结束后执行
}@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在view视图渲染完成后执行
}
}
复制代码

▐ 拓展

要启用 拦截器还要显式配置它启用,这里我们使用 WebMvcConfigurerAdapter 对它进行配置。需要注意,继承它的的 MvcConfiguration 需要在 ComponentScan 路径下。

@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WhitelistInterceptor()).addPathPatterns("/*").order(1);
// 这里可以配置拦截器启用的 path 的顺序,在有多个拦截器存在时,任一拦截器返回 false 都会使后续的请求方法不再执行
}
}
复制代码

还需要注意,拦截器执行成功后响应码为 200,但响应数据为空。Spring Boot 基础教程和示例源码在这里学习:github.com/javastacks/…

当使用拦截器实现功能后,领导终于祭出大招了:我们已经有一个 Auth 参数了,appkey 可以从 Auth 参数里取到,可以把在不在白名单作为 Auth 的一种方式,为什么不在 Auth 时校验?emmm… 吐血中。

四、ArgumentResolver


参数解析器是 Spring 提供的用于解析自定义参数的工具,我们常用的 @RequestParam 注解就有它的影子,使用它,我们可以将参数在进入Controller Action之前就组合成我们想要的样子。

Spring 会维护一个 ResolverList, 在请求到达时,Spring 发现有自定义类型参数(非基本类型), 会依次尝试这些 Resolver,直到有一个 Resolver 能解析需要的参数。要实现一个参数解析器,需要实现 HandlerMethodArgumentResolver 接口。

▐ 实现

  1. 定义自定义参数类型 AuthParam,类内有 appkey 相关字段;
  2. 定义 AuthParamResolver 并实现 HandlerMethodArgumentResolver 接口;
  3. 实现 supportsParameter() 接口方法将 AuthParam 与 AuthParamResolver 适配起来;
  4. 实现 resolveArgument() 接口方法解析 reqest 对象生成 AuthParam 对象,并在此校验 AuthParam ,确认 appkey 是否在白名单内;
  5. 在 Controller Action 方法上签名内添加 AuthParam 参数以启用此 Resolver;

实现的 AuthParamResolver 类如下

@Component
public class AuthParamResolver implements HandlerMethodArgumentResolver {@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(AuthParam.class);
}@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Whitelist whitelist = parameter.getMethodAnnotation(Whitelist.class);
// 通过 webRequest 和 whitelist 校验白名单
return new AuthParam();
}
}
复制代码

▐ 拓展

当然,使用参数解析器也需要单独配置,我们同样在 WebMvcConfigurerAdapter内配置:

@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthParamResolver());
}
}
复制代码

这次实现完了,我还有些不放心,于是在网上查找是否还有其他方式可以实现此功能,发现常见的还有 Filter。

四、Filter


Filter 并不是 Spring 提供的,它是在 Servlet 规范中定义的,是 Servlet 容器支持的。被 Filter 过滤的请求,不会派发到 Spring 容器中。它的实现也比较简单,实现 javax.servlet.Filter接口即可。

由于不在 Spring 容器中,Filter 获取不到 Spring 容器的资源,只能使用原生 Java 的 ServletRequest 和 ServletResponse 来获取请求参数。

另外,在一个 Filter 中要显示调用 FilterChain 的 doFilter 方法,不然认为请求被拦截。实现类似:

public class WhitelistFilter implements javax.servlet.Filter {@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化后被调用一次
}@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 判断是否需要拦截
chain.doFilter(request, response); // 请求通过要显示调用
}@Override
public void destroy() {
// 被销毁时调用一次
}
}
复制代码

▐ 拓展

Filter 也需要显示配置:
@Configuration
public class FilterConfiguration {@Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new WhitelistFilter());
registration.addUrlPatterns("/*");
registration.setName("whitelistFilter");
registration.setOrder(1); // 设置过滤器被调用的顺序
return registration;
}
}
复制代码

五、总结


四种实现方式都有其适合的场景,那么它们之间的调用顺序如何呢?

Filter 是 Servlet 实现的,自然是最先被调用,后续被调用的是 Interceptor 被拦截了自然不需要后续再进行处理,然后是 参数解析器,最后才是切面的切点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_39240.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

翻转单词序列、按之字形顺序打印二叉树、二叉搜索树的第k个节点

1、翻转单词序列 本题考点&#xff1a;子串划分&#xff0c;子串逆置 牛客链接 题目描述&#xff1a; 牛客最近来了一个新员工Fish&#xff0c;每天早晨总是会拿着一本英文杂志&#xff0c;写些句子在本子上。同事Cat对Fish写的内容颇感兴趣&#xff0c;有一天他向Fish借来翻…

网络安全与IP安全

网络安全 是指网络系统的硬件&#xff0c;软件以及系统中的数据收到的保护。 保护的基本属性为&#xff1a;机密性&#xff0c;身份认证&#xff0c;完整性和可用性&#xff1b; 基本特征&#xff1a;相对性&#xff0c;时效性&#xff0c;相关性&#xff0c;不确定性&#xf…

azkaban表project_flows数据分析

project_flows表中数据是怎么存入进去的呢,其中有个JSON字符串是乱码,怎么设置的呢?搜索插入语句地方如下: 查看压缩类型,2为Gzip压缩 public enum EncodingType {PLAIN(1), GZIP(2); 查看flow.toObject方法,其实返回的是一个MAP,定义如下: 查看convertJsonToBytes方…

YonBuilder开发之后端函数

在前几期的文章中我们已经介绍过如何在YonBuilder中使用前端函数实现数据过滤功能。相对应于前端函数&#xff0c;在YonBuilder中还可以使用后端函数实现对于程序的扩展。通过前端函数实现的更多的是与页面交互相关的功能&#xff0c;而后端函数主要是用于预制按钮功能的扩展开…

自然算法 - AI面试基础补全

手撕BP神经网络手写Bert和Transformer&#xff08;BERT很细节的地方&#xff0c;比如文字标签CLS&#xff0c;par&#xff09;学习pytorch&#xff0c;tensorflow AI算法岗位 可看网站 牛客网站 面经回复 github 项目连接 算法工程师岗位必备知识 问答 ELMO、GPT、…

操作系统_多线程笔记(二)

文章目录1.线程状态2.多线程在的意义是什么?1.线程状态 状态是针对当前线程调度的情况来描述的,因为线程是系统调度的基本单位,所以状态是属于线程的属性 线程的六种状态: 注意: 1.一旦内核里的PCB消亡了,此时代码中创建的thread也就没有用了,即内核里的线程释放的时候无…

信号包络及其提取方法(Matlab)

信号包络及其提取方法 介绍信号包络&#xff0c;以及信号包络的提取方法。 一、信号包络 直观地从时域来讲&#xff0c;信号包络就是信号波形的轮廓。 本质上&#xff0c;信号包络是带通信号的基带部分。 一个实带通信号记为x(t)&#xff0c;将它频谱的中心频点搬移到零频…

Win,M1Mac上安装jupyter的MATLAB支持插件的方法

tags: MATLAB Win Mac Tips 写在前面 11月的最后一天了, 总结一下支持MATLAB的一个jupyter的插件, 有了这个你就可以在jupyter notebook或者jupyter lab上面使用MATLAB语句了, 还是很不错的, 虽然我安装了好久… 下面来说一下我在我的电脑以及朋友的电脑(Win11)上面安装这个…

实例方法(instance method)、类方法、构造方法(三)

实例方法&#xff08;没有static&#xff09;的概念 对象被称为实例。实例相关的有&#xff1a;实例变量、实例方法。实例变量是对象变量。实例方法是对象方法。实例方法没有static。&#xff08;对象方法&#xff0c;对象级别的方法&#xff09; 实例方法的调用需要先new一个…

高维多元时序数据聚类

1. 简介 收集数据的能力不断增强&#xff0c;使我们有可能收集大量的异构数据。在可用的异构数据中&#xff0c;时间序列代表着尚未被充分探索的信息母体。当前的数据挖掘技术在分析时间序列时存在多个缺点&#xff0c;尤其是在应同时分析多个时间序列&#xff08;即多维时间序…

JVM运行时数据 堆

JVM运行时数据 堆快速调试堆参数设置堆分类运行流程Minor GC、Major GC与Full GC分代思想内存分配策略TLAB堆空间参数设置快速调试 一个JVM实例只存在一个堆内存&#xff0c;对也是Java内存管理的核心区域Java 堆区在Jvm启动的时候创建&#xff0c;其空间大小也就确定了。是JV…

[附源码]计算机毕业设计springboot课室预约系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

NVIDIA 7th SkyHackathon(八)使用 Flask 与 Vue 开发 Web

1.页面效果 Web 采用 flaskvue 开发&#xff0c;效果图如下 2.后端 import sys import subprocess import os from PIL import Image from datetime import datetime from ASR_metrics import utils as metricsfrom werkzeug.wrappers import Request, Response from …

分层架构理论基础

一、三层架构 1、什么是三层架构 三层架构&#xff08;3-tier architecture&#xff09;通常意义上的三层架构就是将整个业务应用划分为&#xff1a;表示层&#xff08;User Interface layer&#xff09;、业务逻辑层&#xff08;Business Logic Layer&#xff09;、数据访问层…

数仓之hive自定义UDTF函数详解

学习目录一、自定义UDTF函数一、自定义UDTF函数 1.说明文档 A custom UDTF can be created by extending the GenericUDTF abstract class and then implementing the initialize, process, and possibly close methods. The initialize method is called by Hive to notify t…

本机使用python操作hdfs搭建及常见问题

一.虚拟机安装CentOS7并配置共享文件夹 二.CentOS 7 上hadoop伪分布式搭建全流程完整教程 三.本机使用python操作hdfs搭建及常见问题 四.mapreduce搭建 五.mapper-reducer编程搭建 本机使用python操作hdfs搭建及常见问题一、环境搭建1.打开虚拟机系统&#xff0c;打开hadoop2.修…

高效率开发Web安全扫描器之路(一)

一、背景 经常看到一些SRC和CNVD上厉害的大佬提交了很多的漏洞&#xff0c;一直好奇它们怎么能挖到这么多漏洞&#xff0c;开始还以为它们不上班除了睡觉就挖漏洞&#xff0c;后来有机会认识了一些大佬&#xff0c;发现它们大部分漏洞其实是通过工具挖掘的&#xff0c;比如说下…

安卓版微信8.0.31内测版出炉:安装包变小,功能变多!

人是社会性生物&#xff0c;建立依恋、经营亲密关系是人的本能&#xff0c;只不过到了网络时代之后&#xff0c;用户进行交流的方式几乎都变成了微信等社交软件。 不仅可以让用户很便捷的和朋友进行沟通&#xff0c;并且在上班办公的时候&#xff0c;也是可以轻松传输文件等&a…

MCUXpresso IDE下高度灵活的FreeMarker链接文件模板机制

一、准备工作 首先需要准备好环境&#xff0c;包含必要的软件&#xff0c;痞子衡的环境如下&#xff1a; 集成开发环境&#xff1a; MCUXpresso IDE_11.6.0_8187&#xff0c;点此下载软件开发包&#xff1a; SDK_2.12.1_EVK-MIMXRT1170&#xff08;Toolchain需包含MCUXpresso I…

Compose学习-> Text()

设置文本&#xff1a;text xxx 直接设置 Text(text "我是一个Text")引用资源文件&#xff1a;stringResource Text(text stringResource(id R.string.string_text))设置字体颜色&#xff1a;color xxx 引用系统自带的颜色 Text(text "我是一个Text"…