Shiro系统性介绍

By | 2023年8月18日

Shiro系统性介绍

Shiro是一个使用简单但是功能又强大的Java安全框架,它提供了认证、授权、会话管理等主要功能。

Shiro使用过程中有两个顶级配置对象 SecurityManagerShiroFilterFactoryBean。 其他配置对象都是这两类对象的延伸。

一、 术语介绍

它有几个重要的概念。

1. Subject

SubjectShiro中是一个核心概念。它对于Shiro来讲就是一个用户的概念,这个用户可以是登录人也可以是其他访问的服务器等等。

之所以没有叫User,是因为其他很多框架都有这个概念了,防止冲突和混淆。

2. Principals

是一个 Subject的标识属性,是一个能代表 Subject的属性,可以是登录名、手机号等等

3. Realms

Shiro框架中的 DAO角色,负责从后台数据库获取对象数据。比如从数据库中获取用户信息、权限信息等等。

二、 认证过程(Authenticate)

认证过程主要分为三步:

  1. 收集Subject的Principals和credentials信息
    比如登录名和密码
  2. 将收集到的信息提交给认证系统
  3. 经过认证,如果成功则返回用户的详细信息,如果失败则拒绝访问

1. 收集信息

收集到的信息以 token的形式存在。
最简单和最常用的收集方式就是通过用户名和密码来完成。 UsernamePasswordToken是一个封装好的用户名密码token对象

UsernamePasswordToken token = new UsernamePasswordToken( username, password );

2. 提交token信息进行认证

认证系统通过Realms来获取后台数据并进行认证

//With most of Shiro, you'll always want to make sure you're working with the currently
//executing user, referred to as the subject
Subject currentUser = SecurityUtils.getSubject();

//Authenticate the subject by passing
//the user name and password token
//UsernamePasswordToken token = new UsernamePasswordToken( username, password );
//into the login method
currentUser.login(token);

首先,需要获取一个代表当前用户的对象 SecurityUtils.getSubject()。没有认证前身份是匿名用户(anonymous),并且没有标识信息与其关联。
然后,通过 currentUser.login(token);进行认证,如果认证通过了会把当前用户信息关联到上步获取到的 Subject上。

3. 处理认证结果

认证结果可能成功也可能失败,如果失败了则直接拒绝访问。如果成功了则从后台查询用户信息和权限信息并关联到 Subject

登录失败异常情况:

  1. UnknownAccountException
  2. IncorrectCredentialsException
  3. LockedAccountException
  4. ExcessiveAttemptsException
  5. AuthenticationException

等等

三、 SecurityManager

顶级配置对象。SecurityManager 安全管理器是Shiro系统的核心框架内容接口。这也代表了Shiro的能力内容

  1. 他本身提供了三个方法
    1. login 登录
    2. logout 登出
    3. createSubject 创建Subject
  2. 继承Authenticator(验证器)
    1. authenticate 对给定Token进行验证的方法
  3. 继承Authorizer(访问控制器)
    1. isPermitted
    2. checkPermission
    3. hasRole
    4. checkRole
  4. 继承SessionManager(会话管理)
    1. start
    2. getSession
public interface SecurityManager extends Authenticator, Authorizer, SessionManager {

    Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;
    void logout(Subject subject);
    Subject createSubject(SubjectContext context);
}
public interface Authenticator {
    public AuthenticationInfo authenticate(AuthenticationToken authenticationToken) throws AuthenticationException;
}
public interface Authorizer {
    boolean isPermitted(PrincipalCollection principals, String permission);
    boolean isPermitted(PrincipalCollection subjectPrincipal, Permission permission);
    boolean[] isPermitted(PrincipalCollection subjectPrincipal, String... permissions);
    boolean[] isPermitted(PrincipalCollection subjectPrincipal, List<Permission> permissions);
    boolean isPermittedAll(PrincipalCollection subjectPrincipal, String... permissions);
    boolean isPermittedAll(PrincipalCollection subjectPrincipal, Collection<Permission> permissions);
    void checkPermission(PrincipalCollection subjectPrincipal, String permission) throws AuthorizationException;
    void checkPermission(PrincipalCollection subjectPrincipal, Permission permission) throws AuthorizationException;
    void checkPermissions(PrincipalCollection subjectPrincipal, String... permissions) throws AuthorizationException;
    void checkPermissions(PrincipalCollection subjectPrincipal, Collection<Permission> permissions) throws AuthorizationException;
    boolean hasRole(PrincipalCollection subjectPrincipal, String roleIdentifier);
    boolean[] hasRoles(PrincipalCollection subjectPrincipal, List<String> roleIdentifiers);
    boolean hasAllRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers);
    void checkRole(PrincipalCollection subjectPrincipal, String roleIdentifier) throws AuthorizationException;
    void checkRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers) throws AuthorizationException;
    void checkRoles(PrincipalCollection subjectPrincipal, String... roleIdentifiers) throws AuthorizationException;
}
public interface SessionManager {
    Session start(SessionContext context);
    Session getSession(SessionKey key) throws SessionException;
}

默认实现

SecurityManager提供了默认实现类 DefaultWebSecurityManager。我们通常利用这个默认实现来完成Shiro的功能,它也给出了相关方法来设置具体的对象。

构造函数依赖:

  1. Realm: 创建是需要给定一个Realm的实现类,来完成后台数据的读取

1. Realm

一个Realm接口有下面几个基本的方法:

  1. getName 当前Realm对象的名称,是一个标识
  2. supports 单签Realm对象是否支持指定类型的token
  3. getAuthenticationInfo 验证token
public interface Realm {
    String getName();
    boolean supports(AuthenticationToken token);
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
}

不过在实际使用过程中会使用它的某个子实现,是在基本方法的基础上扩展了若干功能,比如cache的能力、访问控制的能力、角色解析、权限解析等能力。我们根据实际需求实现不同能力层级的对象即可。
比如 AuthorizingRealm就是一个常用的实现子类

公开方法:

  1. public void setSessionManager(SessionManager sessionManager)

2. SessionManager

一个完整的企业级Session管理框架,有如下特点

  1. POJO/J2SE based (IoC friendly)
    在Shiro中一切都是面向接口和POJO实现的。这就意味着你可以很方便的定义其中的某些对象以及扩展这些对象
  2. Easy Custom Session Storage
    容易定制的Session存储机制。因为Shiro中的对象是基于Pojo,所以这些对象可以很方便的存储在各种类型的数据存储容器中。比如文件系统、MemCache等内存系统、关系型数据库等等
  3. Container-Independent Clustering
    方便定制会话集群, like Ehcache + Terracotta, Coherence, GigaSpaces等等
  4. Heterogeneous Client Access
    支持各种客户端访问,比如web用户、手机端用户、服务器用户等等,都可以很方便的访问同一个Session
  5. Event Listeners
    运行监听会话过程中的各个生命周期
  6. Host Address Retention
    主机地址存储,Session是保留会话创建这的IP和hostname的,因此可以方便的进行相关业务扩展
  7. Inactivity/Expiration Support
    支持会话过期
  8. Transparent Web Use
    支持 Servlet 2.5的会话系统,你可以基于现有的Session使用Shiro的Session管理
  9. Can be used for SSO
    可以用于SSO。
2.1配置

SessionManager有一个默认的实现类 DefaultWebSessionManager,可以通过这个类配置相关的行为

公开方法:

  1. setSessionIdCookieEnabled 是否启用Cookie
  2. setSessionIdCookie 设置Cookie对象
    1. Cookie
      1. void setName(String name);
      2. void setValue(String value);
      3. void setComment(String comment);
      4. void setDomain(String domain);
      5. void setMaxAge(int maxAge);
      6. void setPath(String path);
      7. void setSecure(boolean secure);
      8. void setVersion(int version);
      9. boolean isHttpOnly();
  3. 继承DefaultSessionManager
    1. sessionDAO: 构造参数,session的存储方式对象
    2. setSessionDAO
    3. setSessionFactory
    4. setCacheManager
    5. 继承AbstractValidatingSessionManager
      1. setSessionValidationSchedulerEnabled 是否启用定时Session失效
      2. setSessionValidationScheduler 设置失效策略对象
      3. setSessionValidationInterval 设置失效时间
      4. 继承AbstractNativeSessionManager
        1. setSessionListeners 设置Session事件监听器
        2. setEventBus 设置事件总线
        3. setTimeout(SessionKey key, long maxIdleTimeInMillis) 设置指定Session失效时间
        4. setAttribute(SessionKey sessionKey, Object attributeKey, Object value) 设置session属性
        5. 继承AbstractSessionManager
        6. setGlobalSessionTimeout 设置全局失效时间
        7. 继承NativeSessionManager
        8. getTimeout(SessionKey key)
        9. getLastAccessTime(SessionKey key)
        10. 等等
2.2 使用Session
Subject currentUser = SecurityUtils.getSubject();

Session session = currentUser.getSession();
session.setAttribute( "someKey", someValue);

在获取Session时,如果Subject已经关联了一个Session那么直接返回,如果没有管理则根据参数觉得新建一个Session还是返回null

五、ShiroFilterFactoryBean

Shiro过滤器,顶级配置对象。用来配置指定url的拦截器,登录成功地址等等

公开方法:

  1. setSecurityManager 设置并关联安全管理框架对象
  2. setLoginUrl 设置登录界面地址,访问无权限是会跳转该地址
  3. setSuccessUrl 设置登录成功跳转地址
  4. setUnauthorizedUrl 设置无权限访问跳转地址
  5. setFilters(Map<String, Filter> filters) 设置filter名称和filter的对应关系Map
  6. setFilterChainDefinitionMap 设置请求路径和对应filte的名称的Map对象
  7. setFilterChainDefinitions 设置请求路径和对应filte的名称
  8. setGlobalFilters 设置全局请求Filter,每个请求都会执行