Shiro框架:Shiro SecurityManager安全管理器解析

打印 上一主题 下一主题

主题 968|帖子 968|积分 2906

目次
1. SecurityManager先容
1.1 Authenticator
1.2 Authorizer
1.3 SessionManager
2. DefaultWebSecurityManager解析
2.1 Destroyable
2.2 CacheManagerAware
2.3 EventBusAware
2.4 CachingSecurityManager(聚合缓存管理和事件监听管理功能)
2.5 RealmSecurityManager(聚合Realm管理功能)
2.6 AuthenticatingSecurityManager(聚合登录认证功能)
2.7 AuthorizingSecurityManager(聚合访问控制功能)
2.8 SessionsSecurityManager(聚合Session管理功能)
2.9 DefaultSecurityManager
2.9.1 创建用户(Subject)功能
2.9.2 存储用户(Subject)功能
2.9.3 用户RememberMe管理功能
2.10 DefaultWebSecurityManager

Shiro作为一款比较流行的登录认证、访问控制安全框架,被广泛应用在程序员社区;Shiro登录验证、访问控制、Session管理等流程内部都是委托给SecurityManager安全管理器来完成的,在前述文章全面解析Shiro框架原理的底子之上,详见:Shiro框架:ShiroFilterFactoryBean过滤器源码解析-CSDN博客、Shiro框架:Shiro内置过滤器源码解析-CSDN博客,本篇文章继承深入解析Shiro SecurityManager安全管理器的结构和功能,为后续各处理流程的解析做好铺垫。
   假如想要对用户登录认证、用户访问控制鉴权流程有更深条理的理解,请移步:
  Shiro框架:Shiro用户登录认证流程源码解析
  Shiro框架:Shiro用户访问控制鉴权流程-内置过滤器方式源码解析
  Shiro框架:Shiro用户访问控制鉴权流程-Aop注解方式源码解析
  1. SecurityManager先容

SecurityManager接口类图如下:

可以看到SecurityManager实际上整合了3部门功能:
   

  • Authenticator:登录认证器
  • Authorizer:访问控制器
  • SessionManager:Session管理器
  同时SecurityManager也定义了登录、退出以及创建用户的接口功能,如下:
  1. public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
  2.     /**
  3.      * Logs in the specified Subject using the given {@code authenticationToken}, returning an updated Subject
  4.      * instance reflecting the authenticated state if successful or throwing {@code AuthenticationException} if it is
  5.      * not.
  6.      */
  7.     Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;
  8.     /**
  9.      * Logs out the specified Subject from the system.
  10.      */
  11.     void logout(Subject subject);
  12.     /**
  13.      * Creates a {@code Subject} instance reflecting the specified contextual data.
  14.      */
  15.     Subject createSubject(SubjectContext context);
  16. }
复制代码
1.1 Authenticator

登录认证器,定义了用户登录认证方法,交由子类详细实现,如下:
  1. public interface Authenticator {
  2.     /**
  3.      * Authenticates a user based on the submitted {@code AuthenticationToken}.
  4.      */
  5.     public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
  6.             throws AuthenticationException;
  7. }
复制代码
1.2 Authorizer

用户鉴权器,引入了鉴权方法对登录用户执行鉴权操作,交由子类详细实现;
Authorizer定义如下:
  1. public interface Authorizer {
  2.     /**
  3.      * Returns <tt>true</tt> if the corresponding subject/user is permitted to perform an action or access a resource
  4.      */
  5.     boolean isPermitted(PrincipalCollection principals, String permission);
  6.     /**
  7.      * Ensures the corresponding Subject/user implies the specified permission String.
  8.      */
  9.     void checkPermission(PrincipalCollection subjectPrincipal, String permission) throws AuthorizationException;
  10.     /**
  11.      * Returns <tt>true</tt> if the corresponding Subject/user has the specified role, <tt>false</tt> otherwise.
  12.      */
  13.     boolean hasRole(PrincipalCollection subjectPrincipal, String roleIdentifier);
  14.     /**
  15.      * Asserts the corresponding Subject/user has the specified role by returning quietly if they do or throwing an
  16.      * {@link AuthorizationException} if they do not.
  17.      */
  18.     void checkRole(PrincipalCollection subjectPrincipal, String roleIdentifier) throws AuthorizationException;
  19.     //其它鉴权方法
  20. }
复制代码
1.3 SessionManager

Session管理器,提供了创建Session以及获取Session操作方法:
  1. public interface SessionManager {
  2.     /**
  3.      * Starts a new session based on the specified contextual initialization data, which can be used by the underlying
  4.      * implementation to determine how exactly to create the internal Session instance.
  5.      * <p/>
  6.      * This method is mainly used in framework development, as the implementation will often relay the argument
  7.      * to an underlying {@link SessionFactory} which could use the context to construct the internal Session
  8.      * instance in a specific manner.  This allows pluggable {@link org.apache.shiro.session.Session Session} creation
  9.      * logic by simply injecting a {@code SessionFactory} into the {@code SessionManager} instance.
  10.      */
  11.     Session start(SessionContext context);
  12.     /**
  13.      * Retrieves the session corresponding to the specified contextual data (such as a session ID if applicable), or
  14.      * {@code null} if no Session could be found.  If a session is found but invalid (stopped or expired), a
  15.      * {@link SessionException} will be thrown.
  16.      */
  17.     Session getSession(SessionKey key) throws SessionException;
  18. }
复制代码
2. DefaultWebSecurityManager解析

在Web服务中,Shiro中应用的SecurityManager详细为子类DefaultWebSecurityManager,为了对其有足够的理解,下面对DefaultWebSecurityManager类继承条理中相关类进行解析说明,其类继承结构如下:

2.1 Destroyable

Shiro框架自定义了Destroyable接口,引入了destroy方法,支持销毁操作:
  1. public interface Destroyable {
  2.     void destroy() throws Exception;
  3. }
复制代码
2.2 CacheManagerAware

子类实现支持注入缓存管理器,进而获取构造缓存实例,执行缓存操作;
  1. public interface CacheManagerAware {
  2.     /**
  3.      * Sets the available CacheManager instance on this component.
  4.      *
  5.      * @param cacheManager the CacheManager instance to set on this component.
  6.      */
  7.     void setCacheManager(CacheManager cacheManager);
  8. }
复制代码
2.3 EventBusAware

子类对象支持注入EventBus对象,雷同Guava EventBus,引入事件机制,可以发布事件(publish),进而调用已注册(register)的事件监听器;
  1. public interface EventBusAware {
  2.     /**
  3.      * Sets the available {@code EventBus} that may be used for publishing and subscribing to/from events.
  4.      *
  5.      * @param bus the available {@code EventBus}.
  6.      */
  7.     void setEventBus(EventBus bus);
  8. }
复制代码
  1. public interface EventBus {
  2.     void publish(Object event);
  3.     void register(Object subscriber);
  4.     void unregister(Object subscriber);
  5. }
复制代码
2.4 CachingSecurityManager(聚合缓存管理和事件监听管理功能)

同时实现了Destroyable, CacheManagerAware, EventBusAware,支持注入成员变量cacheManager和eventBus,并定义了销毁操作,主要实现如下:
  1. public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware, EventBusAware {
  2.     /**
  3.      * The CacheManager to use to perform caching operations to enhance performance.  Can be null.
  4.      */
  5.     private CacheManager cacheManager;
  6.     /**
  7.      * The EventBus to use to use to publish and receive events of interest during Shiro's lifecycle.
  8.      * @since 1.3
  9.      */
  10.     private EventBus eventBus;
  11.     /**
  12.      * Default no-arg constructor that will automatically attempt to initialize a default cacheManager
  13.      */
  14.     public CachingSecurityManager() {
  15.         //use a default event bus:
  16.         setEventBus(new DefaultEventBus());
  17.     }
  18.     /**
  19.      * Destroys the {@link #getCacheManager() cacheManager} via {@link LifecycleUtils#destroy LifecycleUtils.destroy}.
  20.      */
  21.     public void destroy() {
  22.         LifecycleUtils.destroy(getCacheManager());
  23.         this.cacheManager = null;
  24.         LifecycleUtils.destroy(getEventBus());
  25.         this.eventBus = new DefaultEventBus();
  26.     }
  27. }
复制代码
2.5 RealmSecurityManager(聚合Realm管理功能)

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主要实现如下: 
  1. public abstract class RealmSecurityManager extends CachingSecurityManager {
  2.     /**
  3.      * Internal collection of <code>Realm</code>s used for all authentication and authorization operations.
  4.      */
  5.     private Collection<Realm> realms;
  6.     public void setRealms(Collection<Realm> realms) {
  7.         if (realms == null) {
  8.             throw new IllegalArgumentException("Realms collection argument cannot be null.");
  9.         }
  10.         if (realms.isEmpty()) {
  11.             throw new IllegalArgumentException("Realms collection argument cannot be empty.");
  12.         }
  13.         this.realms = realms;
  14.         afterRealmsSet();
  15.     }
  16.     protected void afterRealmsSet() {
  17.         applyCacheManagerToRealms();
  18.         applyEventBusToRealms();
  19.     }
  20.     public void destroy() {
  21.         LifecycleUtils.destroy(getRealms());
  22.         this.realms = null;
  23.         super.destroy();
  24.     }
  25. }
复制代码
2.6 AuthenticatingSecurityManager(聚合登录认证功能

AuthenticatingSecurityManager注入成员变量authenticator(默认初始化的实例是ModularRealmAuthenticator)并进行初始化设置(注入认证组件Realm),同时实现了接口Authenticator定义的authenticate方法,内部委托给成员变量authenticator,如下:
  1.     private Authenticator authenticator;
  2.     /**
  3.      * Default no-arg constructor that initializes its internal
  4.      * <code>authenticator</code> instance to a
  5.      * {@link org.apache.shiro.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator}.
  6.      */
  7.     public AuthenticatingSecurityManager() {
  8.         super();
  9.         this.authenticator = new ModularRealmAuthenticator();
  10.     }
  11.     /**
  12.      * Passes on the {@link #getRealms() realms} to the internal delegate <code>Authenticator</code> instance so
  13.      * that it may use them during authentication attempts.
  14.      */
  15.     protected void afterRealmsSet() {
  16.         super.afterRealmsSet();
  17.         if (this.authenticator instanceof ModularRealmAuthenticator) {
  18.             ((ModularRealmAuthenticator) this.authenticator).setRealms(getRealms());
  19.         }
  20.     }
  21.     public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
  22.         return this.authenticator.authenticate(token);
  23.     }
复制代码
2.7 AuthorizingSecurityManager(聚合访问控制功能)

和AuthenticatingSecurityManager雷同,这里注入了成员变量authorizer来实现访问控制的鉴权功能,authorizer的默认实现是ModularRealmAuthorizer,同样也对authorizer进行了初始化设置,注入Reaml,并详细实现了接口Authorizer引入的访问控制方法,如下:
  1.     /**
  2.      * The wrapped instance to which all of this <tt>SecurityManager</tt> authorization calls are delegated.
  3.      */
  4.     private Authorizer authorizer;
  5.     /**
  6.      * Default no-arg constructor that initializes an internal default
  7.      * {@link org.apache.shiro.authz.ModularRealmAuthorizer ModularRealmAuthorizer}.
  8.      */
  9.     public AuthorizingSecurityManager() {
  10.         super();
  11.         this.authorizer = new ModularRealmAuthorizer();
  12.     }
  13.     protected void afterRealmsSet() {
  14.         super.afterRealmsSet();
  15.         if (this.authorizer instanceof ModularRealmAuthorizer) {
  16.             ((ModularRealmAuthorizer) this.authorizer).setRealms(getRealms());
  17.         }
  18.     }
  19.     public boolean isPermitted(PrincipalCollection principals, String permissionString) {
  20.         return this.authorizer.isPermitted(principals, permissionString);
  21.     }
复制代码
2.8 SessionsSecurityManager(聚合Session管理功能)

内部注入了成员变量sessionManagerSession管理器,实现了session创建、session获取的方法:
  1.     /**
  2.      * The internal delegate <code>SessionManager</code> used by this security manager that manages all the
  3.      * application's {@link Session Session}s.
  4.      */
  5.     private SessionManager sessionManager;
  6.     /**
  7.      * Default no-arg constructor, internally creates a suitable default {@link SessionManager SessionManager} delegate
  8.      * instance.
  9.      */
  10.     public SessionsSecurityManager() {
  11.         super();
  12.         this.sessionManager = new DefaultSessionManager();
  13.         applyCacheManagerToSessionManager();
  14.     }
  15.    
  16.     public Session start(SessionContext context) throws AuthorizationException {
  17.         return this.sessionManager.start(context);
  18.     }
  19.     public Session getSession(SessionKey key) throws SessionException {
  20.         return this.sessionManager.getSession(key);
  21.     }
复制代码
2.9 DefaultSecurityManager

DefaultSecurityManager主要聚合了3部门功能,这点可以从其包含的成员变量看出,下面进行分别说明:
  1.     protected RememberMeManager rememberMeManager;
  2.     protected SubjectDAO subjectDAO;
  3.     protected SubjectFactory subjectFactory;
  4.     /**
  5.      * Default no-arg constructor.
  6.      */
  7.     public DefaultSecurityManager() {
  8.         super();
  9.         this.subjectFactory = new DefaultSubjectFactory();
  10.         this.subjectDAO = new DefaultSubjectDAO();
  11.     }
复制代码
2.9.1 创建用户(Subject)功能

Subject创建过程是通过工厂方法模式实现了,其工厂类为SubjectFactory,Web服务中详细的工厂类为DefaultWebSubjectFactory,工厂类的继承结构如下:

DefaultSecurityManager的方法doCreateSubject委托给了SubjectFactory来完成:
  1.     protected Subject doCreateSubject(SubjectContext context) {
  2.         return getSubjectFactory().createSubject(context);
  3.     }
复制代码
详细创建过程这里不做睁开,后续在用户登录流程中再进行深入分析;
2.9.2 存储用户(Subject)功能

Subject的CRUD操作是通过SubjectDAO完成的,详细实现类为DefaultSubjectDAO,在DefaultSubjectDAO中,Subject的内部状态是通过存放到Session中完成的;
DefaultSecurityManager的save和delete方法实现内部委托给了成员变量subjectDAO来完成;
  1.     /**
  2.      * Saves the subject's state to a persistent location for future reference if necessary.
  3.      * <p/>
  4.      * This implementation merely delegates to the internal {@link #setSubjectDAO(SubjectDAO) subjectDAO} and calls
  5.      * {@link SubjectDAO#save(org.apache.shiro.subject.Subject) subjectDAO.save(subject)}.
  6.      *
  7.      * @param subject the subject for which state will potentially be persisted
  8.      * @see SubjectDAO#save(org.apache.shiro.subject.Subject)
  9.      * @since 1.2
  10.      */
  11.     protected void save(Subject subject) {
  12.         this.subjectDAO.save(subject);
  13.     }
  14.     /**
  15.      * Removes (or 'unbinds') the Subject's state from the application, typically called during {@link #logout}..
  16.      * <p/>
  17.      * This implementation merely delegates to the internal {@link #setSubjectDAO(SubjectDAO) subjectDAO} and calls
  18.      * {@link SubjectDAO#delete(org.apache.shiro.subject.Subject) delete(subject)}.
  19.      *
  20.      * @param subject the subject for which state will be removed
  21.      * @see SubjectDAO#delete(org.apache.shiro.subject.Subject)
  22.      * @since 1.2
  23.      */
  24.     protected void delete(Subject subject) {
  25.         this.subjectDAO.delete(subject);
  26.     }
复制代码
2.9.3 用户RememberMe管理功能

在用户登录成功之后,可以通过RememberMeManager记载用户登录信息,比如登录用户名称,内部是通过记载Cookie的方法实现的,详细过程后续分析用户登录认证过程时再睁开分析;
RememberMeManager通过如下的方法对用户登录成功、登录失败、退出时进行拦截,并进行相应后处理操作;
  1.     protected void rememberMeSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) {
  2.         RememberMeManager rmm = getRememberMeManager();
  3.         if (rmm != null) {
  4.             try {
  5.                 rmm.onSuccessfulLogin(subject, token, info);
  6.             } catch (Exception e) {
  7.                 if (log.isWarnEnabled()) {
  8.                     String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() +
  9.                             "] threw an exception during onSuccessfulLogin.  RememberMe services will not be " +
  10.                             "performed for account [" + info + "].";
  11.                     log.warn(msg, e);
  12.                 }
  13.             }
  14.         } else {
  15.             if (log.isTraceEnabled()) {
  16.                 log.trace("This " + getClass().getName() + " instance does not have a " +
  17.                         "[" + RememberMeManager.class.getName() + "] instance configured.  RememberMe services " +
  18.                         "will not be performed for account [" + info + "].");
  19.             }
  20.         }
  21.     }
  22.     protected void rememberMeFailedLogin(AuthenticationToken token, AuthenticationException ex, Subject subject) {
  23.         RememberMeManager rmm = getRememberMeManager();
  24.         if (rmm != null) {
  25.             try {
  26.                 rmm.onFailedLogin(subject, token, ex);
  27.             } catch (Exception e) {
  28.                 if (log.isWarnEnabled()) {
  29.                     String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() +
  30.                             "] threw an exception during onFailedLogin for AuthenticationToken [" +
  31.                             token + "].";
  32.                     log.warn(msg, e);
  33.                 }
  34.             }
  35.         }
  36.     }
  37.     protected void rememberMeLogout(Subject subject) {
  38.         RememberMeManager rmm = getRememberMeManager();
  39.         if (rmm != null) {
  40.             try {
  41.                 rmm.onLogout(subject);
  42.             } catch (Exception e) {
  43.                 if (log.isWarnEnabled()) {
  44.                     String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() +
  45.                             "] threw an exception during onLogout for subject with principals [" +
  46.                             (subject != null ? subject.getPrincipals() : null) + "]";
  47.                     log.warn(msg, e);
  48.                 }
  49.             }
  50.         }
  51.     }
复制代码
2.10 DefaultWebSecurityManager

DefaultWebSecurityManager继承自DefaultSecurityManager,同时实现了WebSecurityManager接口,标识session模式是http模式(Servlet管理sessiion),非native模式(Shiro管理session),
同时初始化了成员变量的详细实现类,包罗DefaultWebSubjectFactory、CookieRememberMeManager、ServletContainerSessionManager,如下:
  1.     public DefaultWebSecurityManager() {
  2.         super();
  3.         ((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
  4.         this.sessionMode = HTTP_SESSION_MODE;
  5.         setSubjectFactory(new DefaultWebSubjectFactory());
  6.         setRememberMeManager(new CookieRememberMeManager());
  7.         setSessionManager(new ServletContainerSessionManager());
  8.     }
复制代码
至此,SecurityManager的内容解析完毕,可以看到在不同的继承条理上为SecurityManager聚合了不同的功能,条理清楚、职责分明,通过关注点分离的方式满意了“单一职责”的设计原则,是个很好的鉴戒。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用多少眼泪才能让你相信

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表