zoukankan      html  css  js  c++  java
  • springsecurity 源码解读之 SecurityContext

    在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中了。

  • 相关阅读:
    [转] 献给所有正在找路的人
    在同一表单内,多个提交按钮的处理方式
    javascript高级选择器querySelector和querySelectorAll
    一位年轻女董事长的37条忠告很受启发吧?
    函数的延迟加载
    WCF的CommunicationObjectFaultedException异常问题
    WCF Test Client对象数组输入问题
    [转载]C#开发Winform记录用户登录状态的方法
    using(C#)
    使用 SCTP 优化网络
  • 原文地址:https://www.cnblogs.com/yg_zhang/p/10651990.html
Copyright © 2011-2022 走看看