首页 > 其他分享 >Sa-Token的v1.39.0自定义鉴权注解怎么玩

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

时间:2024-09-17 19:54:24浏览次数:10  
标签:String 自定义 Token 鉴权 token transCode 注解 Sa public

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

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview


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

简介

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 {

    @Override
    public 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
    @AllArgsConstructor
    private static class ApiAccessPO {

        private String transCode;

        private String token;
    }
}

3、配置拦截器

@Slf4j
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    private final ApiAccessInterceptor apiAccessInterceptor;

    @Override
    public 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都能通过验证才能通过拦截器。

Sa-Token的v1.39.0自定义鉴权注解怎么玩_拦截器_02

使用新版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> {

    @Override
    public Class<ApiAccess> getHandlerAnnotationClass() {
        return ApiAccess.class;
    }

    @Override
    public 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;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 删除 registry.addInterceptor(apiAccessInterceptor);
        registry.addInterceptor(new SaInterceptor());
    }
}

4、验证

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

怎么做到的?

对比差异

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

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

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

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

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

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

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

Sa-Token的v1.39.0自定义鉴权注解怎么玩_拦截器_06

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,并提供鉴权策略类和注解处理器集合。

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

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

还是废话一下吧。

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

小结

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

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

写在最后

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


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

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview

Sa-Token的v1.39.0自定义鉴权注解怎么玩_拦截器_08

标签:String,自定义,Token,鉴权,token,transCode,注解,Sa,public
From: https://blog.51cto.com/wnhyang/12037638

相关文章

  • The 1st Universal Cup. Stage 19: America
    Preface中秋加训,发现Ucup里好多比赛要么之前做过,要么就是太毒瘤(有场比赛直接把模\(998244353\)写在题目名里了),因此就直接跳到这场北美区决赛了这场前期因为卡题意以及卡签到打的还是挺难受的,不过好在恶心归恶心题目还是都能出,最后也是堪堪写了9题由于这场没找到题解(其实......
  • which products are best-selling and which are slow-moving, so as to adjust procu
    SKUstandsforStockKeepingUnit. Specifically: I.Intheretailindustry 1. Representingspecificproducts:Foraspecificproduct,eachdifferentcombinationofattributes(suchascolor,size,style,etc.)isusuallyregardedasanindependen......
  • 一个绝佳的Windows和WSA传输文件的方法
    前言在Windows和Windows子系统安卓(WSA)之间传输文件,一直以来都是一个让人感到头疼的问题。虽然网络上有很多解决方案,但大多数方法要么过于繁琐,要么效果不尽如人意,常常需要依赖繁琐的步骤和额外的工具,效率一言难尽。最近经常用Localsend,在我的电脑和两台安卓设备,对象的Iphon......
  • 【生成对抗网络GAN】最全的关于生成对抗网络Generative Adversarial Networks,GAN的介
    【生成对抗网络GAN】最全的关于生成对抗网络GenerativeAdversarialNetworks,GAN的介绍!!【生成对抗网络GAN】最全的关于生成对抗网络GenerativeAdversarialNetworks,GAN的介绍!!文章目录【生成对抗网络GAN】最全的关于生成对抗网络GenerativeAdversarialNetworks,GAN的......
  • 前端必知必会-Sass 嵌套规则和属性
    文章目录Sass嵌套规则和属性Sass嵌套规则Sass嵌套属性总结Sass嵌套规则和属性Sass嵌套规则Sass允许您以与HTML相同的方式嵌套CSS选择器。查看网站导航的一些Sass代码示例:示例SCSS语法:nav{ ul{ margin:0; padding:0; list-style:none......
  • Python中的魔法:探索自定义Context Manager的魅力
    引言在日常开发中,我们经常需要处理各种资源管理的问题。比如,打开一个文件后需要记得关闭;使用完数据库连接后需要释放等。如果这些操作处理不当,可能会导致内存泄漏或者其他资源浪费的问题。ContextManager的设计正是为了解决这些问题而生,它提供了一种自动化的资源管理方式。那么,如......
  • 【SCI2区】麻雀搜索算法SSA-TCN-Multihead-Attention回归预测(多输入单输出)【含Matlab
    ✅博主简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,Matlab项目合作可私信或扫描文章底部QQ二维码。......
  • Python Web开发中的扩展与插件开发:从自定义到打包与发布
    PythonWeb开发中的扩展与插件开发:从自定义到打包与发布目录⚙️Flask中的自定义扩展开发......
  • 自定义类型结构体
    1.结构体类型的声明 structStu{charname[20];//名字intage;//年龄charsex[5];//性别charid[20];//学号};//分号不能丢2.结构体变量的创建和初始化#include<stdio.h>structStu{charname[20];//名字intage;//年龄charsex[5];//性别c......
  • tauri2.x+vue3实践篇|封装多窗口|tauri2.0自定义托盘闪烁消息提示+右键菜单
    最近一直在捣鼓Tauri2.0跨平台框架,之前也有分享几篇tauri1.x实例项目。相较于1.0,tauri2.x框架api有了比较多的变更,而且支持创建android/ios移动端应用。实现类似QQ托盘闪烁消息提醒及右键菜单。框架信息"@tauri-apps/api":">=2.0.0-rc.0","@tauri-apps/cli":">=......