2.詳解SecurityManager(shiro許可權管理門面)
阿新 • • 發佈:2019-01-07
SecurityManager 介面主要作用
為什麼要先說SecurityManager呢?因為我覺得他是shiro的主要入口,幾乎所有相關的許可權操作,都由他代理了。
1.可以說是所有配置的入口,簡化配置,方便使用。
2.一個介面就可以實現,驗證的操作(登入、退出)、授權(授權訪問指定資源、角色)、Session管理,相當於這些操作的門面(門面模式,也叫外觀模式)。
從上圖我們可以看出SecurityManager各個子類的作用及子類的依賴元件的介面。下面我們底層子類往上推,分別詳解各個子類的作用
1. CachingSecurityManager
//該抽象類實現類CacheManagerAware 介面,主要提供快取支援,管理快取操作
public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware {
//快取介面的實現類,實現可以是ehcache,java的HashMap版的cacheManager,redis
private CacheManager cacheManager;
//通過該介面可以獲取cacheManager,然後使用它來做一些你所需的操作,如進行一些自定義的快取管理
public CacheManager getCacheManager () {
return cacheManager;
}
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
//在設定cacheManager後,執行一些後續操作。該後續操作主要是:設定cacheManagr到Realm和SessionManager等相關依cacheManagr的類
afterCacheManagerSet();
}
//Template 方法,讓子類重寫實現業務邏輯
protected void afterCacheManagerSet() {
}
//銷燬SecurityManager時,銷燬該快取例項
public void destroy() {
LifecycleUtils.destroy(getCacheManager());
this.cacheManager = null;
}
}
2.RealmSecurityManager
//該抽象類主要是管理Realm(可以理解為資料處理元件,比如Realm根據使用者名稱查詢底層資料庫,然後取出來和你輸入的使用者密碼進行批評,驗證是否equals來驗證是否登陸成功,還有授權資料等)
public abstract class RealmSecurityManager extends CachingSecurityManager {
//至少需要一個Realm,可以是多個。相當於多個數據處理中心,比如mysql資料庫,redis資料庫
private Collection<Realm> realms;
public void setRealm(Realm realm) {
if (realm == null) {
throw new IllegalArgumentException("Realm argument cannot be null");
}
Collection<Realm> realms = new ArrayList<Realm>(1);
realms.add(realm);
setRealms(realms);
}
public void setRealms(Collection<Realm> realms) {
if (realms == null) {
throw new IllegalArgumentException("Realms collection argument cannot be null.");
}
if (realms.isEmpty()) {
throw new IllegalArgumentException("Realms collection argument cannot be empty.");
}
this.realms = realms;
//模板方法,定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中
afterRealmsSet();
}
protected void afterRealmsSet() {
applyCacheManagerToRealms();
}
//獲取realm,根據你是否有需求來決定使用。可能應用場景:從Realm中獲取使用者的授權、驗證的快取資訊
public Collection<Realm> getRealms() {
return realms;
}
//設定cacheManager到Realm中,因為Realm是資料處理元件,當它從資料庫中載入資料且得到正確的驗證後,可以快取到cacheManager,來提供效能。在shiro裡,AuthorizationInfo(授權資料)和AuthenticationInfo(使用者基本賬戶密碼)都被快取到cacheManager在驗證成功後
protected void applyCacheManagerToRealms() {
CacheManager cacheManager = getCacheManager();
Collection<Realm> realms = getRealms();
if (cacheManager != null && realms != null && !realms.isEmpty()) {
for (Realm realm : realms) {
if (realm instanceof CacheManagerAware) {
((CacheManagerAware) realm).setCacheManager(cacheManager);
}
}
}
}
protected void afterCacheManagerSet() {
applyCacheManagerToRealms();
}
//銷燬realms
public void destroy() {
LifecycleUtils.destroy(getRealms());
this.realms = null;
super.destroy();
}
}
3.AuthenticatingSecurityManager
//實現介面Authenticator,處理使用者登陸驗證的 SecurityManager 的 抽象實現,僅僅代理Authenticator.
public abstract class AuthenticatingSecurityManager extends RealmSecurityManager {
//依賴authenticator驗證器,真正的登陸驗證都在這裡面處理
private Authenticator authenticator;
public AuthenticatingSecurityManager() {
super();
//預設的Authenticator,在大多數情況下,該預設例項足夠用了
this.authenticator = new ModularRealmAuthenticator();
}
public Authenticator getAuthenticator() {
return authenticator;
}
//覆蓋預設的ModularRealmAuthenticator
public void setAuthenticator(Authenticator authenticator) throws IllegalArgumentException {
if (authenticator == null) {
String msg = "Authenticator argument cannot be null.";
throw new IllegalArgumentException(msg);
}
this.authenticator = authenticator;
}
//重寫父類模板方法
protected void afterRealmsSet() {
super.afterRealmsSet();
if (this.authenticator instanceof ModularRealmAuthenticator) {
((ModularRealmAuthenticator) this.authenticator).setRealms(getRealms());
}
}
//該類的核心,呼叫底層的authenticator進行使用者輸入的賬戶密碼驗證
//token 是使用者輸入的賬戶密碼
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
return this.authenticator.authenticate(token);
}
//銷燬Authenticator
public void destroy() {
LifecycleUtils.destroy(getAuthenticator());
this.authenticator = null;
super.destroy();
}
}
4.AuthorizingSecurityManager
//AuthorizingSecurityManager是實現了Authorizer介面的抽象類,該類主要代理Authorizer進行授權。
public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager {
//真正的授權器
private Authorizer authorizer;
public AuthorizingSecurityManager() {
super();
//預設的Authorizer,該預設例項支援大部分使用場景
this.authorizer = new ModularRealmAuthorizer();
}
public Authorizer getAuthorizer() {
return authorizer;
}
//覆蓋預設的Authorizer
public void setAuthorizer(Authorizer authorizer) {
if (authorizer == null) {
String msg = "Authorizer argument cannot be null.";
throw new IllegalArgumentException(msg);
}
this.authorizer = authorizer;
}
//重寫父類的afterRealmsSet(),首先呼叫父類的afterRealmsSet(),然後在設定realms到授權器,如果實現是ModularRealmAuthorizer。也就是說如果你覆蓋了預設的
ModularRealmAuthorizer,那麼你要對這個方法進行處理。
protected void afterRealmsSet() {
super.afterRealmsSet();
if (this.authorizer instanceof ModularRealmAuthorizer) {
((ModularRealmAuthorizer) this.authorizer).setRealms(getRealms());
}
}
public void destroy() {
LifecycleUtils.destroy(getAuthorizer());
this.authorizer = null;
super.destroy();
}
/**
以下都是呼叫底層的authorizer來進行授權處理。所有的使用者是否能授權其實就是呼叫以下方法。後面詳講authorizer,會講解每個方法的意思,大家可以隨便看看有個影像就可以了。
總的來說就兩類:一類是返回true false來說明是否授權成功,一個拋異常來說明是否授權成功
**/
public boolean isPermitted(PrincipalCollection principals, String permissionString) {
return this.authorizer.isPermitted(principals, permissionString);
}
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
return this.authorizer.isPermitted(principals, permission);
}
public boolean[] isPermitted(PrincipalCollection principals, String... permissions) {
return this.authorizer.isPermitted(principals, permissions);
}
public boolean[] isPermitted(PrincipalCollection principals, List<Permission> permissions) {
return this.authorizer.isPermitted(principals, permissions);
}
public boolean isPermittedAll(PrincipalCollection principals, String... permissions) {
return this.authorizer.isPermittedAll(principals, permissions);
}
public boolean isPermittedAll(PrincipalCollection principals, Collection<Permission> permissions) {
return this.authorizer.isPermittedAll(principals, permissions);
}
public void checkPermission(PrincipalCollection principals, String permission) throws AuthorizationException {
this.authorizer.checkPermission(principals, permission);
}
public void checkPermission(PrincipalCollection principals, Permission permission) throws AuthorizationException {
this.authorizer.checkPermission(principals, permission);
}
public void checkPermissions(PrincipalCollection principals, String... permissions) throws AuthorizationException {
this.authorizer.checkPermissions(principals, permissions);
}
public void checkPermissions(PrincipalCollection principals, Collection<Permission> permissions) throws AuthorizationException {
this.authorizer.checkPermissions(principals, permissions);
}
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
return this.authorizer.hasRole(principals, roleIdentifier);
}
public boolean[] hasRoles(PrincipalCollection principals, List<String> roleIdentifiers) {
return this.authorizer.hasRoles(principals, roleIdentifiers);
}
public boolean hasAllRoles(PrincipalCollection principals, Collection<String> roleIdentifiers) {
return this.authorizer.hasAllRoles(principals, roleIdentifiers);
}
public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
this.authorizer.checkRole(principals, role);
}
public void checkRoles(PrincipalCollection principals, Collection<String> roles) throws AuthorizationException {
this.authorizer.checkRoles(principals, roles);
}
public void checkRoles(PrincipalCollection principals, String... roles) throws AuthorizationException {
this.authorizer.checkRoles(principals, roles);
}
}
5.SessionsSecurityManager
//SessionsSecurityManager實現類SessionManager的方法。代理了SessionManager來處理相關的session操作。
public abstract class SessionsSecurityManager extends AuthorizingSecurityManager {
//Session管理
private SessionManager sessionManager;
public SessionsSecurityManager() {
super();
//預設的SessionManager的實現類
this.sessionManager = new DefaultSessionManager();
//顧名思義,設定CacheManager到sessionManager
applyCacheManagerToSessionManager();
}
public void setSessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
afterSessionManagerSet();
}
//在sessionManager被設定後呼叫,該類只調用 applyCacheManagerToSessionManager(),大家可以琢磨下這樣寫的好處,子類可以重寫增強等
protected void afterSessionManagerSet() {
applyCacheManagerToSessionManager();
}
//獲取sessionManager,根據你的需求。除了下面的方法,可能使用場景:獲取所有線上session
public SessionManager getSessionManager() {
return this.sessionManager;
}
//增強父類的afterCacheManagerSet(),滿足本類需求
protected void afterCacheManagerSet() {
super.afterCacheManagerSet();
applyCacheManagerToSessionManager();
}
//如果sessionManager實現了CacheManagerAware,則設定cacheManager
protected void applyCacheManagerToSessionManager() {
if (this.sessionManager instanceof CacheManagerAware) {
((CacheManagerAware) this.sessionManager).setCacheManager(getCacheManager());
}
}
//呼叫sessionManager根據上下文context(儲存session的相關建立資訊,大家可以去了解下上下文)建立個session
public Session start(SessionContext context) throws AuthorizationException {
return this.sessionManager.start(context);
}
//根據sessionID來獲取session,每次生成session後會生成個sessionID返回給瀏覽器,瀏覽器用sessionID來和sessionManager進行互動
public Session getSession(SessionKey key) throws SessionException {
return this.sessionManager.getSession(key);
}
//銷燬sessionManager
public void destroy() {
LifecycleUtils.destroy(getSessionManager());
this.sessionManager = null;
super.destroy();
}
}
6.DefaultSecurityManager
//預設的SessionManager的具體實現,該實現類會合適的初始化依賴元件。如subjectFactory、SubjectDAO
public class DefaultSecurityManager extends SessionsSecurityManager {
private static final Logger log = LoggerFactory.getLogger(DefaultSecurityManager.class);
//是否記住密碼服務元件
protected RememberMeManager rememberMeManager;
//subject(代理特定的一個使用者的所有許可權相關操作,登陸退出、授權,獲取session等)的管理元件,僅提供了save和delete方法。用於儲存subject的狀態,方便與以後可以重建subject
protected SubjectDAO subjectDAO;
//根據SubjectContext(subject的狀態和相關的資料)來建立個subject
protected SubjectFactory subjectFactory;
public DefaultSecurityManager() {
super();
this.subjectFactory = new DefaultSubjectFactory();
this.subjectDAO = new DefaultSubjectDAO();
}
public DefaultSecurityManager(Realm singleRealm) {
this();
setRealm(singleRealm);
}
//realms 呼叫子類的 setRealms(realms),至少需要一個。
public DefaultSecurityManager(Collection<Realm> realms) {
this();
setRealms(realms);
}
//獲取subjectFactory,主要用於建立subject
public SubjectFactory getSubjectFactory() {
return subjectFactory;
}
public void setSubjectFactory(SubjectFactory subjectFactory) {
this.subjectFactory = subjectFactory;
}
//獲取subjectDAO,主要用於持久化或者刪除subject狀態(該狀態可用於以後來重建subject)
public SubjectDAO getSubjectDAO() {
return subjectDAO;
}
public void setSubjectDAO(SubjectDAO subjectDAO) {
this.subjectDAO = subjectDAO;
}
public RememberMeManager getRememberMeManager() {
return rememberMeManager;
}
public void setRememberMeManager(RememberMeManager rememberMeManager) {
this.rememberMeManager = rememberMeManager;
}
protected SubjectContext createSubjectContext() {
return new DefaultSubjectContext();
}
//根據形參,建立個subject。
//token使用者輸入的賬戶密碼,info從資料庫載入的賬戶密碼
protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {
//建立預設的SubjectContext的實現
SubjectContext context = createSubjectContext();
//設定context的是否驗證、token和info,方便以後呼叫
context.setAuthenticated(true);
context.setAuthenticationToken(token);
context.setAuthenticationInfo(info);
if (existing != null) {
context.setSubject(existing);
}
return createSubject(context);
}
//shiri1.2之後已被棄用
@Deprecated
protected void bind(Subject subject) {
save(subject);
}
//執行RememberMeManager的onSuccessfulLogin(subject, token, info)
protected void rememberMeSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) {
RememberMeManager rmm = getRememberMeManager();
if (rmm != null) {
try {
//記住當前subject, principals
rmm.onSuccessfulLogin(subject, token, info);
} catch (Exception e) {
if (log.isWarnEnabled()) {
String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() +
"] threw an exception during onSuccessfulLogin. RememberMe services will not be " +
"performed for account [" + info + "].";
log.warn(msg, e);
}
}
} else {
if (log.isTraceEnabled()) {
log.trace("This " + getClass().getName() + " instance does not have a " +
"[" + RememberMeManager.class.getName() + "] instance configured. RememberMe services " +
"will not be performed for account [" + info + "].");
}
}
}
protected void rememberMeFailedLogin(AuthenticationToken token, AuthenticationException ex, Subject subject) {
RememberMeManager rmm = getRememberMeManager();
if (rmm != null) {
try {
//清除cookie(重置cookie為預設值)
rmm.onFailedLogin(subject, token, ex);
} catch (Exception e) {
if (log.isWarnEnabled()) {
String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() +
"] threw an exception during onFailedLogin for AuthenticationToken [" +
token + "].";
log.warn(msg, e);
}
}
}
}
protected void rememberMeLogout(Subject subject) {
RememberMeManager rmm = getRememberMeManager();
if (rmm != null) {
try {
//清除cookie(重置cookie為預設值)
rmm.onLogout(subject);
} catch (Exception e) {
if (log.isWarnEnabled()) {
String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() +
"] threw an exception during onLogout for subject with principals [" +
(subject != null ? subject.getPrincipals() : null) + "]";
log.warn(msg, e);
}
}
}
}
/**
使用者登陸驗證方法
1.首先呼叫authenticate(token)驗證當前使用者登陸資訊;
2.登陸驗證成功後,建立當前使用者的subject且繫結到當前執行緒和把subject的狀態資訊儲存到session中,方便以後重建subject
**/
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info;
try {
//呼叫父類AuthenticationSecurityManager的authenticate方法,驗證使用者是否登陸成功
info = authenticate(token);
} catch (AuthenticationException ae) {
try {
//如果驗證失敗,重置cookie為初始化值
onFailedLogin(token, ae, subject);
} catch (Exception e) {
if (log.isInfoEnabled()) {
log.info("onFailedLogin method threw an " +
"exception. Logging and propagating original AuthenticationException.", e);
}
}
//驗證失敗,則丟擲異常
throw ae; //propagate
}
//建立subject
Subject loggedIn = createSubject(token, info, subject);
//設定唯一的principle到cookie中
onSuccessfulLogin(token, info, loggedIn);
return loggedIn;
}
protected void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) {
rememberMeSuccessfulLogin(token, info, subject);
}
protected void onFailedLogin(AuthenticationToken token, AuthenticationException ae, Subject subject) {
rememberMeFailedLogin(token, ae, subject);
}
protected void beforeLogout(Subject subject) {
rememberMeLogout(subject);
}
protected SubjectContext copy(SubjectContext subjectContext) {
return new DefaultSubjectContext(subjectContext);
}
public Subject createSubject(SubjectContext subjectContext) {
//複製個subjectContext,不修改原來的subjectContext
SubjectContext context = copy(subjectContext);
//確保context已設定securityManager
context = ensureSecurityManager(context);
//確保context已設定session,如果不存在想辦法獲取已存在的session,然後設定進去
context = resolveSession(context);
//確保context已設定Principals,如果未設定則從rememberManager獲取,然後設定
context = resolvePrincipals(context);
//呼叫subjectFactory建立subject
Subject subject = doCreateSubject(context);
//儲存subject狀態到session
save(subject);
return subject;
}
protected Subject doCreateSubject(SubjectContext context) {
return getSubjectFactory().createSubject(context);
}
//呼叫subjectDAO儲存subject狀態
protected void save(Subject subject) {
this.subjectDAO.save(subject);
}
//呼叫subjectDAO刪除subject狀態
protected void delete(Subject subject) {
this.subjectDAO.delete(subject);
}
@SuppressWarnings({"unchecked"})
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;
}
//確保session被設定到context
@SuppressWarnings({"unchecked"})
protected SubjectContext resolveSession(SubjectContext context) {
//如果已經存在session,則直接返回context
if (context.resolveSession() != null) {
log.debug("Context already contains a session. Returning.");
return context;
}
try {
//根據sessionKey從sessionManager獲取session
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;
}
//根據sessionKey從sessionManager獲取session
protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException {
SessionKey key = getSessionKey(context);
if (key != null) {
return getSession(key);
}
return null;
}
//獲取sessionKey(SID)
protected SessionKey getSessionKey(SubjectContext context) {
Serializable sessionId = context.getSessionId();
if (sessionId != null) {
return new DefaultSessionKey(sessionId);
}
return null;
}
@SuppressWarnings({"unchecked"})
protected SubjectContext resolvePrincipals(SubjectContext context) {
PrincipalCollection principals = context.resolvePrincipals();
if (CollectionUtils.isEmpty(principals)) {
//從Remembered獲取principals
principals = getRememberedIdentity(context);
if (!CollectionUtils.isEmpty(principals)) {
context.setPrincipals(principals);
} else {
log.trace("No remembered identity found. Returning original context.");
}
}
return context;
}
protected SessionContext createSessionContext(SubjectContext subjectContext) {
DefaultSessionContext sessionContext = new DefaultSessionContext();
if (!CollectionUtils.isEmpty(subjectContext)) {
sessionContext.putAll(subjectContext);
}
Serializable sessionId = subjectContext.getSessionId();
if (sessionId != null) {
sessionContext.setSessionId(sessionId);
}
String host = subjectContext.resolveHost();
if (host != null) {
sessionContext.setHost(host);
}
return sessionContext;
}
//使用者退出方法
public void logout(Subject subject) {
if (subject == null) {
throw new IllegalArgumentException("Subject method argument cannot be null.");
}
//在執行退出方法前,重置cookie為初始化值
beforeLogout(subject);
PrincipalCollection principals = subject.getPrincipals();
if (principals != null && !principals.isEmpty()) {
if (log.isDebugEnabled()) {
log.debug("Logging out subject with primary principal {}", principals.getPrimaryPrincipal());
}
//呼叫驗證器的onLogout方法,目前該方法主要是迭代AuthenticationListener的logout方法
Authenticator authc = getAuthenticator();
if (authc instanceof LogoutAware) {
((LogoutAware) authc).onLogout(principals);
}
}
try {
delete(subject);
} catch (Exception e) {
} finally {
try {
//設定session過期
stopSession(subject);
} catch (Exception e) {
}
}
}
//設定session過期
protected void stopSession(Subject subject) {
Session s = subject.getSession(false);
if (s != null) {
s.stop();
}
}
/**
* Unbinds or removes the Subject's state from the application, typically called during {@link #logout}.
* <p/>
* This has been deprecated in Shiro 1.2 in favor of the {@link #delete(org.apache.shiro.subject.Subject) delete}
* method. The implementation has been updated to invoke that method.
*
* @param subject the subject to unbind from the application as it will no longer be used.
* @deprecated in Shiro 1.2 in favor of {@link #delete(org.apache.shiro.subject.Subject)}
*/
@Deprecated
@SuppressWarnings({"UnusedDeclaration"})
protected void unbind(Subject subject) {
delete(subject);
}
//根據SubjectContext從RememberMeManager獲取PrincipalCollection
protected PrincipalCollection getRememberedIdentity(SubjectContext subjectContext) {
RememberMeManager rmm = getRememberMeManager();
if (rmm != null) {
try {
return rmm.getRememberedPrincipals(subjectContext);
} catch (Exception e) {
if (log.isWarnEnabled()) {
String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() +
"] threw an exception during getRememberedPrincipals().";
log.warn(msg, e);
}
}
}
return null;
}
}
7.DefaultWebSecurityManager
//WebSecurityManager實現被使用在基於web的應用程式中或者需要http請求的應用程式中(如SOAP,http remoting, etc)
public class DefaultWebSecurityManager extends DefaultSecurityManager implements WebSecurityManager {
private static final Logger log = LoggerFactory.getLogger(DefaultWebSecurityManager.class);
@Deprecated
public static final String HTTP_SESSION_MODE = "http";
@Deprecated
public static final String NATIVE_SESSION_MODE = "native";
/**
* @deprecated as of 1.2. This should NOT be used for anything other than determining if the sessionMode has changed.
*/
@Deprecated
private String sessionMode;
public DefaultWebSecurityManager() {
super();
//設定SessionStorageEvaluator(評估Session是否需要儲存,在無狀態環境下可以設定為false,如Rest架構)到DefaultSubjectDAO
((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
this.sessionMode = HTTP_SESSION_MODE;
//初始化基於WEB的DefaultWebSubjectFactory
setSubjectFactory(new DefaultWebSubjectFactory());
setRememberMeManager(new CookieRememberMeManager());
//預設是 基於servlet的session的sessionManaer,即不使用shiro自己管理的session
setSessionManager(new ServletContainerSessionManager());
}
@SuppressWarnings({"UnusedDeclaration"})
public DefaultWebSecurityManager(Realm singleRealm) {
this();
setRealm(singleRealm);
}
@SuppressWarnings({"UnusedDeclaration"})
public DefaultWebSecurityManager(Collection<Realm> realms) {
this();
setRealms(realms);
}
@Override
protected SubjectContext createSubjectContext() {
return new DefaultWebSubjectContext();
}
@Override
//since 1.2.1 for fixing SHIRO-350
public void setSubjectDAO(SubjectDAO subjectDAO) {
super.setSubjectDAO(subjectDAO);
//設定sessionManager到DefaultSubjectDAO的SessionStorageEvaluator
applySessionManagerToSessionStorageEvaluatorIfPossible();
}
//在sessionManager被設定後,設定sessionManager到DefaultSubjectDAO的SessionStorageEvaluator
@Override
protected void afterSessionManagerSet() {
super.afterSessionManagerSet();
applySessionManagerToSessionStorageEvaluatorIfPossible();
}
//設定sessionManager到DefaultSubjectDAO的SessionStorageEvaluator
private void applySessionManagerToSessionStorageEvaluatorIfPossible() {
SubjectDAO subjectDAO = getSubjectDAO();
if (subjectDAO instanceof DefaultSubjectDAO) {
SessionStorageEvaluator evaluator = ((DefaultSubjectDAO)subjectDAO).getSessionStorageEvaluator();
if (evaluator instanceof DefaultWebSessionStorageEvaluator) {
((DefaultWebSessionStorageEvaluator)evaluator).setSessionManager(getSessionManager());
}
}
}
@Override
protected SubjectContext copy(SubjectContext subjectContext) {
if (subjectContext instanceof WebSubjectContext) {
return new DefaultWebSubjectContext((WebSubjectContext) subjectContext);
}
return super.copy(subjectContext);
}
@SuppressWarnings({"UnusedDeclaration"})
@Deprecated
public String getSessionMode() {
return sessionMode;
}
/**
* @param sessionMode
* @deprecated since 1.2
*/
@Deprecated
public void setSessionMode(String sessionMode) {
log.warn("The 'sessionMode' property has been deprecated. Please configure an appropriate WebSessionManager " +
"instance instead of using this property. This property/method will be removed in a later version.");
String mode = sessionMode;
if (mode == null) {
throw new IllegalArgumentException("sessionMode argument cannot be null.");
}
mode = sessionMode.toLowerCase();
if (!HTTP_SESSION_MODE.equals(mode) && !NATIVE_SESSION_MODE.equals(mode)) {
String msg = "Invalid sessionMode [" + sessionMode + "]. Allowed values are " +
"public static final String constants in the " + getClass().getName() + " class: '"
+ HTTP_SESSION_MODE + "' or '" + NATIVE_SESSION_MODE + "', with '" +
HTTP_SESSION_MODE + "' being the default.";
throw new IllegalArgumentException(msg);
}
boolean recreate = this.sessionMode == null || !this.sessionMode.equals(mode);
this.sessionMode = mode;
if (recreate) {
LifecycleUtils.destroy(getSessionManager());
SessionManager sessionManager = createSessionManager(mode);
this.setInternalSessionManager(sessionManager);
}
}
@Override
public void setSessionManager(SessionManager sessionManager) {
this.sessionMode = null;
if (sessionManager != null && !(sessionManager instanceof WebSessionManager)) {
if (log.isWarnEnabled()) {
String msg = "The " + getClass().getName() + " implementation expects SessionManager instances " +
"that implement the " + WebSessionManager.class.getName() + " interface. The " +
"configured instance is of type [" + sessionManager.getClass().getName() + "] which does not " +
"implement this interface.. This may cause unexpected behavior.";
log.warn(msg);
}
}
setInternalSessionManager(sessionManager);
}
/**
* @param sessionManager
* @since 1.2
*/
private void setInternalSessionManager(SessionManager sessionManager) {
super.setSessionManager(sessionManager);
}
//判斷是否是使用httpSession
public boolean isHttpSessionMode() {
SessionManager sessionManager = getSessionManager();
return sessionManager instanceof WebSessionManager && ((WebSessionManager)sessionManager).isServletContainerSessions();
}
protected SessionManager createSessionManager(String sessionMode) {
if (sessionMode == null || !sessionMode.equalsIgnoreCase(NATIVE_SESSION_MODE)) {
log.info("{} mode - enabling ServletContainerSessionManager (HTTP-only Sessions)", HTTP_SESSION_MODE);
return new ServletContainerSessionManager();
} else {
log.info("{} mode - enabling DefaultWebSessionManager (non-HTTP and HTTP Sessions)", NATIVE_SESSION_MODE);
return new DefaultWebSessionManager();
}
}
@Override
protected SessionContext createSessionContext(SubjectContext subjectContext) {
SessionContext sessionContext = super.createSessionContext(subjectContext);
if (subjectContext instanceof WebSubjectContext) {
WebSubjectContext wsc = (WebSubjectContext) subjectContext;
ServletRequest request = wsc.resolveServletRequest();
ServletResponse response = wsc.resolveServletResponse();
DefaultWebSessionContext webSessionContext = new DefaultWebSessionContext(sessionContext);
if (request != null) {
webSessionContext.setServletRequest(request);
}
if (response != null) {
webSessionContext.setServletResponse(response);
}
sessionContext = webSessionContext;
}
return sessionContext;
}
@Override
protected SessionKey getSessionKey(SubjectContext context) {
if (WebUtils.isWeb(context)) {
Serializable sessionId = context.getSessionId();
ServletRequest request = WebUtils.getRequest(context);
ServletResponse response = WebUtils.getResponse(context);
return new WebSessionKey(sessionId, request, response);
} else {
return super.getSessionKey(context);
}
}
@Override
protected void beforeLogout(Subject subject) {
super.beforeLogout(subject);
removeRequestIdentity(subject);
}
protected void removeRequestIdentity(Subject subject) {
if (subject instanceof WebSubject) {
WebSubject webSubject = (WebSubject) subject;
ServletRequest request = webSubject.getServletRequest();
if (request != null) {
request.setAttribute(ShiroHttpServletRequest.IDENTITY_REMOVED_KEY, Boolean.TRUE);
}
}
}
}