微服务认证与授权Spring Cloud Security:SecurityContextPersistenceFilter详解

举报
chongz-z 发表于 2021/01/18 12:05:56 2021/01/18
【摘要】 在前面两篇文章介绍了Spring Cloud Security 中的重要过滤器FilterSecurityInterceptor 和 ExceptionTranslationFilter,今天接着介绍spring-security中的核心过滤器:SecurityContextPersistenceFilter。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实现服务间无状态访问的目的。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。