一、结论

先给出结论,是因为本身是很简单的道理。假设我们没有使用任何认证授权的框架,就简单的使用Cookie和HttpSession,那么用户登录后的每一个请求是如何关联上这个用户的呢?答案很简单,由于每个请求Tomcat使用一个单独线程来处理,但是Http请求时是有cookie的,那么一般来说是在cookie中加入sessionId,后台服务根据sessionId去查找HttpSession,这样就可以关联起来了。这本是很基础的内容,为什么在使用Shiro的时候还有这个疑问呢?

二、缘由

起初我的认知:

  1. shiro为每一个用户创建了一个Subject(这个实际并不是每一个用户,只是之前是这样认为的),这个Subject是使用ThreadLocal绑定的。
  2. shiro的退出是直接使用的subject.logout()方法,也可以通过subject获取session、token等认证授权信息。

以上两点是我之前对shiro有的认知,所以我以为一个用户登录之后会有一个Subject。因此我就发现这里就有一个疑问的地方,如果一个用户一个Subject,那Subject又是和线程绑定的,用户每一个请求都是一个单独的线程,那么用户的请求是如何与登录请求关联上获取到同一个Subject的呢?

三、关键点分析

这里就直接开始的跟踪源码分析,首先我想的是查看获取Subject相关的源码,跟踪到ThreadContext类中关键代码,两部分

public static Subject getSubject() {
return (Subject) get(SUBJECT_KEY);
} public static Object get(Object key) {
if (log.isTraceEnabled()) {
String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
log.trace(msg);
} Object value = getValue(key);
if ((value != null) && log.isTraceEnabled()) {
String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
log.trace(msg);
}
return value;
} //这里是最终获取到Subject的地方,resource为InheritableThreadLocalMap对象
private static Object getValue(Object key) {
return resources.get().get(key);
}

另外一个地方

private static final Logger log = LoggerFactory.getLogger(ThreadContext.class);

    public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY";
public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY"; private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();

可以看到,Subject确实是从InheritableThreadLocalMap对象中取出来的。但是为什么是InheritableThreadLocalMap,这样的话,子线程是哪里产生的?

然后从shiro里层的过滤器开始跟踪代码,发现在AbstractShiroFilterdoFilterInternal方法中有关键代码:

try {
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
//关键部分1
final Subject subject = createSubject(request, response); //noinspection unchecked
//关键部分2
subject.execute(new Callable() {
public Object call() throws Exception {
updateSessionLastAccessTime(request, response);
executeChain(request, response, chain);
return null;
}
});
} catch (ExecutionException ex) {
t = ex.getCause();
} catch (Throwable throwable) {
t = throwable;
}

在这里可以知道了,这里是另起线程来处理之后的事情。并且subject是每一次请求都会创建一个,那么请求之间的subject是如何关联起来的呢?跟踪createSubject方法,找到关键代码DefaultSecurityManagercreateSubject方法

public Subject createSubject(SubjectContext subjectContext) {
//create a copy so we don't modify the argument's backing map:
SubjectContext context = copy(subjectContext); //ensure that the context has a SecurityManager instance, and if not, add one:
context = ensureSecurityManager(context); //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before
//sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the
//process is often environment specific - better to shield the SF from these details:
context = resolveSession(context); //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first
//if possible before handing off to the SubjectFactory:
context = resolvePrincipals(context); Subject subject = doCreateSubject(context); //save this subject for future reference if necessary:
//(this is needed here in case rememberMe principals were resolved and they need to be stored in the
//session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation).
//Added in 1.2:
save(subject); return subject;
}

在这个代码之前还有一些处理,主要的包括,将Request和Response对象放入之前的InheritableThreadLocalMap对象中。这里的代码主要是将SecurityManage实例、Session对象以及登录之后的认证信息Principals存入SubjectContext。然后在doCreateSubject方法中将这些内容都赋值给Subject。这样,虽然每一次请求都是一个新的Subject,但是subject里面的内容都是一致的。最后在subject.logout()方法中,删除掉session即可实现退出功能。

参考文章:https://blog.zlf.me/Shiro-web线程绑定解惑.html

关于Shiro的退出请求是如何关联到登录请求的思考的更多相关文章

  1. Shiro 整合SpringMVC 并实现权限管理,登录和注销

    Shiro 整合SpringMVC 并且实现权限管理,登录和注销 Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring S ...

  2. 转:PHP中的使用curl发送请求(GET请求和POST请求)

    原文地址:http://www.jb51.net/article/104974.htm 使用CURL发送请求的基本流程 使用CURL的PHP扩展完成一个HTTP请求的发送一般有以下几个步骤: 1.初始 ...

  3. Http请求详解(转)----请求+响应各字段详解

    参考HTTP深入浅出http请求(转)-----http请求的过程和实现机制 1. HTTP请求格式 首先介绍HTTP协议:超文本传输协议(HTTP,HyperText Transfer Protoc ...

  4. 使用Shiro+JWT完成的微信小程序的登录(含讲解)

    使用Shiro+JWT完成的微信小程序的登录 源码地址https://github.com/Jirath-Liu/shiro-jwt-wx 微信小程序用户登陆,完整流程可参考下面官方地址,本例中是按此 ...

  5. 为什么axios请求接口会发起两次请求

    之前在使用axios发现每次调用接口都会有两个请求,第一个请求时option请求,而且看不到请求参数,当时也没注意,只当做是做了一次预请求,判断接口是否通畅,但是最近发现并不是那么回事. 首先我们知道 ...

  6. JMeter 将上一个请求的结果作为下一个请求的参数——使用正则提取器(转载)

    在接口测试和压力测试过程中,经常会将几个流程串联起来才能测试.如:我要进行获取用户信息接口测试,我就要先登录成功后,才能获取用户信息.所以,我就要首先要登录,获得我的登录凭证(tokenId或tick ...

  7. jmeter 如何将上一个请求的结果作为下一个请求的参数——使用正则提取器

    1.简介 Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试,它最初被设计用于Web应用测试但后来扩展到其他测试领域. 它可以用于测试静态和动态资源例如 ...

  8. 使用https协议解决掉顽固不化的已解密的登录请求

    1.1 已解密的登录请求概述 在应用程序测试过程中,检测到将未加密的登录请求发送到服务器.由于登录过程所用的部分输入字段(例如:用户名.密码.电子邮件地址.社会保险号码,等等)是个人敏感信息,建议通过 ...

  9. 无法打开登录所请求的数据库 "ASPState"。登录失败。 用户 'NT AUTHORITY/SYSTEM' 登录失败。

    原文:无法打开登录所请求的数据库 "ASPState".登录失败. 用户 'NT AUTHORITY/SYSTEM' 登录失败. 无法打开登录 'ASPState' 中请求的数据库 ...

随机推荐

  1. mvc中使用remote属性来做ajax验证

    mvc中使用remote属性来做ajax验证比較easy : [Remote("Action", "Controller", AdditionalFields ...

  2. bzoj1193: [HNOI2006]马步距离(贪心+bfs)

    1193: [HNOI2006]马步距离 题目:传送门 题解: 毒瘤题... 模拟赛时的一道题,刚开始以为是一道大难题...一直在拼命找规律 结果.... 还是说正解吧: 暴力的解法肯定是直接bfs, ...

  3. rest_framework-节流-总结完结篇

    列表从后往前读 #1.在request中获取IP#2.访问记录 VISIT_RECORD = {} 放缓存 数据库 都可以 建议缓存import timeclass VisitThrottle(obj ...

  4. springboot shiro配置

    导入相关包(这里配合使用Ehcache缓存) <dependency> <groupId>org.apache.shiro</groupId> <artifa ...

  5. Deleting elements

    There are several ways to delete elements from a list. If you know the index of the element you want ...

  6. 8.queue

    #include <iostream> #include <stack> #include <algorithm> #include <list> #i ...

  7. svn回到某个历史版本的做法

    作者:朱金灿 来源:http://blog.csdn.net/clever101 一.在选中的文件上弹出svn的右键菜单,单击"Updateto revision"菜单项,如下图: ...

  8. c# static 常量

    1 关键字 static 修饰 类  字段 属性 方法 ,标记static的就不用创建类的实例调用了,直接通过类名点出来 2 用于变量前,表示每次重新使用该变量所在的方法,类或者自定义的类时,变量的值 ...

  9. JAVA-截取字符串两边指定字符

    工具类: /** * 工具类 */ public class Tool { /** * 截取两边指定的字符 * @param character * @param symbol * @return * ...

  10. HDU-2050 折线分割平面 找规律&递推

    题目链接:https://cn.vjudge.net/problem/HDU-2050 题意 算了吧,中文题不解释了 我们看到过很多直线分割平面的题目,今天的这个题目稍微有些变化,我们要求的是n条折线 ...