栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于SpringBoot的影像注册系统04 sa-token使用(源码解析 + 万字

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于SpringBoot的影像注册系统04 sa-token使用(源码解析 + 万字

逻辑:查询username存不存在,再查询密码是否正确,只要没有抛异常就调用sa-token的login方法。

StpUtil.login(userReal.getId());

login方法传入我们user的id,比如:

把int类型的数值传进去了。

步骤 4 sa-token登录认证

=================

[核心思想](()

所谓登录认证,说白了就是限制某些API接口必须登录后才能访问(例:查询我的账号资料)

那么如何判断一个会话是否登录?框架会在登录成功后给你做个标记,每次登录认证时校验这个标记,有标记者视为已登录,无标记者视为未登录!

所以,只要登录成功,我们就用

StpUtil.login(userReal.getId());

记录了当前用户的登录id

步骤 5 StpUtil.login(userReal.getId())的秘密

=======================================

我们如果不使用sa-token,怎么做登录功能呢?

是不是需要在传参的时候加一个HttpServletRequest,然后再用getSession方法获取session,把登录用户的信息放到session中?

而现在,只需要一句StpUtil.login(userReal.getId())就维持了登录状态,想也知道sa-token框架肯定也是把登录id放到session中了。因为没有用redis,所以要维持登录肯定是用了session或cookie。

如果是第一次登录,就生成tokenValue

然后把生成的token写入storage([存储器] 包装类SaStorage),

// 在当前会话写入当前tokenValue

setTokenValue(tokenValue, loginModel.getCookieTimeout());

setTokenValue方法:

public void setTokenValue(String tokenValue, int cookieTimeout){

SaTokenConfig config = getConfig();

// 将token保存到[存储器]里

SaStorage storage = SaHolder.getStorage();

// 判断是否配置了token前缀

String tokenPrefix = config.getTokenPrefix();

if(SaFoxUtil.isEmpty(tokenPrefix)) {

storage.set(splicingKeyJustCreatedSave(), tokenValue);

} else {

// 如果配置了token前缀,则拼接上前缀一起写入

storage.set(splicingKeyJustCreatedSave(), tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT + tokenValue);

}

// 注入Cookie

if(config.getIsReadCookie()){

SaResponse response = SaHolder.getResponse();

response.addCookie(getTokenName(), tokenValue, “/”, config.getCookieDomain(), cookieTimeout);

}

}

SaStorage是一个接口,set方法是把token存入request对象中。

关键是下面一段:

esponse.addCookie(getTokenName(), tokenValue, “/”, config.getCookieDomain(), cookieTimeout);

这句话利用cookie保存了当前登录用户的token。

谷歌浏览器查看cookie方式:右上角有三个点的按钮 - 设置

搜索localhost,找到satoken,这就是上面代码中getTokenName()方法的返回值

步骤 6 sa-token为什么能获取到response对象?SaHolder的秘密。。。(深挖,看不懂就跳过,没事)

===========================================================

之前我也一直想不通一个问题,sa-token用起来也太方便了吧,就这么一句话,什么都搞定了。我也不需要去关心session,也不要管HttpServletResponse啥的。

秘密就在这:

SaResponse response = SaHolder.getResponse();

SaHolder调用getResponse方法得到SaResponse, 这个SaResponse是一个接口

package cn.dev33.satoken.context.model;

public interface SaResponse {

public Object getSource();

public void deleteCookie(String name);

public void addCookie(String name, String value, String path, String domain, int timeout);

public SaResponse setHeader(String name, String value);

public default SaResponse setServer(String value) {

return this.setHeader(“Server”, value);

}

public Object redirect(String url);

}

我们目前只用了addCookie方法,然后再看SaResponse的实现类

只有一个实现类,addCookie方法如下:

@Override

public void addCookie(String name, String value, String path, String domain, int timeout) {

Cookie cookie = new Cookie(name, value);

if(SaFoxUtil.isEmpty(path) == true) {

path = “/”;

}

if(SaFoxUtil.isEmpty(domain) == false) {

cookie.setDomain(domain);

}

cookie.setPath(path);

cookie.setMaxAge(timeout);

response.addCookie(cookie);

}

和我们预期的是一致的。

现在的问题是,SaHolder究竟是怎么getResponse的?

代码如下:

public static SaResponse getResponse() {

return SaManager.getSaTokenContext().getResponse();

}

原来是saTokenContext的绝活,再看SaManager的getSaTokenContext方法:

public static SaTokenContext getSaTokenContext() {

if (saTokenContext == null) {

synchronized (SaManager.class) {

if (saTokenContext == null) {

setSaTokenContext(new SaTokenContextDefaultImpl());

}

}

}

return saTokenContext;

}

我以为秘密在 new SaTokenContextDefaultImpl() 里面。

SaTokenContextDefaultImpl是Sa-Token 上下文处理器 [默认实现类]。

结果一看代码,懵逼了:

敢情这是在嘲讽我吗,这估计是不正常的情况下才会走到这吧,肯定不是。那如果不是用的SaTokenContextDefaultImpl,难道saTokenContext本来就有值的?

saTokenContext是一个接口,有三个实现类:

因为这是SpringBoot项目怎么看也像是SaTokenContextForSpring

重新登录看看,发现

这个方法返回了SaResponseForServlet对象,属于SaResponse的实现类,所以在上面SaHolder的getResponse方法获取的实际是SaResponseForServlet对象。又是多态,多态好是好,封装了底层的实现。只给出接口类的简单操作,就是如果翻源码会有点麻烦,需要一层层地找。

SaResponseForServlet的实例化代码如下

那么关键就在于SpringMVCUtil是怎么getResponse的?

关键就在于RequestContextHolder了,它调用了getRequestAttributes方法,得到 servletRequestAttributes对象,查看数据发现

哦,到这一步就有点豁然开朗了,response的值是:[com.alibaba.druid.support.http.WebStatFilter$StatHttpServletResponseWrapper@3cca73f3](()

至于这个玩意又是什么东东,老实说,我目前还没有完全搞明白,目前只知道这个类的位置是在druid的jar包里面:

点开WebStatFilter,发现里面有个内部类StatHttpServletResponseWrapper,原来那个$是内部类的意思

public final static class StatHttpServletResponseWrapper extends HttpServletResponseWrapper implements HttpServletResponse {

//初始值应该设置为:HttpServletResponse.SC_OK,而不是 0。

private int status = HttpServletResponse.SC_OK;

public StatHttpServletResponseWrapper(HttpServletResponse response){

super(response);

}

public void setStatus(int statusCode) {

super.setStatus(statusCode);

this.status = statusCode;

}

@SuppressWarnings(“deprecation”)

public void setStatus(int statusCode, String statusMessage) {

super.setStatus(statusCode, statusMessage);

this.status = statusCode;

}

public void sendError(int statusCode, String statusMessage) throws IOException {

super.sendError(statusCode, statusMessage);

this.status = statusCode;

}

public void sendError(int statusCode) throws IOException {

super.sendError(statusCode);

this.status = statusCode;

}

public int getStatus() {

return status;

}

}

这个StatHttpServletResponseWrapper类继承了HttpServletResponseWrapper,而HttpServletResponseWrapper又继承了ServletResponseWrapper,ServletResponseWrapper实现了HttpServletResponse(嗯??见到 HttpServletResponse了,终于看到了老朋友,不容易)

因为这个项目使用了Druid数据源,所以肯定是某个时间点把这个类new出来了,因为多态的关系,不会影响其他功能,这个咱们先讲到这。

好了,回到SpringMVCUtil的getResponse方法:

public static HttpServletResponse getResponse() {

ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

if(servletRequestAttributes == null) {

throw new SaTokenException(“非Web上下文无法获取Response”);

}

return servletRequestAttributes.getResponse();

}

我知道你一定有很多的疑惑,比如RequestContextHolder是啥,怎么就getRequestAttributes了,servletRequestAttributes又是啥,凭什么就可以getResponse?

别着急,咱一个一个来。

首先是RequestContextHolder,它的getRequestAttributes实现如下:

注释说返回绑定当前线程的RequestAttributes(请求参数),用到了requestAttributesHolder

private static final ThreadLocal requestAttributesHolder =

new NamedThreadLocal(“Request attributes”);

这个requestAttributesHolder是一个ThreadLocal,是线程本地变量,并且设置了final和static。设置final是因为不希望被修改,设置static是为了方便其他地方也能调用它。

ThreadLocal是java.lang包下面的,已经和框架无关了。其get方法源码如下:

简单说一下,ThreadLocal是和当前线程相关的,具体原理我们就先不说了,等以后重新开一个章节单独。现在,你只需要知道,Spring框架的org.springframework.web.context.request帮我们完成了这个事情,他就是拿到response了。而sa-token框架是直接取用了Spring框架的Response。

这个Response是和当前线程相关的,每个用户访问Tomcat,走到Controller,service,dao再返回数据,这整个过程都是在一个线程里面,和其他用户的访问无关,这个叫做线程隔离。

咳咳,最后我们捋一捋:

讲了这么多,其实我们的问题就是SaHolder为什么能获取response对象,现在直接说结论,因为SaHolder调用了SaManager.getSaTokenContext(),得到了SaTokenContext才可以通过getResponse方法获取SaResponse,而SaTokenContext的真实身份其实是SaTokenContextForSpring,SaTokenContextForSpring重写了getResponse,就是在这个方法去调用Spring的Response。

我们理解到这一步已经足够了。

步骤 7 sa-token默认配置

=================

如果你不单独做配置,就采用默认配置,默认配置是写在SaTokenConfig中的。

private String tokenName = “satoken”;

private long timeout = 60 * 60 * 24 * 30;

private long activityTimeout = -1;

private Boolean isConcurrent = true;

private Boolean isShare = true;

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/855339.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号