深入技术细节:放弃Spring Security,自己实现Token权限控制!

news2024/9/21 21:59:19

最近做了个项目,大家都知道很多的项目都是在自己手上原本的框架内进行业务开发。但是甲方爸爸的这个项目需要交付原代码,并且要求框架逻辑简单清晰,二次开发简易上手。

那不是要重新从0到1写一套框架吗?

试着先给甲方爸爸报一下老的框架吧。于是我把框架内有的技术点和实现组件罗列了出来,甲方爸爸看后立马做了表述:框架臃肿复杂,就单单一个Spring Security必须要求切割。

没办法谁给钱,谁就是老大呗。

索性开发周期时长还算可以,那就重新弄一套,就简简单单的框架逻辑,有数据库、缓存机制就可以了,把之前的什么消息队列、加密机制、OSS、推送、短信还有大数据套件全删除掉,自己还轻松不是。

以上说的都好办,但是问题来了,如何放弃Spring Security,利用Spring Boot自身的接口来实现Token校验和长登录状态以及登录异常回馈呢。总不能一个接口下进行一次校验那么麻烦吧。

索性咱写代码和思维逻辑还挺牛掰的,一通搞下来,其实很简单。那么来看看怎么样实现吧,

1. 了解AsyncHandlerInterceptor

AsyncHandlerInterceptor 是 Spring 框架中的一个接口,它用于处理异步请求的拦截。当一个请求被异步处理时,即请求的处理被提交到一个单独的线程中执行,标准的 HandlerInterceptor 可能不会按预期工作,因为它们通常依赖于请求和响应的生命周期。AsyncHandlerInterceptor 提供了额外的回调方法来处理异步请求的完成阶段。

来看看接口的方法:

// 当异步请求开始处理时调用。在这个阶段,主线程可能会继续处理其他请求,而异步处理则在另一个线程中继续。这个方法可以用来执行一些初始化操作,例如设置异步上下文。
afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler); 
// 在请求处理之前调用。这个方法是 HandlerInterceptor 接口的一部分,也被 AsyncHandlerInterceptor 继承。
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
// 在请求处理之后,但在视图渲染之前调用。这个方法也是 HandlerInterceptor 接口的一部分。
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView); 
// 在整个请求完成之后调用,包括视图渲染。这个方法也是 HandlerInterceptor 接口的一部分。在异步处理的情况下,这个方法将在异步任务完成后被调用。
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);

afterConcurrentHandlingStarted当异步请求开始处理时调用。主线程可能会继续处理其他请求,而异步处理则在另一个线程中继续。这个方法可以用来执行一些初始化操作,例如:设置异步上下文。

由此方法我们可以设想,在异步请求处理时,我们可以将前端请求信息进行拦截,然后进行逻辑判断不就可以了吗?

那么问题就好解决了,我们来实现

2.接口实现

我们完成上下文的设置,首先我们先一个保存请求和返回还有用户信息的类

@Data
public class ContextHolder {
    private HttpServletRequest request;
    private HttpServletResponse response;
    /**
     * 用户信息缓存
     */
    private SysUser userCache;
}

然后我们写一个ThreadLocal,将ContextHolder保存在内。

public class RequestContext {

    private static ThreadLocal<ContextHolder> context = new ThreadLocal<>();

    public static void setContext(ContextHolder contextHolder) {
        context.set(contextHolder);
    }

    public static HttpServletRequest getHttpRequest() throws ExchangeException {
        return getContext().getRequest();
    }


    public static HttpServletResponse getHttpResponse() throws ExchangeException {
        return getContext().getResponse();
    }

    public static SysUser getAccountCache() throws ExchangeException {
        return getContext().getUserCache();
    }

    private static ContextHolder getContext() throws ExchangeException {
        ContextHolder contextHolder = context.get();
        if (contextHolder == null) {
            throw new ExchangeException(ResultStatusEnum.CONTEXT_ERROR);
        }
        return contextHolder;
    }

    public static void remove() {
        context.remove();
    }
}

​​​​​​​最后,我们将HandlerInterceptor的方法重写并实现。

@Component
public class ApiHandlerInterceptor implements AsyncHandlerInterceptor {
 @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 一些不需要拦截的资源
        // spring静态资源处理不拦截
        if (handler instanceof ResourceHttpRequestHandler) {
            return true;
        }
        // 特殊的链接放行,如swagger
        String url = request.getRequestURI();
        if (url.contains(EXCLUDE_URL)) {
            return true;
        }
        // 排除标记过的接口
        // 权限配置:目录3说明
        
        //保存请求上下文
        ContextHolder holder = new ContextHolder();
        holder.setRequest(request);
        holder.setResponse(response);
        RequestContext.setContext(holder);
        
        
        // TOKEN校验 目录4说明
     }
     
    // 销毁
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        RequestContext.remove();
    }
}

3. 权限接口实现

以上我们排除了一个静态资源和特殊的链接,那么我们怎么样设置权限呢,如:登录注册和特定的无Token亦可访问的接口和需要Token访问的接口怎么配置

先看例子,我们再来将处理逻辑。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
    boolean required() default true;
}

​​​​​​​我们先一个注解@Login。

 // 排除标记过的接口
if (handler instanceof HandlerMethod) {
    HandlerMethod method = (HandlerMethod) handler;
    Login login = method.getMethodAnnotation(Login.class);
    if (null != ogin) {
        return true;
    }
}

​​​​​​​在HandlerInterceptor方法实现内有handler参数,如果handler是HandlerMethod的实现,那么我们通过反射获取到方法上标记的注解即可标明此方法不进入TOKEN校验

接口实现如下:

@Login
@RequestMapping(path = "/register", method = RequestMethod.POST)
public ResponseData<Integer> register(@RequestBody @Valid RegistersCnd cnd) throws Exception {
    return ResponseData.success(userService.register(cnd));
}

4. TOKEN校验

Token的检验,前端一般会放在请求头那么就是在HttpServletRequest内获取,来看实现:

String token = request.getHeader(UserConstruct.ACCESS_TOKEN);
// 判断header里面是否有token信息
if (StrUtil.isNotEmpty(token)) {
    // 从redis中获取用户的token信息
    String userCache = RedissonUtils.getBucket(UserRedisConfig.TOKEN_REDIS_PREFIX + token);
    if (null == userCache || userCache.isEmpty()) {
        //获取 刷新TOKEN 的信息
        String refreshToken = RedissonUtils.getBucket(UserRedisConfig.TOKEN_TO_REFRESH_PREFIX + token);
        // 未获取到,表示TOKEN真正过期
        if (null == refreshToken || refreshToken.isEmpty()) {
            throw new TokenException(ResultStatusEnum.TOKEN_EXPIRE);
        }
        // 未用户获取到,表示TOKEN真正过期
        userCache = RedissonUtils.getBucket(UserRedisConfig.REFRESH_TOKEN_REDIS_PREFIX + refreshToken);
        if (null == userCache || userCache.isEmpty()) {
            throw new TokenException(ResultStatusEnum.TOKEN_EXPIRE);
        } else {
            //获取到了,给前端提示,需要刷新TOKEN了
            throw new TokenException(ResultStatusEnum.REFRESH_TOKEN);
        }
    }
    SysUser sysUser = JSONUtil.toObj(userCache, SysUser.class);
    // 检查用户状态
    checkUserStatus(sysUser);
    // 存储用户信息
    holder.setUserCache(sysUser);
    return true;
}

​​​​​​​以上逻辑内,包含了获取到token信息后,进行和缓存内的用户信息进行匹配检查、以及刷新token的业务逻辑,刷新Token的标记也是缓存内的获取检查。

至此,一个简单的校验权限匹配机制完成。

本人前后端全栈开发,所以约定俗称的接口调试及标记校验都是一个人完成,此种实现简化了好多好多的代码逻辑。不用臃肿复杂的Spring Security其实挺好。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

美食杂志制作秘籍:引领潮流,引领味蕾

美食杂志是一种介绍美食文化、烹饪技巧和美食体验的杂志&#xff0c;通过精美的图片和生动的文字&#xff0c;向读者展示各种美食的魅力。那么&#xff0c;如何制作一本既美观又实用的美食杂志呢&#xff1f; 首先&#xff0c;你需要选择一款适合你的制作软件。比如FLBOOK在线制…

Java微服务分布式事务框架seata的TCC模式

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

教大家使用vue实现 基础购物车。

首先我要知道一点 其能够数据变化的 都用{{}}来进行渲染类似 来看一下实现效果 实现思路 &#xff1a; 1&#xff0c;引进 vue.js 2,setup 将声明变量 方法放在setup里面 3&#xff0c;用响应式声明 ref&#xff08;&#xff09; 或rective声明 可以声明对象等等 let 也…

AST学习入门

AST学习入门 1.AST在线解析网站 https://astexplorer.net/ 1.type: 表示当前节点的类型&#xff0c;我们常用的类型判断方法t.is********(node)**,就是判断当前的节点是否为某个类型。 2**.start**:表示当前节点的开始位置 3.end:当前节点结束 4.loc : 表示当前节点所在的行…

产品推荐 | 基于Xilinx FPGA XC5VFX100T的6U VPX视频叠加板卡

01、产品概述 本板卡是基于Xilinx FPGA XC5VFX100T的6U VPX视频叠加板卡。主要用于视频叠加板具有多种高清图形输入接口&#xff0c;可实现其中两路高清视频信号的开窗显示和叠加显示功能&#xff1b;或者输出和输入图形接口的转换。 02、物理特性 ● 尺寸&#xff1a;6U CPC…

【文献阅读】AlphaFold touted as next big thing for drug discovery — but is it?

今天来精读2023年10月发在《Nature》上的一篇新闻&#xff1a;AlphaFold touted as next big thing for drug discovery — but is it? (nature.com)https://www.nature.com/articles/d41586-023-02984-w Questions remain about whether the AI tool for predicting protein …

Python Windows系统 虚拟环境使用

目录 1、安装 2、激活 3、停止 1、安装 1&#xff09;为项目新建一个目录&#xff08;比如&#xff1a;目录命名为learning_log&#xff09; 2&#xff09;在终端中切换到这个目录 3&#xff09;执行命令&#xff1a;python -m venv ll_env&#xff0c;即可创建一个名为ll…

常用的6个的ChatGPT网站,国内可用!

GPTGod &#x1f310; 链接&#xff1a; GPTGod &#x1f3f7;️ 标签&#xff1a; GPT-4 免费体验 支持API 支持绘图 付费选项 &#x1f4dd; 简介&#xff1a;GPTGod 是一个功能全面的平台&#xff0c;提供GPT-4的强大功能&#xff0c;包括API接入和绘图支持。用户可以选择免…

成都百洲文化传媒有限公司电商新浪潮的领航者

在当今电商行业风起云涌的时代&#xff0c;成都百洲文化传媒有限公司以其独特的视角和专业的服务&#xff0c;成为了众多商家争相合作的伙伴。今天&#xff0c;就让我们一起走进百洲文化的世界&#xff0c;探索其背后的成功密码。 一、百洲文化的崛起之路 成都百洲文化传媒有限…

短视频矩阵系统---php7.40版本升级自研

短视频矩阵系统---php7.40版本升级自研 1.部署及搭建 相对于其他系统&#xff0c;该系统得开发及部署难度主要在各平台官方应用权限的申请上&#xff0c;据小编了解&#xff0c;目前抖音短视频平台部分权限内侧名额已满&#xff0c;巧妇难为无米之炊&#xff0c;在做相关程序…

unity 添加newtonsoft-json

再git url上添加&#xff1a;com.unity.nuget.newtonsoft-json

手机如何设置静态IP地址显示

随着移动互联网的普及&#xff0c;手机已经成为我们日常生活中不可或缺的一部分。在连接无线网络时&#xff0c;我们有时需要设置手机的IP地址为静态&#xff0c;以满足特定的网络需求或解决某些网络问题。本文将指导您如何在手机上设置静态IP地址显示&#xff0c;让您更好地管…

二、typescript基础语法

一、条件语句 二、函数 1、有名函数 function add(x:number, y:number):number {return x y;}2、匿名函数 let add function (x:number, y:number):number {return x y;}函数可选参数 function buildName(firstname: string, lastname?:string) {if (lastname) {return fi…

蓝牙耳机哪个品牌最好?2024年热门机型推荐合集分享

​随着蓝牙耳机的普及&#xff0c;越来越多的年轻人开始追求这种无线的便利。市场上品牌众多&#xff0c;款式多样&#xff0c;选择起来确实让人眼花缭乱。我整理了一份蓝牙耳机品牌排行榜前十名&#xff0c;希望能为你提供一些参考&#xff0c;帮助你找到心仪的耳机。 一、202…

HarmonyOS实战开发-编写一个分布式邮件系统

概述 本篇Codelab是基于TS扩展的声明式开发范式编程语言编写的一个分布式邮件系统&#xff0c;可以由一台设备拉起另一台设备&#xff0c;每次改动邮件内容&#xff0c;都会同步更新两台设备的信息。效果图如下&#xff1a; 说明&#xff1a; 本示例涉及使用系统接口&#xff…

基于springboot的牙科就诊管理系统

技术&#xff1a;springbootmysqlvue 一、系统背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样…

mysql分页查询多用GitCode平台

目录 一、在GitCode平台AI搜索结果&#xff08;这个更优&#xff09; 二、在百度搜索输入“mysql Java分页查询”的输出结果&#xff1a; 三、推荐的文章 四、GitCode的使用 1&#xff09;如搜索jdk11可以直接下载jdk11的包 2&#xff09;搜索开源项目 3&#xff09;如搜…

学浪怎么下载视频_学浪下载视频方法

越来越多人购买了学浪课程&#xff0c;但是苦于学浪视频官方不提供下载选项&#xff0c;于是就有人做了学浪视频下载软件&#xff0c;这里就教大家如何下载你已经购买的学浪课程 本次教程用到的软件都在下面的链接 链接&#xff1a;https://pan.baidu.com/s/1y7vcqILToULrYAp…

网络: 数据链路层

数据链路层: 数据帧的封装与传输 以太网数据帧 源地址和目的地址是指网卡的硬件地址(也叫MAC地址), 长度是48位,是在网卡出厂时固化的;帧协议类型字段有三种值,分别对应IP、ARP、RARP;帧末尾是CRC校验码 以太网 "以太网" 不是一种具体的网络, 而是一种技术标准; 既…

【机器学习】一文搞懂算法模型之:Transformer

Transformer 1、引言2、Transformer2.1 定义2.2 原理2.3 算法公式2.3.1 自注意力机制2.3.1 多头自注意力机制2.3.1 位置编码 2.4 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c; 你说transformer是个啥&#xff1f; 小鱼&#xff1a;嗯… 啊… 嗯…就是… 小屌…