微服务认证与授权Spring Cloud Security:SecurityContextPersistenceFilter详解
在前面两篇文章介绍了Spring Cloud Security 中的重要过滤器FilterSecurityInterceptor 和 ExceptionTranslationFilter
,今天接着介绍spring-security中的核心过滤器:SecurityContextPersistenceFilter。
SecurityContextPersistenceFilter
位于安全过滤器调用链的上游,主要负责两个重要的事宜,一是在请求开始时创建SecurityContext
并存储到SecurityContextHolder
,为接下来调用链中其他安全过滤器提供安全上下文;二是在请求结束时清理SecurityContextHolder
SecurityContextPersistenceFilter
的核心处理流程如下:
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) {
chain.doFilter(request, response);
return;
}
final boolean debug = logger.isDebugEnabled();
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
// 是否有session可用
if (forceEagerSessionCreation) {
HttpSession session = request.getSession();
}
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
response);
// 如果可能的话,从SecurityContextRepository中加载SecurityContext
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
try {
//请求开始时,设置SecurityContext,后续的SecurityContext可以直接从SecurityContextHolder中获取
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());
}finally {
// 请求结束的时候,从SecurityContextHolder中清理SecurityContext
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
}
}
SecurityContextPersistenceFilter
在请求开始时候,为请求注入安全上下文,用于接下来的安全处理任务;在请求结束的时候,清理SecurityContextHolder
中的安全上下文。
SecurityContext
的获取和保存都委托给了SecurityContextRepository
执行,它提供了以下的接口
public interface SecurityContextRepository {
// 加载安全上下文
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
// 保存安全上下文
void saveContext(SecurityContext context, HttpServletRequest request,
boolean containsContext(HttpServletRequest request);
}
SecurityContextRepository
的默认实现为HttpSessionSecurityContextRepository
,是通过HttpSession
来保存HttpSessionSecurityContextRepository
,其loadContext()
方法代码如下
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
HttpServletRequest request = requestResponseHolder.getRequest();
HttpServletResponse response = requestResponseHolder.getResponse();
HttpSession httpSession = request.getSession(false);
// 从session中获取SecurityContext
SecurityContext context = readSecurityContextFromSession(httpSession);
// 如果不存在创建一个新的空SecurityContext
if (context == null) {
context = generateNewContext();
}
...
return context;
}
在微服务的情况下,我们期望每一次的请求都是无状态,所以希望HTTP传输中不存在session,那么可以通过在WebSecurityConfigurerAdapter
中配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
// 不使用和创建任何的HttpSession
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
}
或者设置
HttpSessionSecurityContextRepository.setAllowSessionCreation(false)
禁止session的创建。这时SecurityContextPersistenceFilter
都会在请求开始时为每个请求创建新的SecurityContext
以及请求结束时不会将SecurityContext
保存在session中,达到访问无状态的要求。
甚至也可以配置直接用NullSecurityContextRepository
替换掉HttpSessionSecurityContextRepository
实现服务间无状态访问的目的。
- 点赞
- 收藏
- 关注作者
评论(0)