【Shior学习】subject和session

前一篇已经介绍过了shiroFilter(shiro的入口点,所有请求都会经该过滤器,然后找到对应的过滤器处理请求。)

// 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);
 			// 注意这里会创建Subject对象,从而引出今天的主题Subject
            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;
                }
            });
        } 
        ...//省略代码,不影响阅读
    }
 
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
            throws IOException, ServletException {
        // 使用FilterChainResolver来获取代理过滤器链
        FilterChain chain = getExecutionChain(request, response, origChain);
        // 执行代理过滤器链,由上一篇可知,会先执行shiro的过滤器链,然后执行原过滤器链
        chain.doFilter(request, response);
}

请注意上面的代码中有这么一行,是用来创建Subject对象的。

final Subject subject = createSubject(request, response);

那么Subject在shiro框架中到底代表了什么?

Subject和Session的关系又是什么呢?

带着这两个疑问,我们逐一揭开其神秘面纱!

首先来看Subject的默认实现类DelegatingSubject

//Subject的默认实现类
public class DelegatingSubject implements Subject {

    private static final Logger log = LoggerFactory.getLogger(DelegatingSubject.class);

    private static final String RUN_AS_PRINCIPALS_SESSION_KEY =
            DelegatingSubject.class.getName() + ".RUN_AS_PRINCIPALS_SESSION_KEY";

    protected PrincipalCollection principals;//当前用户
    protected boolean authenticated;//是否登陆认证过
    protected String host;//主机地址
    protected Session session;//Session引用
    /**
     * @since 1.2
     */
    protected boolean sessionCreationEnabled;
    protected transient SecurityManager securityManager;

由上可知Subject内部维护了当前登陆的用户,是否登陆认证成功,当前主机地址和Session的引用。

接下来我们看一下Subject创建的过程:

//AbstractShiroFilter.createSubject
protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
	return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
}

``WebSubject继承了Subject,而WebSubject.BuilderWebSubject的内部类,同样继承了Subject.Builder`

//WebSuject.Builder的构造函数
public Builder(SecurityManager securityManager, ServletRequest request, ServletResponse response) {
    super(securityManager);
    if (request == null) {
    	throw new IllegalArgumentException("ServletRequest argument cannot be null.");
    }
    if (response == null) {
    	throw new IllegalArgumentException("ServletResponse argument cannot be null.");
    }
    setRequest(request);
    setResponse(response);
}
//Subject.Builder构造函数
public Builder(SecurityManager securityManager) {
    if (securityManager == null) {
        throw new NullPointerException("SecurityManager method argument cannot be 				null.");	
    }
    this.securityManager = securityManager;
    //构造一个SubjectContext上下文环境
    this.subjectContext = newSubjectContextInstance();
    if (this.subjectContext == null) {
        throw new IllegalStateException("Subject instance returned from 						'newSubjectContextInstance' " +"cannot be null.");
    }
    //让securityManager放入SubjectContext上下文环境,使之贯穿Subject整个创建过程。
    this.subjectContext.setSecurityManager(securityManager);
}

subjectContext是构造Subject过程中,为Subject提供所需的上下文环境。

//WebSubject.Builder.buildWebSubject
public WebSubject buildWebSubject() {
    Subject subject = super.buildSubject();
    if (!(subject instanceof WebSubject)) {
        String msg = "Subject implementation returned from the SecurityManager was..."
        throw new IllegalStateException(msg);
    }
    return (WebSubject) subject;
}

WebSubject.Builder.buildWebSubject调用父类的buildSubject()方法。

public Subject buildSubject() {
	return this.securityManager.createSubject(this.subjectContext);
}

DefaultSecurityManager.createSubject

public Subject createSubject(SubjectContext subjectContext) {
    //复制subjectContext实例
    SubjectContext context = copy(subjectContext);
    
    //确保subjectContext中存在SecurityManager实例,不存在则创建一个
    context = ensureSecurityManager(context);
    
	//在构造Subject之前,解析Session(通过SessionId)把它放入SubjectContext,Session的维护是交给
    //专门的SessionManager来维护
    context = resolveSession(context);

    //同样的,在构造Subject之前,解析Principals把它放入SubjectContext,此过程有可能失败
    //导致最终的SubjectContext缺少Principal信息。具体过程见下文
    context = resolvePrincipals(context);

    // 创建Subject,委托给专门的SubjectFactory来构造
    // SubjectFactory接口的默认实现为DefaultWebSubjectFactory
    // 观察其对createSubject方法的实现正式将会话域context这一路收集来的信息汇总生成一个WebDelegatingSubject实例(又增加一个中间层)。
    Subject subject = doCreateSubject(context);

    //保存subject在session中,以备将来会用到(开启了rememberMe功能)
    save(subject);
    return subject;
}

解析Principals过程

//DefaultSecurityManager.resolvePrincipals
protected SubjectContext resolvePrincipals(SubjectContext context) {
	//从SubjectContext中解析principals,详见下文
    PrincipalCollection principals = context.resolvePrincipals();
    if (isEmpty(principals)) {
        //从SubjectContext中解析principals失败,尝试RemeberMe来获取principals
        principals = getRememberedIdentity(context);
        if (!isEmpty(principals)) {
            context.setPrincipals(principals);
        } else {
            log.trace("No remembered identity found.  Returning original context.");
        }
    }
    return context;
}

SubjectContext中解析principals

//SubjectContext.resolvePrincipals
public PrincipalCollection resolvePrincipals() {
    //首先查看当前上下文SubjectContext中是否存在Principals
    PrincipalCollection principals = getPrincipals();
    if (isEmpty(principals)) {
        //check to see if they were just authenticated:
        //不存在,尝试从上下文SubjectContext中的AuthenticationInfo中获取Principals
        AuthenticationInfo info = getAuthenticationInfo();
        if (info != null) {
            principals = info.getPrincipals();
        }
    }
    if (isEmpty(principals)) {
        //AuthenticationInfo也解析失败,尝试从SubjectContext中的Subject解析Principals
        Subject subject = getSubject();
        if (subject != null) {
            principals = subject.getPrincipals();
        }
    }
    if (isEmpty(principals)) {
        //Subject也解析失败,则尝试从session中获取当前的Principals
        Session session = resolveSession();
        if (session != null) {
            principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);
        }
    }
    return principals;
}

由于session是shirosessionManger管理的,当用户登陆过了,就会通过sessionId将Principals保存在session中,以便下次重新访问时,可以根据sessionId获取当前的用户。

用户信息是存在session中的,但我们获取当前用户都是通过Subject来获取,因为Subject通过sessionId取出对应 的用户信息,并放到当前Subject中,而Subject是通过ThreadLocal模式将自身绑定到当前线程。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值