前言

  本文主要整理一下SecurityContext的存储方式。

SecurityContext接口

顾名思义,安全上下文。即存储认证授权的相关信息,实际上就是存储"当前用户"账号信息和相关权限。这个接口只有两个方法,Authentication对象的getter、setter。

package org.springframework.security.core.context;

import java.io.Serializable;
import org.springframework.security.core.Authentication; public interface SecurityContext extends Serializable {
Authentication getAuthentication(); void setAuthentication(Authentication var1);
}

Authentication接口又是干嘛的?

注意:SecurityContext存储的Authentication对象是经过认证的,所以它会带有权限,它的getAuthorities()方法会返回相关权限。

package org.springframework.security.core;

import java.io.Serializable;
import java.security.Principal;
import java.util.Collection; public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities(); Object getCredentials(); Object getDetails(); Object getPrincipal(); boolean isAuthenticated(); void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

SecurityContextHolder工具类

前面说的"当前用户"实际上指的是当前这个请求所对应的用户,那么怎么知道当前用户是谁呢?由于一个请求从开始到结束都由一个线程处理,这个线程中途也不会去处理其他的请求。所以在这段时间内,相当于这个线程跟当前用户是一一对应的。SecurityContextHolder工具类就是把SecurityContext存储在当前线程中。

SecurityContextHolder可以用来设置和获取SecurityContext。它主要是给框架内部使用的,可以利用它获取当前用户的SecurityContext进行请求检查,和访问控制等。

在Web环境下,SecurityContextHolder是利用ThreadLocal来存储SecurityContext的。

请求结束,SecurityContext存储在哪里?

我们知道Sevlet中线程是被池化复用的,一旦处理完当前的请求,它可能马上就会被分配去处理其他的请求。而且也不能保证用户下次的请求会被分配到同一个线程。所以存在线程里面,请求一旦结束,就没了。如果没有保存,不是每次请求都要重新认证登录?想想看,如果没有权限框架我们是怎么处理的?

想到了吧,如果不用权限框架,我们一般是把认证结果存在Session中的。同理,它也把认证结果存储到Session了。

对应的Key是:"SPRING_SECURITY_CONTEXT"

流程视图

SecurityContextPersistenceFilter拦截器

SecurityContextPersistenceFilter是Security的拦截器,而且是拦截链中的第一个拦截器,请求来临时它会从HttpSession中把SecurityContext取出来,然后放入SecurityContextHolder。在所有拦截器都处理完成后,再把SecurityContext存入HttpSession,并清除SecurityContextHolder内的引用。

注:其中repo对象是HttpSessionSecurityContextRepository

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res; if (request.getAttribute(FILTER_APPLIED) != null) {
// ensure that filter is only applied once per request
chain.doFilter(request, response);
return;
} final boolean debug = logger.isDebugEnabled(); request.setAttribute(FILTER_APPLIED, Boolean.TRUE); if (forceEagerSessionCreation) {
HttpSession session = request.getSession(); if (debug && session.isNew()) {
logger.debug("Eagerly created session: " + session.getId());
}
} HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
response);
//利用HttpSecurityContextRepository从HttpSesion中获取SecurityContext对象
//如果没有HttpSession,即浏览器第一次访问服务器,还没有产生会话。
//它会创建一个空的SecurityContext对象
SecurityContext contextBeforeChainExecution = repo.loadContext(holder); try {
//把SecurityContext放入到SecurityContextHolder中
SecurityContextHolder.setContext(contextBeforeChainExecution);
//执行拦截链,这个链会逐层向下执行
chain.doFilter(holder.getRequest(), holder.getResponse()); }
finally {
//当拦截器都执行完的时候把当前线程对应的SecurityContext从SecurityContextHolder中取出来
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
// Crucial removal of SecurityContextHolder contents - do this before anything
// else.
SecurityContextHolder.clearContext();
//利用HttpSecurityContextRepository把SecurityContext写入HttpSession
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
request.removeAttribute(FILTER_APPLIED); if (debug) {
logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}
}

Tomcat建立会话的流程

  有人可能对Tomcat建立会话的流程还不熟悉,这里稍微整理一下。是这样的,当客户浏览器打开后第一次访问Tomcat服务器,Tomcat会创建一个HttpSesion对象,存入一个ConcurrentHashMap,Key是SessionId,Value就是HttpSession。然后请求完成后,在返回的报文中添加Set-Cookie:JSESSIONID=xxx,然后客户端浏览器会保存这个Cookie。当浏览器再次访问这个服务器的时候,都会带上这个Cookie。Tomcat接收到这个请求后,根据JSESSIONID把对应的HttpSession对象取出来,放入HttpSerlvetRequest对象里面。

重点:

1.HttpSession会一直存在服务端,实际上是存在运行内存中。除非Session过期 OR Tomcat奔溃 OR 服务器奔溃,否则会话信息不会消失。

2.如无特殊处理,Cookie JSESSIONID会在浏览器关闭的时候清除。

3.Tomcat中HttpSesion的默认过期时间为30分钟。

4.这些处理都在Security的拦截链之前完成。

——————————————————2019年1月21日更正——————————————

浏览器访问Web项目并不会创建Session,只有在编程调用request.getSession()方法后Session才会建立,浏览器才会有JSESSIONID的Cookie。

一般是认证成功后,调用getSession()(其实在这之前Session并不存在),获得Session对象,然后把信息存里面。

所以,一般的,在登录之前,你不管怎么访问网站,都不会建立会话。

【详解】Spring Security 之 SecurityContext的更多相关文章

  1. 详解Spring Security的HttpBasic登录验证模式

    一.HttpBasic模式的应用场景 HttpBasic登录验证模式是Spring Security实现登录验证最简单的一种方式,也可以说是最简陋的一种方式.它的目的并不是保障登录验证的绝对安全,而是 ...

  2. 详解Spring Security的formLogin登录认证模式

    一.formLogin的应用场景 在本专栏之前的文章中,已经给大家介绍过Spring Security的HttpBasic模式,该模式比较简单,只是进行了通过携带Http的Header进行简单的登录验 ...

  3. applicationContext.xml详解 spring+mybatis+struts

    今天给大家详细解释一项关于Spring的applicationContext.xml文件,这对于初学者来说,应该是很有帮助的, 以下是详解Spring的applicationContext.xml文件 ...

  4. spring原理案例-基本项目搭建 02 spring jar包详解 spring jar包的用途

    Spring4 Jar包详解 SpringJava Spring AOP: Spring的面向切面编程,提供AOP(面向切面编程)的实现 Spring Aspects: Spring提供的对Aspec ...

  5. 【转】详解spring 每个jar的作用

    spring.jar 是包含有完整发布模块的单个jar 包.但是不包括mock.jar, aspects.jar, spring-portlet.jar, and spring-hibernate2. ...

  6. 用IDEA详解Spring中的IoC和DI(挺透彻的,点进来看看吧)

    用IDEA详解Spring中的IoC和DI 一.Spring IoC的基本概念 控制反转(IoC)是一个比较抽象的概念,它主要用来消减计算机程序的耦合问题,是Spring框架的核心.依赖注入(DI)是 ...

  7. 使用IDEA详解Spring中依赖注入的类型(上)

    使用IDEA详解Spring中依赖注入的类型(上) 在Spring中实现IoC容器的方法是依赖注入,依赖注入的作用是在使用Spring框架创建对象时动态地将其所依赖的对象(例如属性值)注入Bean组件 ...

  8. 详解Spring中Bean的作用域与生命周期

    摘要:在利用Spring进行IOC配置时,关于bean的配置和使用一直都是比较重要的一部分,同时如何合理的使用和创建bean对象,也是小伙伴们在学习和使用Spring时需要注意的部分,所以这一篇文章我 ...

  9. 10分钟详解Spring全家桶7大知识点

    Spring框架自2002年诞生以来一直备受开发者青睐,它包括SpringMVC.SpringBoot.Spring Cloud.Spring Cloud Dataflow等解决方案.有人亲切的称之为 ...

  10. [转载] 多图详解Spring框架的设计理念与设计模式

    转载自http://developer.51cto.com/art/201006/205212_all.htm Spring作为现在最优秀的框架之一,已被广泛的使用,51CTO也曾经针对Spring框 ...

随机推荐

  1. Java Token的原理和生成使用机制

    在此之前我们先了解一下什么是Cookie.Session.Token 1.什么是Cookie? cookie指的就是浏览器里面能永久存储数据的一种数据存储功能.cookie由服务器生成,发送给浏览器, ...

  2. (转)android import library switch语句报错case expressions must be constant expressions

    今天当我从github上下载一个工程,并把它的库文件导入eclipse中,发现switch语句报错case expressions must be constant expressions : 解决方 ...

  3. 网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP (转)

    网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP 27 March 2013 TUN 设备 TUN 设备是一种虚拟网络设备,通过此设备,程序可以方便得模拟网络行为.先来看看物理设 ...

  4. service 设计问题

    今天写了一段让自己尴尬的代码,就是在一个方法中调用了两个 service 方法,而我为每个service 都定义了 事物回滚. 然后郁闷了,我执行请求调用该方法, 发现第二个service方法执行失败 ...

  5. 初级PM要做什么

    首先让我们看下这张图,产品经理进入公司后将要面临着许多工作 或许你有疑问,如果是产品助理的话,上面这么多工作都要去做吗? 其实不然,初级产品经理由于工作经历有限,对行业的研究以及对市场的把控是有视野限 ...

  6. Java8特性之Lambda、方法引用和Streams

    这里涉及三个重要特性: Lambda 方法引用 Streams ① Lambda 最早了解Lambda是在C#中,而从Java8开始,Lambda也成为了新的特性,而这个新的特性的目的,就是为了消除单 ...

  7. 编译搭建lnmp+zabbix

    搭建nginx 1)基础依赖包安装 yum -y install gcc gcc-c++ vim tree make cmake autoconf yum -y install openssl ope ...

  8. Elasticsearch下安装ik分词器

    安装ik分词器(必须安装maven) 上传相应jar包 解压到相应目录 unzip elasticsearch-analysis-ik-master.zip(zip包) cp -r elasticse ...

  9. 《Linux就该这么学》第八天课程

        当一个人的心中,有着更高的山峰想要去攀登时,他就不会在意脚下的泥沼. 今天发一下干货,常用命令的一些总结,今天的理论知识比较多. 原创地址:https://www.linuxprobe.com ...

  10. qhfl-4 注册-登录-认证

    认证 任何的项目都需要认证,当用户输入了用户名和密码,验证通过,代表用户登录成功 那HTTP请求是无状态的,下次这个用户再请求,是不可能识别这个用户是否登录的 就要有自己的方式来实现这个认证,用户登录 ...