赞
踩
目录
2. DefaultWebSecurityManager解析
2.4 CachingSecurityManager(聚合缓存管理和事件监听管理功能)
2.5 RealmSecurityManager(聚合Realm管理功能)
2.6 AuthenticatingSecurityManager(聚合登录认证功能)
2.7 AuthorizingSecurityManager(聚合访问控制功能)
2.8 SessionsSecurityManager(聚合Session管理功能)
2.10 DefaultWebSecurityManager
Shiro作为一款比较流行的登录认证、访问控制安全框架,被广泛应用在程序员社区;Shiro登录验证、访问控制、Session管理等流程内部都是委托给SecurityManager安全管理器来完成的,在前述文章全面解析Shiro框架原理的基础之上,详见:Shiro框架:ShiroFilterFactoryBean过滤器源码解析-CSDN博客、Shiro框架:Shiro内置过滤器源码解析-CSDN博客,本篇文章继续深入解析Shiro SecurityManager安全管理器的结构和功能,为后续各处理流程的解析做好铺垫。
如果想要对用户登录认证、用户访问控制鉴权流程有更深层次的理解,请移步:
SecurityManager接口类图如下:
可以看到SecurityManager实际上整合了3部分功能:
- Authenticator:登录认证器
- Authorizer:访问控制器
- SessionManager:Session管理器
同时SecurityManager也定义了登录、退出以及创建用户的接口功能,如下:
- public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
- /**
- * Logs in the specified Subject using the given {@code authenticationToken}, returning an updated Subject
- * instance reflecting the authenticated state if successful or throwing {@code AuthenticationException} if it is
- * not.
- */
- Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;
-
- /**
- * Logs out the specified Subject from the system.
- */
- void logout(Subject subject);
-
- /**
- * Creates a {@code Subject} instance reflecting the specified contextual data.
- */
- Subject createSubject(SubjectContext context);
-
- }
登录认证器,定义了用户登录认证方法,交由子类具体实现,如下:
- public interface Authenticator {
-
- /**
- * Authenticates a user based on the submitted {@code AuthenticationToken}.
- */
- public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
- throws AuthenticationException;
- }
用户鉴权器,引入了鉴权方法对登录用户执行鉴权操作,交由子类具体实现;
Authorizer定义如下:
- public interface Authorizer {
-
- /**
- * Returns <tt>true</tt> if the corresponding subject/user is permitted to perform an action or access a resource
- */
- boolean isPermitted(PrincipalCollection principals, String permission);
-
- /**
- * Ensures the corresponding Subject/user implies the specified permission String.
- */
- void checkPermission(PrincipalCollection subjectPrincipal, String permission) throws AuthorizationException;
-
- /**
- * Returns <tt>true</tt> if the corresponding Subject/user has the specified role, <tt>false</tt> otherwise.
- */
- boolean hasRole(PrincipalCollection subjectPrincipal, String roleIdentifier);
-
- /**
- * Asserts the corresponding Subject/user has the specified role by returning quietly if they do or throwing an
- * {@link AuthorizationException} if they do not.
- */
- void checkRole(PrincipalCollection subjectPrincipal, String roleIdentifier) throws AuthorizationException;
-
- //其它鉴权方法
- }
Session管理器,提供了创建Session以及获取Session操作方法:
- public interface SessionManager {
-
- /**
- * Starts a new session based on the specified contextual initialization data, which can be used by the underlying
- * implementation to determine how exactly to create the internal Session instance.
- * <p/>
- * This method is mainly used in framework development, as the implementation will often relay the argument
- * to an underlying {@link SessionFactory} which could use the context to construct the internal Session
- * instance in a specific manner. This allows pluggable {@link org.apache.shiro.session.Session Session} creation
- * logic by simply injecting a {@code SessionFactory} into the {@code SessionManager} instance.
- */
- Session start(SessionContext context);
-
- /**
- * Retrieves the session corresponding to the specified contextual data (such as a session ID if applicable), or
- * {@code null} if no Session could be found. If a session is found but invalid (stopped or expired), a
- * {@link SessionException} will be thrown.
- */
- Session getSession(SessionKey key) throws SessionException;
- }
在Web服务中,Shiro中应用的SecurityManager具体为子类DefaultWebSecurityManager,为了对其有足够的理解,下面对DefaultWebSecurityManager类继承层次中相关类进行解析说明,其类继承结构如下:
Shiro框架自定义了Destroyable接口,引入了destroy方法,支持销毁操作:
- public interface Destroyable {
- void destroy() throws Exception;
- }
子类实现支持注入缓存管理器,进而获取构造缓存实例,执行缓存操作;
- public interface CacheManagerAware {
-
- /**
- * Sets the available CacheManager instance on this component.
- *
- * @param cacheManager the CacheManager instance to set on this component.
- */
- void setCacheManager(CacheManager cacheManager);
- }
子类对象支持注入EventBus对象,类似Guava EventBus,引入事件机制,可以发布事件(publish),进而调用已注册(register)的事件监听器;
- public interface EventBusAware {
-
- /**
- * Sets the available {@code EventBus} that may be used for publishing and subscribing to/from events.
- *
- * @param bus the available {@code EventBus}.
- */
- void setEventBus(EventBus bus);
- }
- public interface EventBus {
-
- void publish(Object event);
-
- void register(Object subscriber);
-
- void unregister(Object subscriber);
- }
同时实现了Destroyable, CacheManagerAware, EventBusAware,支持注入成员变量cacheManager和eventBus,并定义了销毁操作,主要实现如下:
- public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware, EventBusAware {
-
- /**
- * The CacheManager to use to perform caching operations to enhance performance. Can be null.
- */
- private CacheManager cacheManager;
-
- /**
- * The EventBus to use to use to publish and receive events of interest during Shiro's lifecycle.
- * @since 1.3
- */
- private EventBus eventBus;
-
- /**
- * Default no-arg constructor that will automatically attempt to initialize a default cacheManager
- */
- public CachingSecurityManager() {
- //use a default event bus:
- setEventBus(new DefaultEventBus());
- }
-
- /**
- * Destroys the {@link #getCacheManager() cacheManager} via {@link LifecycleUtils#destroy LifecycleUtils.destroy}.
- */
- public void destroy() {
- LifecycleUtils.destroy(getCacheManager());
- this.cacheManager = null;
- LifecycleUtils.destroy(getEventBus());
- this.eventBus = new DefaultEventBus();
- }
- }
RealmSecurityManager引入成员变量Realm,支持Realm的管理,包括:注册、销毁等操作;
Realm:Shiro引入的安全组件,支持获取App本地的用户安全相关数据,包括:用户信息、角色、权限等,用于执行用户认证和用户鉴权;
官方释义:
A Realm is a security component that can access application-specific security entities
such as users, roles, and permissions to determine authentication and authorization operations.
RealmSecurityManager主要实现如下:
- public abstract class RealmSecurityManager extends CachingSecurityManager {
-
- /**
- * Internal collection of <code>Realm</code>s used for all authentication and authorization operations.
- */
- private Collection<Realm> 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();
- applyEventBusToRealms();
- }
-
- public void destroy() {
- LifecycleUtils.destroy(getRealms());
- this.realms = null;
- super.destroy();
- }
- }
AuthenticatingSecurityManager注入成员变量authenticator(默认初始化的实例是ModularRealmAuthenticator)并进行初始化设置(注入认证组件Realm),同时实现了接口Authenticator定义的authenticate方法,内部委托给成员变量authenticator,如下:
- private Authenticator authenticator;
-
- /**
- * Default no-arg constructor that initializes its internal
- * <code>authenticator</code> instance to a
- * {@link org.apache.shiro.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator}.
- */
- public AuthenticatingSecurityManager() {
- super();
- this.authenticator = new ModularRealmAuthenticator();
- }
-
- /**
- * Passes on the {@link #getRealms() realms} to the internal delegate <code>Authenticator</code> instance so
- * that it may use them during authentication attempts.
- */
- protected void afterRealmsSet() {
- super.afterRealmsSet();
- if (this.authenticator instanceof ModularRealmAuthenticator) {
- ((ModularRealmAuthenticator) this.authenticator).setRealms(getRealms());
- }
- }
-
- public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
- return this.authenticator.authenticate(token);
- }
和AuthenticatingSecurityManager类似,这里注入了成员变量authorizer来实现访问控制的鉴权功能,authorizer的默认实现是ModularRealmAuthorizer,同样也对authorizer进行了初始化设置,注入Reaml,并具体实现了接口Authorizer引入的访问控制方法,如下:
- /**
- * The wrapped instance to which all of this <tt>SecurityManager</tt> authorization calls are delegated.
- */
- private Authorizer authorizer;
-
- /**
- * Default no-arg constructor that initializes an internal default
- * {@link org.apache.shiro.authz.ModularRealmAuthorizer ModularRealmAuthorizer}.
- */
- public AuthorizingSecurityManager() {
- super();
- this.authorizer = new ModularRealmAuthorizer();
- }
-
- protected void afterRealmsSet() {
- super.afterRealmsSet();
- if (this.authorizer instanceof ModularRealmAuthorizer) {
- ((ModularRealmAuthorizer) this.authorizer).setRealms(getRealms());
- }
- }
-
- public boolean isPermitted(PrincipalCollection principals, String permissionString) {
- return this.authorizer.isPermitted(principals, permissionString);
- }
内部注入了成员变量sessionManagerSession管理器,实现了session创建、session获取的方法:
- /**
- * The internal delegate <code>SessionManager</code> used by this security manager that manages all the
- * application's {@link Session Session}s.
- */
- private SessionManager sessionManager;
-
- /**
- * Default no-arg constructor, internally creates a suitable default {@link SessionManager SessionManager} delegate
- * instance.
- */
- public SessionsSecurityManager() {
- super();
- this.sessionManager = new DefaultSessionManager();
- applyCacheManagerToSessionManager();
- }
-
- public Session start(SessionContext context) throws AuthorizationException {
- return this.sessionManager.start(context);
- }
-
- public Session getSession(SessionKey key) throws SessionException {
- return this.sessionManager.getSession(key);
- }
DefaultSecurityManager主要聚合了3部分功能,这点可以从其包含的成员变量看出,下面进行分别说明:
- protected RememberMeManager rememberMeManager;
- protected SubjectDAO subjectDAO;
- protected SubjectFactory subjectFactory;
-
- /**
- * Default no-arg constructor.
- */
- public DefaultSecurityManager() {
- super();
- this.subjectFactory = new DefaultSubjectFactory();
- this.subjectDAO = new DefaultSubjectDAO();
- }
Subject创建过程是通过工厂方法模式实现了,其工厂类为SubjectFactory,Web服务中具体的工厂类为DefaultWebSubjectFactory,工厂类的继承结构如下:
DefaultSecurityManager的方法doCreateSubject委托给了SubjectFactory来完成:
- protected Subject doCreateSubject(SubjectContext context) {
- return getSubjectFactory().createSubject(context);
- }
具体创建过程这里不做展开,后续在用户登录流程中再进行深入分析;
Subject的CRUD操作是通过SubjectDAO完成的,具体实现类为DefaultSubjectDAO,在DefaultSubjectDAO中,Subject的内部状态是通过存放到Session中完成的;
DefaultSecurityManager的save和delete方法实现内部委托给了成员变量subjectDAO来完成;
- /**
- * Saves the subject's state to a persistent location for future reference if necessary.
- * <p/>
- * This implementation merely delegates to the internal {@link #setSubjectDAO(SubjectDAO) subjectDAO} and calls
- * {@link SubjectDAO#save(org.apache.shiro.subject.Subject) subjectDAO.save(subject)}.
- *
- * @param subject the subject for which state will potentially be persisted
- * @see SubjectDAO#save(org.apache.shiro.subject.Subject)
- * @since 1.2
- */
- protected void save(Subject subject) {
- this.subjectDAO.save(subject);
- }
-
- /**
- * Removes (or 'unbinds') the Subject's state from the application, typically called during {@link #logout}..
- * <p/>
- * This implementation merely delegates to the internal {@link #setSubjectDAO(SubjectDAO) subjectDAO} and calls
- * {@link SubjectDAO#delete(org.apache.shiro.subject.Subject) delete(subject)}.
- *
- * @param subject the subject for which state will be removed
- * @see SubjectDAO#delete(org.apache.shiro.subject.Subject)
- * @since 1.2
- */
- protected void delete(Subject subject) {
- this.subjectDAO.delete(subject);
- }
在用户登录成功之后,可以通过RememberMeManager记录用户登录信息,比如登录用户名称,内部是通过记录Cookie的方法实现的,具体过程后续分析用户登录认证过程时再展开分析;
RememberMeManager通过如下的方法对用户登录成功、登录失败、退出时进行拦截,并进行相应后处理操作;
- protected void rememberMeSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) {
- RememberMeManager rmm = getRememberMeManager();
- if (rmm != null) {
- try {
- 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 {
- 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 {
- 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);
- }
- }
- }
- }
DefaultWebSecurityManager继承自DefaultSecurityManager,同时实现了WebSecurityManager接口,标识session模式是http模式(Servlet管理sessiion),非native模式(Shiro管理session),
同时初始化了成员变量的具体实现类,包括DefaultWebSubjectFactory、CookieRememberMeManager、ServletContainerSessionManager,如下:
- public DefaultWebSecurityManager() {
- super();
- ((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
- this.sessionMode = HTTP_SESSION_MODE;
- setSubjectFactory(new DefaultWebSubjectFactory());
- setRememberMeManager(new CookieRememberMeManager());
- setSessionManager(new ServletContainerSessionManager());
- }
至此,SecurityManager的内容解析完毕,可以看到在不同的继承层次上为SecurityManager聚合了不同的功能,层次清楚、职责分明,通过关注点分离的方式满足了“单一职责”的设计原则,是个很好的借鉴。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。