Shiro 系列 - 基本知识

 和 Spring Security 项目一样, Apache Shiro 也是一个被广泛使用安全框架, 它们都能完成认证、授权、会话管理等. 简单对比一下 Apache Shiro 和 Spring Security:

1. 适用范围: Spring Security 必须和 Spring MVC 一起使用, 而 Shiro 是一个通用的安全框架, 可以用在 Spring MVC 或 JavaEE/JavaSE 环境中.
2. 复杂程度: Spring Security 是一个非常复杂的架构, 虽然搭配 Spring Boot 后, 使用的难度小了些, 但是还是很复杂. Shiro  概念少, 使用也简单了很多.
3. 版本变动: Spring Security 变化很频繁, 网上教程多是基于 Spring Boot 1.x 的, 在 Spring Boot 2.x 下很多都不适用了. Shiro 从 2010 年发展到现在, 功能和代码已经相当成熟了, 几乎不会有大的变动.
4. 功能多少方面, 方面: Spring Security 内置支持 CSRF 保护, Shiro 没有开箱即用, 基于 Shiro 的项目 buji-pac4j 实现了 CSRF 保护.


==========================
Shiro 的几个概念
==========================
Subject: Subject 代表当前登录系统的主体对象, 比如 username 或 appId.
Realm: 负责提供认证和授权的信息源, 数据源可以来源 Jdbc 数据库或其他.
SecurityManager: Shiro 的核心, 我们需要将 Realm 注入到 SecurityManager, 然后 SecurityManager 为我们提供授权功能. SecurityManager 同时也是内部各个组件的组装器, 如果我们要实现自己的 Cache 或 Session 管理器, 也是通过 SecurityManager 注入.
SecurityManager的两个实现类:
1. DefaultSecurityManager: 用于 JavaSE 环境
2. DefaultWebSecurityManager: 用于 Web 环境的实现, 对应底层使用的是 ServletContainerSessionManager 类 Session 管理器.

 

--------------------------
内部架构:
--------------------------
Authenticator: 认证器, 内置一个实现类是 ModularRealmAuthenticator, ModularRealmAuthenticator 默认的认证策略是 AtLeastOneSuccessfulStrategy, 在这个策略下, 如果我们提供了多个 Realm, 只要一个 Realm 认证通过, 即表示最终认证通过. 可选的认证策略有: AllSuccessfulStrategy(全部认证通过), AtLeastOneSuccessfulStrategy(至少一个认证通过), FirstSuccessfulStrategy (首个认证通过).

Authorizer: 授权器, 或者访问控制器, 用来决定主体是否有权限对某个资源有操作权限.

Cache manager, 用来管理用户、角色、权限缓存, 通常这些配置信息不会频繁改变, 使用 cache 可以提高性能. Shiro 不提供 cache DAO 实现, 如果我们要使用 cache 功能, 需要自己实现.

Session Manager, Shiro 使用 session 来管理 subject 的生命周期, 包括: 创建/停止/过期 等等, shiro 内置了三个实现:
1. DefaultSessionManager, DefaultSecurityManager 使用的默认 Session Manager,用于 JavaSE 环境.
2. ServletContainerSessionManager, DefaultWebSecurityManager 使用的默认 Session Manager,用于 Web 环境,其直接使用 Servlet 容器的会话.
3. DefaultWebSessionManager, 用于 Web 环境的实现,可以替代 ServletContainerSessionManager, 自己维护或持久化会话.

 


--------------------------
认证和赋权相关几个术语:
--------------------------
AuthenticationInfo 接口: 认证通过的身份和凭证. 在 realm 类中, 需要实现一个 doGetAuthenticationInfo(AuthenticationToken) 方法, 在该方法中, 先验证用户密码是否 OK, 验证通过后需要返回 AuthenticationInfo 接口的实现, 通常使用的实现类是 SimpleAccount 或 SimpleAuthenticationInfo. SimpleAccount 和 SimpleAuthenticationInfo 中包含着 Principal[身份] 和 Credentials[凭证] 信息.

 

Principal[身份] 和 Credentials[凭证]  : Principal 基本等同于 Username(在shiro相关方法中, 有的 Principa 对象保存着字符串, 有的保存着 我们自定义的User 对象, 需要格外注意 ), Credentials 往往等同于 password, 需要说明的是, Shiro 支持使用多个 realm 作为认证数据源, 所以一个 subject 有可能会有多个 Principal 的. Principal 是通过验证后才被创建的对象, 可以通过 subject 对象获取 Principal.

AuthorizationInfo 接口: 包含用户所属的角色和具备的权限. 在 realm 类中, 需要实现一个 doGetAuthorizationInfo(PrincipalCollection principals) 方法.

AuthenticationToken: 是用来提交验证的 token 值, 经常使用的实现类是 UsernamePasswordToken.

 


====================================
RunAs 功能
====================================
RunAs 是一个免登录身份切换, 有两个主要的工作场景: 一是后台超级管理员切换到其他用户, 用户debug; 二是方便实现用户代理, 比如秘书代理老板的工作.
主要有4分方法:
1. subject.runAs(new SimplePrincipalCollection(newUsername, MyShiroRealm.class.getSimpleName()));
切换用户身份, 注意: runAs() 并不会对 newUsername 进行任何Authenticate/authorization方面的验证, 需要自行验证.
2. Subject.isRunAs(), 判断当前 subject 是否是在 RunAs 模式下.
3. Subject.getPreviousPrincipals(); 得到切换身份之前的身份,一个用户可以切换很多次身份,之前的身份使用栈数据结构来存储.
4. Subject.releaseRunAs() 切换回之前的身份.

 

 

==========================
使用 Shiro 的一般过程是:
==========================
1. 创建 SecurityManager 对象
    对于 Web 项目可以直接 new DefaultWebSecurityManager 子类, ini 文件的realm也可以使用 IniSecurityManagerFactory 工厂方法创建.
2. 在程序中注入 SecurityManager 对象,
    对于 Spring 项目, 需要定义一个 ShiroFilterFactoryBean bean, 然后通过该bean来注册 SecurityManager, shiroFilterFactoryBean.setSecurityManager(securityManager) .
    对于其他项目, 可以使用 SecurityUtils.setSecurityManager(securityManager) 注入.
3. 进行资源授权配置.
    对于 Spring 项目, 可以用 ShiroFilterFactoryBean bean来设置拦截器;
    也可以在 ini 文件的 url 节中定义资源授权配置.
4. 定义 Realm, 实现 doGetAuthenticationInfo(AuthenticationToken) 和 doGetAuthorizationInfo() 方法. doGetAuthenticationInfo( authcToken)
     对于 JDBC , 一般是 user/role/permission 三张表, 以及 user_role/role_permssion 两个关系表.
    也可以在 Ini 文件中定义, 基本用作demo.
5. 使用 SecurityUtils.getSubject() 获取 subject, 然后调用 subject.login(), 该方法将触发 realm 的 doGetAuthenticationInfo(AuthenticationToken authcToken) 方法, 进行身份验证
6. 在进行权限验证的时候, 会触发 realm 的 doGetAuthorizationInfo(PrincipalCollection principals) 函数, 由该函数进行角色和权限的验证.

posted @ 2018-11-18 11:21  harrychinese  阅读(1096)  评论(0编辑  收藏  举报