SecurityContextPersistenceFilter 在过滤器链完成后清除 SecurityContextHolder

SecurityContextPersistenceFilter clears SecurityContextHolder after filter chain is finished

我目前正在学习 Spring 安全基础知识。我正在阅读文档和源代码,因为我希望获得尽可能多的关于引擎盖下发生的事情的详细信息。除了一件事,一切都有些可以理解:

SecurityContextPersistenceFilter 过滤器 class 是 Spring 安全性的第一个过滤器,根据 this page class 的 doFilter() 方法的实现看起来像这样:

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
    if (request.getAttribute("__spring_security_scpf_applied") != null) {
        chain.doFilter(request, response);
    } else {
        request.setAttribute("__spring_security_scpf_applied", Boolean.TRUE);
        if (this.forceEagerSessionCreation) {
            HttpSession session = request.getSession();
            if (this.logger.isDebugEnabled() && session.isNew()) {
                this.logger.debug(LogMessage.format("Created session %s eagerly", session.getId()));
            }
        }

        HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
        SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
        boolean var10 = false;

        try {
            var10 = true;
            SecurityContextHolder.setContext(contextBeforeChainExecution);
            if (contextBeforeChainExecution.getAuthentication() == null) {
                this.logger.debug("Set SecurityContextHolder to empty SecurityContext");
            } else if (this.logger.isDebugEnabled()) {
                this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", contextBeforeChainExecution));
            }

            chain.doFilter(holder.getRequest(), holder.getResponse());
            var10 = false;
        } finally {
            if (var10) {
                SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
                SecurityContextHolder.clearContext();
                this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
                request.removeAttribute("__spring_security_scpf_applied");
                this.logger.debug("Cleared SecurityContextHolder to complete request");
            }
        }

        SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
        SecurityContextHolder.clearContext();
        this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
        request.removeAttribute("__spring_security_scpf_applied");
        this.logger.debug("Cleared SecurityContextHolder to complete request");
    }
}

我的问题是:显然,在过滤器链完成其工作后,SecurityContextHolder.clearContext() 方法要么在 finally 块中调用,要么在方法结束时调用。但是我知道,我仍然可以在我的控制器和服务层中使用 SecurityContextHolder.getContext() 访问 SecurityContext 对象。如果上面的代码清除了 SecurityContextHolder,那怎么可能呢?我错过了什么吗?

首先,您所指的文档是 spring security 的 4.1.2 版,该版本于 2016 年 12 月 21 日发布。Spring security 当前为 5.6.1,the filter list looks a lot different in the current documentation.

那么上面代码中你的问题的答案是,因为这一行

this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());

SecurityContextHolder 是一个单例,所以 SecurityContext 被获取(这将创建一个副本)然后在 singleton 中被清除,最后 this.repo.save 保存下一次的上下文。

这是如何保存的,是不同的,默认情况下相信它使用 HttpSession(这意味着你将获得一个会话 cookie),它在 HttpSessionSecurityContextRepository 中的 Spring 中实现,但你可以设置一个单独的数据库,如果您愿意,可以永久保存它,这样它在重新启动等后仍然存在。