在自己的项目中,对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。