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

Sa-Token的v1.39.0自定义鉴权注解怎么玩

个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview


image

简介

Sa-Token最新的v1.39.0版本的更新日志中有这么一句话

核心:

  • 升级:重构注解鉴权底层,支持自定义鉴权注解了。 [重要]

正巧最近有看一个关于鉴权的东西,顺便看一下吧!

常见的自定义注解鉴权

目标:对于后端开放的api进行鉴权。

1、自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ApiAccess {/*** 交易码** @return 交易码*/String transCode();
}

2、定义拦截器

@Slf4j
@Component
public class ApiAccessInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("ApiAccessInterceptor preHandle");if (handler instanceof HandlerMethod) {Method method = ((HandlerMethod) handler).getMethod();if (method.isAnnotationPresent(ApiAccess.class)) {ApiAccess apiAccess = method.getAnnotation(ApiAccess.class);String transCode = apiAccess.transCode();String transCodeHeader = request.getHeader("transCode");String token = request.getHeader("token");if (!StringUtils.hasText(transCodeHeader) || !StringUtils.hasText(token)) {throw new RuntimeException("transCode or token is empty");}log.info("transCode: {}, transCodeHeader:{}, token:{}", transCode, transCodeHeader, token);if (!transCode.equals(transCodeHeader)) {throw new RuntimeException("transCode not match");}ApiAccessUtil.valid(transCode, token);}}return true;}
}

下面是辅助验证的方法,真实生产上应该是查数据库或其他。

public class ApiAccessUtil {public static final List<ApiAccessPO> API_ACCESS_LIST = new ArrayList<>();static {API_ACCESS_LIST.add(new ApiAccessPO("wnhyang01", "123456"));API_ACCESS_LIST.add(new ApiAccessPO("wnhyang02", "234567"));API_ACCESS_LIST.add(new ApiAccessPO("wnhyang03", "888888"));API_ACCESS_LIST.add(new ApiAccessPO("wnhyang04", "666666"));}public static void valid(String transCode, String token) {for (ApiAccessPO apiAccessPO : API_ACCESS_LIST) {if (apiAccessPO.getTransCode().equals(transCode) && apiAccessPO.getToken().equals(token)) {return;}}throw new RuntimeException("invalid access");}@Data@NoArgsConstructor@AllArgsConstructorprivate static class ApiAccessPO {private String transCode;private String token;}
}

3、配置拦截器

@Slf4j
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {private final ApiAccessInterceptor apiAccessInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(apiAccessInterceptor);}
}

4、验证

@Slf4j
@RestController
@RequestMapping("/api")
public class ApiAccessController {@GetMapping("/wnhyang01")@ApiAccess(transCode = "wnhyang01")public String wnhyang01() {return "wnhyang01";}@GetMapping("/wnhyang02")@ApiAccess(transCode = "wnhyang02")public String wnhyang02() {return "wnhyang02";}@GetMapping("/wnhyang03")@ApiAccess(transCode = "wnhyang03")public String wnhyang03() {return "wnhyang03";}@GetMapping("/wnhyang04")@ApiAccess(transCode = "wnhyang04")public String wnhyang04() {return "wnhyang04";}
}

测试可知,只有HeadertransCodetoken都不为空,并且HeadertransCode与接口配置的唯一transCode一致前提下,transCodetoken都能通过验证才能通过拦截器。

image

使用新版Sa-Token完成

自定义注解

1、自定义注解

与前面一致就行

2、创建注解处理器

实现SaAnnotationHandlerInterface接口的两个抽象方法就好,checkMethod放鉴权逻辑,与前面的拦截器方法一致就好,将拦截器里使用的request替换为SaHolder.getRequest()(关于这个自己可以看官网文档或者源码都可,我之前介绍Sa-Token组件时也有提过)。

注意使用@Component将类注册为IOCBean就省去了手动注册了。

SaAnnotationStrategy.instance.registerAnnotationHandler(new CheckAccountHandler());
@Slf4j
@Component
public class ApiAccessHandler implements SaAnnotationHandlerInterface<ApiAccess> {@Overridepublic Class<ApiAccess> getHandlerAnnotationClass() {return ApiAccess.class;}@Overridepublic void checkMethod(ApiAccess apiAccess, Method method) {log.info("checkMethod");String transCode = apiAccess.transCode();String transCodeHeader = SaHolder.getRequest().getHeader("transCode");String token = SaHolder.getRequest().getHeader("token");if (!StringUtils.hasText(transCodeHeader) || !StringUtils.hasText(token)) {throw new RuntimeException("transCode or token is empty");}log.info("transCode: {}, transCodeHeader:{}, token:{}", transCode, transCodeHeader, token);if (!transCode.equals(transCodeHeader)) {throw new RuntimeException("transCode not match");}ApiAccessUtil.valid(transCode, token);}
}

3、配置拦截器

与前面一样,删除自己的拦截器配置,加上SaInterceptor就好

@Slf4j
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {private final ApiAccessInterceptor apiAccessInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 删除 registry.addInterceptor(apiAccessInterceptor);registry.addInterceptor(new SaInterceptor());}
}

4、验证

试过你就话发现,前后效果一样。

怎么做到的?

对比差异

通过Idea选择不同版本的提交记录对比,因为我使用的是Jdk17+boot3所以看的是图下的差异,其他类同的。

image

可以看到,SaInterceptor鉴权逻辑从左边一坨变为了右边一行。

image

sa-token-core的对比来看,原来的SaStrategy删除了所有相关于鉴权的方法,与此对应新增了SaAnnotationStrategy类。

image

SaAnnotationStrategy默认注册了原来的所有鉴权处理器。

image

SaInterceptor鉴权使用的就是SaAnnotationStrategycheckMethodAnnotation方法,其就是将所有注册的注解处理器跑一遍。

/*** 对一个 [Method] 对象进行注解校验 (注解鉴权内部实现)*/
@SuppressWarnings("unchecked")
public SaCheckMethodAnnotationFunction checkMethodAnnotation = (method) -> {// 遍历所有的注解处理器,检查此 method 是否具有这些指定的注解for (Map.Entry<Class<?>, SaAnnotationHandlerInterface<?>> entry: annotationHandlerMap.entrySet()) {// 先校验 Method 所属 Class 上的注解Annotation classTakeAnnotation = instance.getAnnotation.apply(method.getDeclaringClass(), (Class<Annotation>)entry.getKey());if(classTakeAnnotation != null) {entry.getValue().check(classTakeAnnotation, method);}// 再校验 Method 上的注解Annotation methodTakeAnnotation = instance.getAnnotation.apply(method, (Class<Annotation>)entry.getKey());if(methodTakeAnnotation != null) {entry.getValue().check(methodTakeAnnotation, method);}}
};

默认的所有注解处理器可以看sa-token-corecn.dev33.satoken.annotation.handler包。

可以知道其实就是将原来的SaStrategy的鉴权方法抽象到SaAnnotationHandlerInterface,并提供鉴权策略类和注解处理器集合。

image

另外记得前面说的如果自定义实现了SaAnnotationHandlerInterface并且加入了Spring Ioc容器,就不用手动注册了吗?其实这个并不必多说,想想就知道是怎么实现的。

还是废话一下吧。

core里的默认注解处理器并不需要加入Spring Ioc,因为默认的初始化方法已经讲这些注册了,而用户如果自己扩展了注解处理器,就会被autoconfigSaBeaInject找到并完成注册。
在这里插入图片描述

小结

作为源码的主导者其实应该更加清楚,项目的不足,主导项目未来的规划发展,不仅仅是对于使用者提出的issues,更加应该是项目开发者的初心。

最后一句话:停在港口的船最安全,但这不是造船的意义!

写在最后

拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。


个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • SQL案例分析:计算延迟法定退休年龄
  • 【C++】——继承详解
  • CMS之Wordpress建设
  • 数学基础 -- 线性代数之奇异值
  • iOS 18 正式上線,但 Apple Intelligence 還要再等一下
  • Python实现Socket.IO的完整指南
  • ARM驱动学习之8 动态申请字符类设备号
  • Serverless 安全新杀器:云安全中心护航容器安全
  • string的模拟实现and友元
  • 政务安全体系构建中的挑战
  • Python的学习步骤
  • TDengine 与 SCADA 强强联合:提升工业数据管理的效率与精准
  • 物联网架构
  • steamdeck执行exe文件
  • 什么是Bean的循环依赖?解决方案是什么?
  • 收藏网友的 源程序下载网
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • CentOS 7 防火墙操作
  • const let
  • Cumulo 的 ClojureScript 模块已经成型
  • hadoop集群管理系统搭建规划说明
  • JAVA 学习IO流
  • JSONP原理
  • js数组之filter
  • LeetCode算法系列_0891_子序列宽度之和
  • Linux快速复制或删除大量小文件
  • Spark RDD学习: aggregate函数
  • 彻底搞懂浏览器Event-loop
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 我看到的前端
  • 携程小程序初体验
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • ​【经验分享】微机原理、指令判断、判断指令是否正确判断指令是否正确​
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • #07【面试问题整理】嵌入式软件工程师
  • #14vue3生成表单并跳转到外部地址的方式
  • #vue3 实现前端下载excel文件模板功能
  • #绘制圆心_R语言——绘制一个诚意满满的圆 祝你2021圆圆满满
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (12)目标检测_SSD基于pytorch搭建代码
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (算法)硬币问题
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转) RFS+AutoItLibrary测试web对话框
  • * CIL library *(* CIL module *) : error LNK2005: _DllMain@12 already defined in mfcs120u.lib(dllmodu
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET COER+CONSUL微服务项目在CENTOS环境下的部署实践