sa-token源码解析一

Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证权限认证Session会话

点登录OAuth2.0微服务网关鉴权 等一系列权限相关问题。

首先引入pom

<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
		<dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot-starter</artifactId>
            <version>${sa-token-version}</version>
        </dependency>
我们来一起看哈SaToken的登录认证是怎么实现的就一行代码
StpUtil.login(id);  

我们从入口debugger看源码

 使用stpLogic

 StpLogic默认type为login

 看stpLogic的构造方法

 

 

 调用stplogic的login方法,传入SaLoginModal配置类

 主要逻辑都在这里面我们一步步看

0.前置检查:如果此账号已被封禁. 

SaTokenDao的实现类

 默认实现为SaTokenDaoDefaultImpl 从dataMap中取出为key为封禁的数据,如果不为空说明被封禁

 如果添加了redis支持,则查询通过key查询redis数据

1.初始化 loginModel 

 

 starter里SaTokenConfig 已经在spring ioc 过程已经设置进去了

 那SaTokenConfig是怎么读取yml或properties的值

 

 

 

初始哈loginModal完成

2、生成一个token  

判断是否允许并发登录,如果配置为共享token, 则尝试从Session签名记录里取出token,如果不允许并发登录,则将这个账号的历史登录标记为:被顶下线,仍未成功创建tokenValue, 则开始生成一个

我们具体来看一哈创建token的方法

 创建的方法是放在SaStrategy里的createToken方法上

 我们继续来看看SaTokenAction接口的实现类 SaTokenActionDefaultImpl

 通过不同规则生产Token(默认,还是uuid )

 3. 获取 User-Session , 续期

续期具体实现实际上就是在SaTokenDao实现类中

 

4. 持久化其它数据    token -> id 映射关系  

在redis中数据为

 

 

目前有4种存储值的方法 (在 Request作用域里: 存值、取值)

 

看到这里我们看看这个response从哪里来 

 

 我们能看出来spring中还是通过RequestContextHolder中拿到的response

RequestContextHolder顾名思义,持有上下文的Request容器.

request和response等是什么时候设置进去的?

需要对springMVC结构的`DispatcherServlet`的结构有一定了解

1. HttpServletBean 进行初始化工作

2. FrameworkServlet 初始化 WebApplicationContext,并提供service方法预处理请

3. DispatcherServlet 具体分发处理.

那么就可以在FrameworkServlet查看到该类重写了service(),doGet(),doPost()...等方法,这些实现里面都有一个预处理方法`processRequest(request, response);`,所以定位到了我们要找的位置

查看`processRequest(request, response);`的实现,具体可以分为三步:

  1. 获取上一个请求的参数
  2. 重新建立新的参数
  3. 设置到XXContextHolder
  4. 父类的service()处理请求
  5. 恢复request
  6. 发布事

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取上一个请求保存的LocaleContext
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//建立新的LocaleContext
    LocaleContext localeContext = buildLocaleContext(request);
//获取上一个请求保存的RequestAttributes
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//建立新的RequestAttributes
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, 
response, previousAttributes);
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), 
new RequestBindingInterceptor());
//具体设置的方法
    initContextHolders(request, localeContext, requestAttributes);
try {
        doService(request, response);
    }
catch (ServletException ex) {
failureCause = ex;
throw ex;
    }
catch (IOException ex) {
   failureCause = ex;
   throw ex;
    }
catch (Throwable ex) {
   failureCause = ex;
   throw new NestedServletException("Request processing failed", ex);
    }
finally {
//恢复
        resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
        }
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
            }
else {
if (asyncManager.isConcurrentHandlingStarted()) {
                    logger.debug("Leaving response open for concurrent processing");
                }
else {
this.logger.debug("Successfully completed request");
                }
            }
        }
//发布事件
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

再看initContextHolders(request, localeContext, requestAttributes)方法,把新的RequestAttributes设置进LocalThread,实际上保存的类型为ServletRequestAttributes,这也是为什么在使用的时候可以把RequestAttributes强转为ServletRequestAttributes.

private void initContextHolders(HttpServletRequest request, 
                                LocaleContext localeContext, 
                                RequestAttributes requestAttributes) {
if (localeContext != null) {
        LocaleContextHolder.setLocaleContext(localeContext, 
this.threadContextInheritable);
    }
if (requestAttributes != null) {
        RequestContextHolder.setRequestAttributes(requestAttributes, 
this.threadContextInheritable);
    }
if (logger.isTraceEnabled()) {
        logger.trace("Bound request context to thread: " + request);
    }
}

 因此RequestContextHolder里面最终保存的为ServletRequestAttributes,这个类相比RequestAttributes`方法是多了很多

好的回归正题

通知监听器,账号xxx 登录成功 

 

 回调默认监听器

 返回登录成功

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
sa-token可以通过以下配置来校验token: 1. 配置token名称:在yml配置文件中,可以通过设置`sa-token.token-name`来指定token的名称,例如`sa-token.token-name: X-Token`\[1\]。 2. 配置token有效期:可以通过设置`sa-token.timeout`来指定token的有效期,单位为秒,默认为30天,可以设置为-1代表永不过期,例如`sa-token.timeout: 2592000`\[1\]。 3. 配置token临时有效期:可以通过设置`sa-token.activity-timeout`来指定token的临时有效期,即在指定时间内无操作就视为token过期,单位为秒,默认为-1,表示不设置临时有效期,例如`sa-token.activity-timeout: -1`\[1\]。 4. 配置是否允许同一账号并发登录:可以通过设置`sa-token.is-concurrent`来指定是否允许同一账号并发登录,为true时允许一起登录,为false时新登录会挤掉旧登录,例如`sa-token.is-concurrent: true`\[1\]。 5. 配置是否共用一个token:可以通过设置`sa-token.is-share`来指定在多人登录同一账号时,是否共用一个token,为true时所有登录共用一个token,为false时每次登录会新建一个token,例如`sa-token.is-share: true`\[1\]。 6. 配置是否输出操作日志:可以通过设置`sa-token.is-log`来指定是否输出操作日志,为true时输出操作日志,为false时不输出操作日志,例如`sa-token.is-log: false`\[1\]。 7. 配置是否使用cookie保存token:可以通过设置`sa-token.is-read-cookie`来指定是否使用cookie保存token,为true时使用cookie保存token,为false时不使用cookie保存token,例如`sa-token.is-read-cookie: false`\[1\]。 8. 配置是否使用head保存token:可以通过设置`sa-token.is-read-head`来指定是否使用head保存token,为true时使用head保存token,为false时不使用head保存token,例如`sa-token.is-read-head: true`\[1\]。 通过以上配置,sa-token可以根据配置的规则来校验token的有效性。 #### 引用[.reference_title] - *1* [最简单的权限验证实现——使用Sa-Token进行权限验证](https://blog.csdn.net/lp840312696/article/details/127072424)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [探索 Sa-Token (一) SpringBoot 集成 Sa-Token](https://blog.csdn.net/weixin_38982591/article/details/126764928)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没事搞点事做serendipity

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值