通过观察Filter的名字,就能大概猜出来这个过滤器的作用,是的,持久化SecurityContext实例。这个过滤器位置是;
org.springframework.security.web.context.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) {
- // ensure that filter is only applied once per request
- chain.doFilter(request, response);
- return;
- }
- final boolean debug = logger.isDebugEnabled();
- request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
- if (forceEagerSessionCreation) {
- HttpSession session = request.getSession();
- if (debug && session.isNew()) {
- logger.debug(“Eagerly created session: “ + session.getId());
- }
- }
- //将request、response对象交给HttpRequestResponseHolder维持
- HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
- //通过SecurityContextRepository接口的实现类装载SecurityContext实例
- //HttpSessionSecurityContextRepository将产生SecurityContext实例的任务交给SecurityContextHolder.createEmptyContext()完成
- //SecurityContextHolder再根据策略模式的不同,
- //把任务再交给相应策略类完成SecurityContext的创建
- //如果没有配置策略名称,则默认为
- //ThreadLocalSecurityContextHolderStrategy,
- //该类直接通过new SecurityContextImpl()创建实例
- SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
- try {
- //将产生的SecurityContext再通过SecurityContextHolder->
- //ThreadLocalSecurityContextHolderStrategy设置到ThreadLocal中
- SecurityContextHolder.setContext(contextBeforeChainExecution);
- //继续把请求流向下一个过滤器执行
- chain.doFilter(holder.getRequest(), holder.getResponse());
- } finally {
- //先从SecurityContextHolder获取SecurityContext实例
- SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
- // Crucial removal of SecurityContextHolder contents – do this before anything else.
- //再把SecurityContext实例从SecurityContextHolder中清空
- SecurityContextHolder.clearContext();
- //将SecurityContext实例持久化到session中
- repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
- request.removeAttribute(FILTER_APPLIED);
- if (debug) {
- logger.debug(“SecurityContextHolder now cleared, as request processing completed”);
- }
- }
- }
通过源码中的注释,应该可以看出来,这个Filter的作用主要是创建一个空的SecurityContext(如果session中没有SecurityContext实例),然后持久化到session中。
接下来看看repo.loadContext(holder);代码:
- 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,新建一个空的SecurityContext实例
- if (context == null) {
- if (logger.isDebugEnabled()) {
- logger.debug(“No SecurityContext was available from the HttpSession: “ + httpSession +“. “ +
- “A new one will be created.”);
- }
- context = generateNewContext();
- }
- //这里需要注意一下,response装饰器类重新包装了response
- requestResponseHolder.setResponse(new SaveToSessionResponseWrapper(response, request,
- httpSession != null, context.hashCode()));
- return context;
- }
进一步分析generateNewContext方法
- SecurityContext generateNewContext() {
- SecurityContext context = null;
- //创建SecurityContext实例并返回
- if (securityContextClass == null) {
- context = SecurityContextHolder.createEmptyContext();
- return context;
- }
- try {
- context = securityContextClass.newInstance();
- } catch (Exception e) {
- ReflectionUtils.handleReflectionException(e);
- }
- return context;
- }
实际上,SecurityContextHolder类也是把创建SecurityContext任务交给具体的SecurityContextHolderStrategy实现类处理,SecurityContextHolder类有一个静态初始化过程
- static {
- initialize();
- }
- private static void initialize() {
- if ((strategyName == null) || “”.equals(strategyName)) {
- // Set default
- strategyName = MODE_THREADLOCAL;
- }
- //默认的SecurityContextHolderStrategy实现类为
- //ThreadLocalSecurityContextHolderStrategy
- if (strategyName.equals(MODE_THREADLOCAL)) {
- strategy = new ThreadLocalSecurityContextHolderStrategy();
- } else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
- strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
- } else if (strategyName.equals(MODE_GLOBAL)) {
- strategy = new GlobalSecurityContextHolderStrategy();
- } else {
- // Try to load a custom strategy
- try {
- Class<?> clazz = Class.forName(strategyName);
- Constructor<?> customStrategy = clazz.getConstructor();
- strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
- } catch (Exception ex) {
- ReflectionUtils.handleReflectionException(ex);
- }
- }
- initializeCount++;
- }
现在来看ThreadLocalSecurityContextHolderStrategy源码
- final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
- //~ Static fields/initializers =====================================================================================
- private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<SecurityContext>();
- //~ Methods ========================================================================================================
- public void clearContext() {
- contextHolder.set(null);
- }
- 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);
- }
- //直接new一个SecurityContextImpl对象,
- //SecurityContextImpl类实现SecurityContext接口
- public SecurityContext createEmptyContext() {
- return new SecurityContextImpl();
- }
- }
分析到这里,整个过程也清楚了。不过在filter原路返回时,还需要保存这个SecurityContext实例到session中,并且通过SecurityContextHolder将ThreadLocalSecurityContextHolderStrategy中ThreadLocal维持的SecurityContext实例清空。
- //将SecurityContext实例持久化到session中
- repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
- public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
- //由于之前response装饰器类SaveToSessionResponseWrapper
- //重新装饰了response
- SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = (SaveContextOnUpdateOrErrorResponseWrapper)response;
- // saveContext() might already be called by the response wrapper
- // if something in the chain called sendError() or sendRedirect(). This ensures we only call it
- // once per request.
- if (!responseWrapper.isContextSaved() ) {
- //SaveToSessionResponseWrapper保存SecurityContext实例
- responseWrapper.saveContext(context);
- }
- }
SaveToSessionResponseWrapper的saveContext方法源码:
- protected void saveContext(SecurityContext context) {
- // See SEC-776
- if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) {
- if (logger.isDebugEnabled()) {
- logger.debug(“SecurityContext contents are anonymous – context will not be stored in HttpSession. “);
- }
- return;
- }
- HttpSession httpSession = request.getSession(false);
- if (httpSession == null) {
- httpSession = createNewSessionIfAllowed(context);
- }
- // If HttpSession exists, store current SecurityContextHolder contents but only if
- // the SecurityContext has actually changed (see JIRA SEC-37)
- if (httpSession != null && context.hashCode() != contextHashBeforeChainExecution) {
- //保存SecurityContext到session中
- httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
- if (logger.isDebugEnabled()) {
- logger.debug(“SecurityContext stored to HttpSession: ‘” + context + “‘”);
- }
- }
- }