2018.3.8 shiro的buildSubject相关源码解析

在自己的项目中,对login方法的使用示例:

Subject currentUser = new Subject.Builder().buildSubject();
token = new UsernamePasswordToken(username,password);
currentUser.login(token);


Subject.Builder().builderSubject实际最终调用的是securityManger.createSubject()。

public interface Subject{
	//其余略
	void login(AuthenticationToken token) throws AuthenticationException;
	void logout();

	public static class Builder {
		//其余略
		private final SubjectContext subjectContext;
		private final SecurityManager securityManager;

		public Builder() {
			this(SecurityUtils.getSecurityManager());
		}

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

		public Builder(SecurityManager securityManager) {
			if (securityManager == null) {
				throw new NullPointerException("SecurityManager method argument cannot be null.");
			}
			this.securityManager = securityManager;
			this.subjectContext = newSubjectContextInstance();
			if (this.subjectContext == null) {
				throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
							"cannot be null.");
			}
			this.subjectContext.setSecurityManager(securityManager);
		
		}

		protected SubjectContext newSubjectContextInstance() {
			return new DefaultSubjectContext();
		}

	}
}


DefaultSecurityManager是SecurityManger的实现类,它有一个子类DefaultWebSecurityManager,用于web环境。

DefaultSecurityManager的无参构造函数如下。

public class DefaultSecurityManager extends SessionsSecurityManager {
	public DefaultSecurityManager() {
            super();
            this.subjectFactory = new DefaultSubjectFactory();
            this.subjectDAO = new DefaultSubjectDAO();
    }
}


DefaultWebSecurityManager的无参构造函数如下。

public class DefaultWebSecurityManager extends DefaultSecurityManager implements WebSecurityManager {
	public DefaultWebSecurityManager() {
            super();
            ((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
            this.sessionMode = HTTP_SESSION_MODE;
            setSubjectFactory(new DefaultWebSubjectFactory());
            setRememberMeManager(new CookieRememberMeManager());
            setSessionManager(new ServletContainerSessionManager());
        }
}

在这里以DefaultSecurityManager.createSubject()为例。DefaultWebSecurityManager中并没有重写父类的createSubject方法。子方法的重写这里不详述了。

public class DefaultSecurityManager extends SessionsSecurityManager {

	public Subject createSubject(SubjectContext subjectContext) {
		SubjectContext context = copy(subjectContext);

		context = ensureSecurityManager(context);
		context = resolveSession(context);
		context = resolvePrincipals(context);

		Subject subject = doCreateSubject(context);
		save(subject);

		return subject;
	}
}


1 copy

首先创建了一个copy,以免修改原本的SubjectContext。

SubjectContext接口继承自map,里面存有一些相关信息,比如securityManger,subject,sessionId,principals,session等。DefaultSubjectContext是它的默认实现,他有一个子类是DefaultWebSubjectContext。

这里可以理解为拿到上下文环境。

public class DefaultSecurityManager extends SessionsSecurityManager {

	protected SubjectContext copy(SubjectContext subjectContext) {
			return new DefaultSubjectContext(subjectContext);
	}
}


2 ensure security manager

然后要确认securityManager是否存在context中,不存在则创建。创建完后放入subjectContext,以后就直接从中取出。

context.resolveSecurityManager()实际最终调用的是SecurityUtils.getSecurityManager()来获取。

public class DefaultSecurityManager extends SessionsSecurityManager {

	protected SubjectContext ensureSecurityManager(SubjectContext context) {
		if (context.resolveSecurityManager() != null) {
			log.trace("Context already contains a SecurityManager instance.  Returning.");
			return context;
		}
		log.trace("No SecurityManager found in context.  Adding self reference.");
		context.setSecurityManager(this);
		return context;
	}
}

public class DefaultSubjectContext extends MapContext implements SubjectContext {

	public SecurityManager resolveSecurityManager() {
		SecurityManager securityManager = this.getSecurityManager();
		
		if (securityManager == null) {
			if (log.isDebugEnabled()) {
				log.debug("No SecurityManager available in subject context map.  Falling back to SecurityUtils.getSecurityManager() lookup.");
			}

			try {
				securityManager = SecurityUtils.getSecurityManager();
			} catch (UnavailableSecurityManagerException var3) {
				if (log.isDebugEnabled()) {
					log.debug("No SecurityManager available via SecurityUtils.  Heuristics exhausted.", var3);
				}
			}
		}

		return securityManager;
	}
}

public abstract class SecurityUtils {
	//其余略
	private static SecurityManager securityManager;
	
	public static void setSecurityManager(SecurityManager securityManager) {
        SecurityUtils.securityManager = securityManager;
    }
	
	public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException {
		SecurityManager securityManager = ThreadContext.getSecurityManager();
		if (securityManager == null) {
			securityManager = SecurityUtils.securityManager;
		}
		if (securityManager == null) {
			String msg = "No SecurityManager accessible to the calling code, either bound to the " +
					ThreadContext.class.getName() + " or as a vm static singleton.  This is an invalid application " +
					"configuration.";
			throw new UnavailableSecurityManagerException(msg);
		}
		return securityManager;
	}
}

3 resolve session

然后要确认session是否存在,不存在则创建,因为后续的操作要用到。同上,创建完后放入subjectContext,以后就直接从中取出。

public class DefaultSecurityManager extends SessionsSecurityManager {

    protected SubjectContext resolveSession(SubjectContext context) {
        if (context.resolveSession() != null) {
            log.debug("Context already contains a session.  Returning.");
            return context;
        }
        try {
            //Context couldn't resolve it directly, let's see if we can since we have direct access to 
            //the session manager:
            Session session = resolveContextSession(context);
            if (session != null) {
                context.setSession(session);
            }
        } catch (InvalidSessionException e) {
            log.debug("Resolved SubjectContext context session is invalid.  Ignoring and creating an anonymous " +
                    "(session-less) Subject instance.", e);
        }
        return context;
    }
}

public class DefaultSubjectContext extends MapContext implements SubjectContext {

	public Session resolveSession() {
        Session session = getSession();
        if (session == null) {
            Subject existingSubject = getSubject();
            if (existingSubject != null) {
                session = existingSubject.getSession(false);
            }
        }
        return session;
    }
	
	protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException {
        SessionKey key = getSessionKey(context);
        if (key != null) {
            return getSession(key);
        }
        return null;
    }
	
	protected SessionKey getSessionKey(SubjectContext context) {
        Serializable sessionId = context.getSessionId();
        if (sessionId != null) {
            return new DefaultSessionKey(sessionId);
        }
        return null;
    }
}


3 resolve principals

和上面类似,创建完后放入subjectContext,以后就直接从中取出。注意,这里有一个getRememberedIdentity。

public class DefaultSecurityManager extends SessionsSecurityManager {

	protected SubjectContext resolvePrincipals(SubjectContext context) {

        PrincipalCollection principals = context.resolvePrincipals();

        if (CollectionUtils.isEmpty(principals)) {
            log.trace("No identity (PrincipalCollection) found in the context.  Looking for a remembered identity.");

            principals = getRememberedIdentity(context);

            if (!CollectionUtils.isEmpty(principals)) {
                log.debug("Found remembered PrincipalCollection.  Adding to the context to be used " +
                        "for subject construction by the SubjectFactory.");

                context.setPrincipals(principals);
            } else {
                log.trace("No remembered identity found.  Returning original context.");
            }
        }
        return context;
    }
}

先从验证信息中取,再从subject中取,最后从session中取。

public class DefaultSubjectContext extends MapContext implements SubjectContext {

	public PrincipalCollection resolvePrincipals() {
        PrincipalCollection principals = getPrincipals();

        if (CollectionUtils.isEmpty(principals)) {
            //check to see if they were just authenticated:
            AuthenticationInfo info = getAuthenticationInfo();
            if (info != null) {
                principals = info.getPrincipals();
            }
        }

        if (CollectionUtils.isEmpty(principals)) {
            Subject subject = getSubject();
            if (subject != null) {
                principals = subject.getPrincipals();
            }
        }

        if (CollectionUtils.isEmpty(principals)) {
            //try the session:
            Session session = resolveSession();
            if (session != null) {
                principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);
            }
        }

        return principals;
    }
}


4 doCreateSubject

在完成对securityManager、session、principals的获取后,真正执行创建的是doCreateSubject。这里采用的是工厂方式。

public class DefaultSecurityManager extends SessionsSecurityManager {
	protected SubjectFactory subjectFactory;

	public DefaultSecurityManager() {
        super();
        this.subjectFactory = new DefaultSubjectFactory();
        this.subjectDAO = new DefaultSubjectDAO();
    }
	
	protected Subject doCreateSubject(SubjectContext context) {
        return getSubjectFactory().createSubject(context);
    }
}

SujectFactory接口只有一个方法createSubject,DefaultSubjectFactory是它的默认实现类。DefaultSubjectFactory还有一个子类DefaultWebSubjectFactory,是用于web环境下的。

public interface SubjectFactory {
	Subject createSubject(SubjectContext context);
}

public class DefaultSubjectFactory implements SubjectFactory {

    public DefaultSubjectFactory() {
    }

    public Subject createSubject(SubjectContext context) {
        SecurityManager securityManager = context.resolveSecurityManager();
        Session session = context.resolveSession();
        boolean sessionCreationEnabled = context.isSessionCreationEnabled();
        PrincipalCollection principals = context.resolvePrincipals();
        boolean authenticated = context.resolveAuthenticated();
        String host = context.resolveHost();

        return new DelegatingSubject(principals, authenticated, host, session, sessionCreationEnabled, securityManager);
    }

    @Deprecated
    protected Subject newSubjectInstance(PrincipalCollection principals, boolean authenticated, String host,
                                         Session session, SecurityManager securityManager) {
        return new DelegatingSubject(principals, authenticated, host, session, true, securityManager);
    }

}
public class DefaultWebSubjectFactory extends DefaultSubjectFactory {

    public DefaultWebSubjectFactory() {
        super();
    }

    public Subject createSubject(SubjectContext context) {
        if (!(context instanceof WebSubjectContext)) {
            return super.createSubject(context);
        }
        WebSubjectContext wsc = (WebSubjectContext) context;
        SecurityManager securityManager = wsc.resolveSecurityManager();
        Session session = wsc.resolveSession();
        boolean sessionEnabled = wsc.isSessionCreationEnabled();
        PrincipalCollection principals = wsc.resolvePrincipals();
        boolean authenticated = wsc.resolveAuthenticated();
        String host = wsc.resolveHost();
        ServletRequest request = wsc.resolveServletRequest();
        ServletResponse response = wsc.resolveServletResponse();

        return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,
                request, response, securityManager);
    }

    @Deprecated
    protected Subject newSubjectInstance(PrincipalCollection principals, boolean authenticated,
                                         String host, Session session,
                                         ServletRequest request, ServletResponse response,
                                         SecurityManager securityManager) {
        return new WebDelegatingSubject(principals, authenticated, host, session, true,
                request, response, securityManager);
    }
}


DefaultSubjectFactory在创建subject的时候,实际创建的是一个DelegatingSubject。

public class DelegatingSubject implements Subject {

	public DelegatingSubject(PrincipalCollection principals, boolean authenticated, String host,
                             Session session, boolean sessionCreationEnabled, SecurityManager securityManager) {
        if (securityManager == null) {
            throw new IllegalArgumentException("SecurityManager argument cannot be null.");
        }
        this.securityManager = securityManager;
        this.principals = principals;
        this.authenticated = authenticated;
        this.host = host;
        if (session != null) {
            this.session = decorate(session);
        }
        this.sessionCreationEnabled = sessionCreationEnabled;
    }
}

它有一个子类是WebDelegatingSubject,是用于web环境下的。对应的,DefaultWebSubjectFactory,实际创建的是一个DelegatingWebSubject。可以看到,构造上多了response和request参数。

public class WebDelegatingSubject extends DelegatingSubject implements WebSubject {
	public WebDelegatingSubject(PrincipalCollection principals, boolean authenticated,
								String host, Session session, boolean sessionEnabled,
								ServletRequest request, ServletResponse response,
								SecurityManager securityManager) {
		super(principals, authenticated, host, session, sessionEnabled, securityManager);
		this.servletRequest = request;
		this.servletResponse = response;
	}
}


5 save subject

创建完之后,需要保存subject。最终调用的是subjectDao.save(subject);

public class DefaultSecurityManager extends SessionsSecurityManager {
	protected void save(Subject subject) {
        this.subjectDAO.save(subject);
    }
}

接口SubjectDao有两个方法。

public interface SubjectDAO {
	Subject save(Subject subject);
	void delete(Subject subject);
}


它的默认实现是DefaultSubjectDAO。save方法中,当配置设置为可以保存时,会将subject保存至session。

public class DefaultSubjectDAO implements SubjectDAO {
	public Subject save(Subject subject) {
		if (isSessionStorageEnabled(subject)) {
			saveToSession(subject);
		} else {
			log.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " +
					"authentication state are expected to be initialized on every request or invocation.", subject);
		}

		return subject;
	}
}


至此,create subject功能完成。

6 总结

(1)创建的步骤

  • 拿到subject context。
  • 验证security,不存在则创建,放入contex中。
  • 验证session,不存在则创建,放入context中。
  • 验证principals,放入context中。
  • 通过subjectFactory创建subject。
  • 通过sessionDAO保存到session中。

(2)涉及的重要类

  • SecurityManager,DefaultSecurityManager,DefaultWebSecurityManager。
  • SubjectFactory,DefaultSubjectFactory,DefaultWebSubjectFactory。
  • Subject,Builder,DelegatingSubject,WebDelegatingSubject。
  • SubjectContext,DefaultSubjectContext,DefaultWebSubjectContext。
  • SessionDAO,DefaultSessionDAO。

(3)类图




















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值