在springsecurity 中,我们一般可以通过代码:
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication auth = securityContext.getAuthentication();
获取当前登录人员信息,其实我们可以从SecurityContext 获取 springsecurity 实现的秘密。
就让我从SecurityContextHolder 一步步抽丝剥茧,解读一下SecurityContext 的来源。
这里我们可以通过查看SecurityContextHolder 源码,看看这个SecurityContext 到底 是哪里来的。
public static void clearContext() { strategy.clearContext(); } /** * Obtain the current <code>SecurityContext</code>. * * @return the security context (never <code>null</code>) */ public static SecurityContext getContext() { return strategy.getContext(); } public static void setContext(SecurityContext context) { strategy.setContext(context); }
代码很简单,我们可以看到他是通过 strategy 获取 的,那这个strategy 有是什么东西呢?
通过跟踪 源码发现 ,这个 strategy 实际是ThreadLocalSecurityContextHolderStrategy 的一个实例对象。
那这个ThreadLocalSecurityContextHolderStrategy 又是什么呢,我们继续看源码。
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { //~ Static fields/initializers ===================================================================================== private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<SecurityContext>(); //~ Methods ======================================================================================================== public void clearContext() { contextHolder.remove(); } public SecurityContext getContext() { SecurityContext ctx = contextHolder.get(); if (ctx == null) { ctx = createEmptyContext(); contextHolder.set(ctx); } return ctx; } public void setContext(SecurityContext context) { Assert.notNull(context, "Only non-null SecurityContext instances are permitted"); contextHolder.set(context); } public SecurityContext createEmptyContext() { return new SecurityContextImpl(); } }
不过就是一个放到一个线程变量中。看到这里我们需要知道 什么使用调用了这个setContext 方法。
通过跟踪源码发现:原来调用这个setContext方法的类:
SecurityContextPersistenceFilter
这是一个过滤器。我们知道 springsecurity 是有一组 过滤器 组成的,并且这个过滤器排在这些过滤器的第一个位置。
这个我们终于找到设置这个context的源头了。
通过阅读源码 ,我们看到这样两行代码.
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response); SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
我们看到了这个SecurityContext 的源头了。
我们找到了这个 SecurityContext 是通过这个SecurityContextRepository 类获取的。
我们找到了这个接口的实现 HttpSessionSecurityContextRepository。
public SecurityContextPersistenceFilter() { this(new HttpSessionSecurityContextRepository()); } public SecurityContextPersistenceFilter(SecurityContextRepository repo) { this.repo = repo; }
最终我们找到了HttpSessionSecurityContextRepository代码
private SecurityContext readSecurityContextFromSession(HttpSession httpSession) { final boolean debug = logger.isDebugEnabled(); if (httpSession == null) { if (debug) { logger.debug("No HttpSession currently exists"); } return null; } // Session exists, so try to obtain a context from it. Object contextFromSession = httpSession.getAttribute(springSecurityContextKey); if (contextFromSession == null) { if (debug) { logger.debug("HttpSession returned null object for SPRING_SECURITY_CONTEXT"); } return null; } // We now have the security context object from the session. if (!(contextFromSession instanceof SecurityContext)) { if (logger.isWarnEnabled()) { logger.warn(springSecurityContextKey + " did not contain a SecurityContext but contained: '" + contextFromSession + "'; are you improperly modifying the HttpSession directly " + "(you should always use SecurityContextHolder) or using the HttpSession attribute " + "reserved for this class?"); } return null; } if (debug) { logger.debug("Obtained a valid SecurityContext from " + springSecurityContextKey + ": '" + contextFromSession + "'"); } // Everything OK. The only non-null return from this method. return (SecurityContext) contextFromSession; }
这里我们可以看到 实际 这个SecurityContext 实际是从session 中获取的。
Object contextFromSession = httpSession.getAttribute("SPRING_SECURITY_CONTEXT");
从这里我们就知道了这个SecurityContext 来源了。
步骤是:
1.通过 SecurityContextPersistenceFilter 这个过滤器,从session 中获取SecurityContext .
2.并且把这个SecurityContext 放到线程变量中,然后我们在这个请求中就可以直接通过SecurityContextHolder.getContext();
获取SecurityContext 对象了。
解决了获取的问题,我们只需要把登录后的SecurityContext 放到httpsession 中就好了。
下面代码就是登录的代码实现:
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, pwd); authRequest.setDetails(new WebAuthenticationDetails(request)); SecurityContext securityContext = SecurityContextHolder.getContext(); Authentication auth = authenticationManager.authenticate(authRequest); securityContext.setAuthentication(auth); HttpSession session = request.getSession(); session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext);
通过上面的代码,我们就将securityContext 放到 session中了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人