SecurityUtils.getSubject()是怎么获取到当前用户信息的?

项目中我们习惯直接使用SecurityUtils.getSubject() 获取当前登录用户的信息

	/**
	 * 获取当前登录者对象
	 */
	public static Principal getPrincipal(){
		try{
			Subject subject = SecurityUtils.getSubject();
			Principal principal = (Principal)subject.getPrincipal();
			if (principal != null){
				return principal;
			}
//			subject.logout();
		}catch (UnavailableSecurityManagerException e) {
			
		}catch (InvalidSessionException e){
			
		}
		return null;
	}

 

 翻看代码,我们很容易看到这里使用了ThreadContext模式,直接从当前线程里拿subject,但是我们知道tomcat为每个请求都会分配一个线程,那这是如何获取当前登录用户信息的呢?

这个问题的答案要从shiro请求的拦截器开始寻找

我们都知道在web.xml中配置的拦截器通过name与spring中配置的实现bean相对应  

  <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

 

	<!-- 安全认证过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" /><!-- 
		<property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> -->
		<property name="loginUrl" value="${adminPath}/login" />
		<property name="successUrl" value="${adminPath}?login" />
		<property name="filters">
            <map>
                <entry key="cas" value-ref="casFilter"/>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
                <entry key="user" value-ref="userFilter"/>
            </map>
        </property>
		<property name="filterChainDefinitions">
			<ref bean="shiroFilterChainDefinitions"/>
		</property>
	</bean>

 通过ShiroFilterFactoryBean的getObject,返回SpringShiroFilter对象注入到spring中

 通过以下源码我们看到这里主要的处理就是获取SecurityManager、初始化过滤器链,这里我们不在详述

  protected AbstractShiroFilter createInstance() throws Exception {

        log.debug("Creating Shiro Filter instance.");

        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            String msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        }

        if (!(securityManager instanceof WebSecurityManager)) {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        }

        FilterChainManager manager = createFilterChainManager();

        //Expose the constructed FilterChainManager by first wrapping it in a
        // FilterChainResolver implementation. The AbstractShiroFilter implementations
        // do not know about FilterChainManagers - only resolvers:
        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);

        //Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
        //FilterChainResolver.  It doesn't matter that the instance is an anonymous inner class
        //here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
        //injection of the SecurityManager and FilterChainResolver:
        return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }

 

 我们分析SpringShiroFilter的父类AbstractShiroFilter,每个请求都会经过这个处理

 方法doFilterInternal如下

protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
            throws ServletException, IOException {

        Throwable t = null;

        try {
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

            final Subject subject = createSubject(request, response);

            //noinspection unchecked
            subject.execute(new Callable() {
                public Object call() throws Exception {
                    updateSessionLastAccessTime(request, response);
                    executeChain(request, response, chain);
                    return null;
                }
            });
        } catch (ExecutionException ex) {
            t = ex.getCause();
        } catch (Throwable throwable) {
            t = throwable;
        }

        if (t != null) {
            if (t instanceof ServletException) {
                throw (ServletException) t;
            }
            if (t instanceof IOException) {
                throw (IOException) t;
            }
            //otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one:
            String msg = "Filtered request failed.";
            throw new ServletException(msg, t);
        }
    }

其中updateSessionLastAccessTime维持session有效,executeChain去执行匹配的过滤器。

在这之前createSubject先创建了Subject,再通过execute绑定到线程

 跟到代码,我们可以看到绑定执行的代码块

org.apache.shiro.subject.support.SubjectThreadState中

    public void bind() {
        SecurityManager securityManager = this.securityManager;
        if ( securityManager == null ) {
            //try just in case the constructor didn't find one at the time:
            securityManager = ThreadContext.getSecurityManager();
        }
        this.originalResources = ThreadContext.getResources();
        ThreadContext.remove();

        ThreadContext.bind(this.subject);
        if (securityManager != null) {
            ThreadContext.bind(securityManager);
        }
    }

       我们可以看到,先解绑然后绑定。

至此真相大白:

    每个shiro拦截到的请求,都会根据seesionid创建Subject,清除当前线程的绑定,然后重新绑定的线程中,之后执行过滤器。

所以我们再SecurityUtils.getSubject()中获取的一直是当前用户的信息。

 

    

  • 39
    点赞
  • 139
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值