前言
在《Spring Security之认证过滤器》中,我们知道认证信息在用户登录成功后,会通过SecurityContextRepository保存。而在《Spring Security之Session管理》中,我们知道SessionManagementConfigurer会创建他。但上面两篇文章都没有仔细说,今天作为主角,咱就好好说道说道,并聊聊与之相关的SecurityContextHolder。
SecurityContextRepository
从名字,我们就知道负责维护SecurityContext。先来看看他的定义:
public interface SecurityContextRepository {
/**
* 保存安全上下文
*/
void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response);
/**
* 从仓库中查询当前请求是否存在上下文
*/
boolean containsContext(HttpServletRequest request);
/**
* 从提供的请求中获取SecurityContext。对于未认证的用户,应返回空SecurityContext,而不是null。
* HttpRequestResponseHolder参数的作用是允许实现者返回二次封装的request和response,以便访问特定的request或者response(也可以都返回)。
* 当最后一次调用的时候,会显式保存SecurityContext,从holder获取的值将被传到过滤器链条和saveContext方法中。
* 实现类可能希望返回-当发生错误或者重定向时,确保上下文已经保存的-SaveContextOnUpdateOrErrorResponseWrapper的子类作为response对象。
* 实现类可能允许传入原始的request的response来实现显式保存。
* @deprecated 请使用#loadDeferredContext(HttpServletRequest)方法代替.
*/
@Deprecated
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
/**
* 延迟从HttpServletRequest加载SecurityContext,在需要的时候才加载。
* 很明显这个方法是前者的代替者。
*/
default DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
Supplier<SecurityContext> supplier = () -> loadContext(new HttpRequestResponseHolder(request, null));
return new SupplierDeferredSecurityContext(SingletonSupplier.of(supplier),
SecurityContextHolder.getContextHolderStrategy());
}
}
前面聊session的时候,我们也说过,这个组件有两个实现,一个负责stateless应用,另一个负责stateful应用。我们重点看看后者。
为什么使用Session存储认证信息
我们知道Session也就是会话,会话可以用于维护用户当前一系列操作请求的状态。而我们的认证信息,就是其中的状态之一。他表示用户已经登录,并且是哪个账号在访问系统。但是很显然的是,我们这些信息只能是一个时间段内的,因为我们的账号可能会退出登录态,也就是登出。因此,虽然我们可以在后台数据库中维持这一状态,但是随着用户的退出,我们保存在数据库中的状态,也就不存在任何意义了,浪费空间。
使用Session来存储这一信息,正好符合诉求。需要使用时,直接读取,不用时(退出登录态)自动清除,腾出空间。
但值得注意的是,使用Session需要留意一下,在登录后,通常我们都会重建Session,这可能会导致一些问题。
HttpSessionSecurityContextRepository
/**
* SecurityContextRepository的一个实现,将安全上下文保存在HttpSession中。
* 默认情况下,会通过loadContext方法(key: #SPRING_SECURITY_CONTEXT_KEY)从HttpSession中查询获取SecurityContext。
* 如果不能从HttpSession中获取到一个有效的SecurityContext,则不管是什么原因,都会调用新的SecurityContextHolder#createEmptyContext()方法创建一个新的SecurityContext,并返回该实例。
*
* 当saveContext方法被调用时,上下文将使用同样的key进行保存,为如下场景提供服务:
* <ol>
* <li>值变更</li>
* <li>配置好的AuthenticationTrustResolver不认为该内容代表一个匿名用户</li>
* </ol>
*
* 在标准配置中,即便是真的不存在HttpSession,loadContext方法也不会创建新的HttpSession。当saveContext在web请求即将结束被调用,只有在传入的SecurityContext不是一个空的SecurityContext实例时,才会创建一个新的HttpSession。这能够避免不必要的HttpSession的创建,但会自动存储请求期间对上下文所做的变更。
* 注意:如果SecurityContextPersistenceFilter配置了提前HttpSession,那么session最小化的逻辑将不再生效。
* 如果你使用急切的session创建方式,那么你应该确保这个类的allowSessionCreation属性设置为true(默认)。
* <p>
* 如果由于某些原因(例如,使用Basic认证方式或者多个类似的客户端永远不会出现相同的jsessionid的情况)导致HttpSession没有被创建,那么应当通过#setAllowSessionCreation(boolean)将allowSessionCreation属性设置为false。
* 只有你真正需要节省服务器内存时,并确保所有使用SecurityContextHolder的类,都被设计成:在不同的web请求之间都不需要持久化的SecurityContext时,你才能这样做。
* @since 3.0
*/
public class HttpSessionSecurityContextRepository implements SecurityContextRepository

最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



