Files
rikako-note/spring/Apache Shiro/Apache Shiro.md
wu xiangkai e00410e379 日常提交
2022-10-20 18:16:53 +08:00

9.2 KiB
Raw Blame History

Apache Shiro

Shiro简介

Shiro是一个简单易用且功能强大的Java安全框架用于实现认证、授权、加密、session管理等场景并且Shiro可以被用于任何应用包括命令行应用、移动应用、大型web应用或是企业应用。
Shiro在如下方面提供Security API

  • Authentication为用户提供身份认证
  • Authorization为应用提供用户的访问控制
  • 加密:避免应用数据处于明文状态
  • session管理每个用户的时间敏感状态

Shiro中常用的概念

Shiro框架的结构主要分为三个部分Subject、SecurityManager、Realms

Subject

SubjectSubject是一个安全术语通常意味着当前执行的用户。

import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
// 获取Subject对象
Subject currentUser = SecurityUtils.getSubject();

SecurityManager

相对于Subject代表对于当前用户的安全操作SecurityManager代表对所有用户的安全操作。SecurityManager是Shiro结构的核心其由多个security component嵌套组成。

一旦SecurityManager和其嵌套的security component被配置完成那么用户将不再使用SecurityManager而是调用Subject API。

对每个应用中只存在一个SecurityManagerSecurityManager是应用范围内的单例。默认SecurityManager的实现是POJO可以通过java代码、Spring xml、yaml、properties等方式来进行配置。

realms

realms是shiro和应用中security data如账户登录的登录数据或授权管理的权限数据之间的连接器。当Shiro和security data进行交互时shiro会从配置好的一个或者多个realm中获取security data。

在上述情况下realm类似于一个安全数据的特定DAO其封装了到数据源连接的详细信息并且为Shiro提供安全数据。当配置Shiro时必须指定至少一个Realm用于身份认证和权限认证。

Shiro提供了开箱即用的Realm来连接很多种security data source比如LDAP关系型数据库JDBC基于文本配置的data source例如ini或properties文件。可以通过自定义Realm的实现来访问自定义的data source如果默认的Realm不能满足需求。

Authentication

认证过程用于认证用户的身份。认证过程的主要流程如下:

  1. 收集用户的身份标识信息通常称之为主体principal和身份证明通常称之为凭据credentials
  2. 向系统提交主体和凭据
  3. 如果提交的凭据和系统中该主体对应的凭据相同,那么该用户会被认为是“通过认证的”。如果提交的凭据不符,那么该用户会被认为是“认证失败的”。
//  认证流程代码
//1. Acquire submitted principals and credentials:
AuthenticationToken token =
new UsernamePasswordToken(username, password);
//2. Get the current Subject:
Subject currentUser = SecurityUtils.getSubject();

//3. Login:
currentUser.login(token);

当login方法被调用时SecurityMananger将会收到AuthenticationToken并且将其分配到一个或多个已经配置好的Realm中并且让每个Realm执行需要的认证流程。每个Reaml都能对提交的AuthenticaitonToken做出处理。
如果认证过程失败那么会抛出AuthenticationException可以通过捕获该异常来对失败的认证进行处理。

//3. Login:
try {
    currentUser.login(token);
} catch (IncorrectCredentialsException ice) { 
} catch (LockedAccountException lae) { 
}

catch (AuthenticationException ae) {
} 

当用户通过身份认证之后,其被认为是“通过身份认证的”,并且被允许使用应用。但是,通过身份认证并不意味着可以在系统中执行任何操作。通过授权,可以决定用户能够在系统中执行哪些操作。

Authorization

Authorization的实质是访问控制通常用于控制用户在系统中能够使用哪些资源。大多数情况下可以通过role或permission等形式来实现访问控制用户通过分配给他们的角色或者权限来执行操作。通过检查用户的role或者permission系统可以决定将哪些功能暴露给用户。

// 通过如下代码Subject可以实现对用户的role检测
if ( subject.hasRole(administrator) ) {
    //show the Create User button
} else {
    //grey-out the button?
} 

// 通过如下代码,可以实现权限分配而不是角色检测
if ( subject.isPermitted(user:create) ) {
    //show the Create User button
} else {
    //grey-out the button?
} 

权限控制甚至支持非常细粒度的权限,譬如实例级别的权限控制。

// 如下代码检测用户是否拥有删除jsmith用户的权限
if ( subject.isPermitted(user:delete:jsmith) ) {
    //delete the jsmith user
} else {
    //dont delete jsmith
}

和Authentication类似Authorization也会进入SecurityManager并且通过一个或者多个Realm来决定是否允许访问。
根据需要Realm既会响应Authentication过程也会响应Authority过程

Session Management

Apache提供了一致的会话管理在任何类型和架构的程序中都可以使用该Session API从小型的守护进程到大型集群web应用都可以使用。
并且Shiro提供的Session API是容器无关的在任何容器环境下Session API都相同。Shiro结构也支持可插入的session存储可以将session存储在企业缓存、关系型数据库或者nosql中。
Shiro Seesion的另一个好处是Shiro Session可以在不同技术实现的客户端之间进行共享譬如Swing桌面客户端可以和web客户端一起加入同一个应用会话用户同时使用swing客户端和web客户端时很有用

/**
* 用户获取Session
*/
// 如果当前用户存在会话,则获取已存在会话,
// 如果当前用户不存在会话,则创建新的会话
Session session = subject.getSession();
// 接受一个boolean值该boolean值标识如果当前用户不存在会话是否创建一个新的会话
Session session = subject.getSession(boolean create);

Shiro Session可在任何应用中使用

Session session = subject.getSession();
session.getAttribute(key, someValue);
Date start = session.getStartTimestamp();
Date timestamp = session.getLastAccessTime();
session.setTimeout(millis);

Shiro加密

Shiro加密是独立于用户Subject故而Shiro加密可以独立于Subject使用。
Shiro加密主要关注于如下两个模块

  • hash加密又名消息摘要message digest
  • ciphers加密

shiro hash

在Shiro hash中如果想要快速计算文件的MD5值可以通过如下方式快速计算

String hex = new Md5Hash(myFile).toHex(); 

在shiro hash中如果想要对密码进行sha-512进行hash操作并且对结果进行base64编码成可打印字符串可以进行如下操作

String encodedPassword =
    new Sha512Hash(password, salt, count).toBase64();

Shiro框架在很大程度上简化了hash和编码。

Shiro Ciphers

Ciphers是一种算法可以通过指定的key将加密后的数据还原到加密之前不同于hash操作hash操作通常是不可逆的。通常用Ciphers来保证数据传输时的安全防止数据在传输时被窥探。
Shiro通过引入CipherService API来简化加密的流程CipherService是一个简单无状态并且线程安全的API可以对应用数据进行加密或者解密操作。

// Shiro CipherService对数据进行加密操作

AesCipherService cipherService = new AesCipherService();
cipherService.setKeySize(256);
//create a test key:
byte[] testKey = cipherService.generateNewKey();

//encrypt a files bytes:
byte[] encrypted =
    cipherService.encrypt(fileBytes, testKey);

Shiro框架的Web支持

Web Session管理

对于web应用shiro默认情况下其session会使用已经存在的servlet容器session。当使用subject.getSession()或者subject.getSession(boolean)获取session实例时Shiro将会返回一个基于Servlet容器的HttpSession实例来作为Session实例返回值

对于Shiro来说其业务层代码通过subject.getSession来获取Shiro Session实例即使当前运行于Servlet容器业务层代码在与Shiro Session交互时也不知道与其交互的是HttpSession。 故而在使用Shiro Session时其Session是独立与环境的Web应用和非Web应用都可以通过相同的Shiro Session API与Shiro Session进行交互而Shiro Session是否是基于Servlet容器的HttpSession用户是无感知的。

Shiro Native Session

当启用Shiro Native Session之后对于Web应用如果想使用Shiro Session来代替基于Servlet容器的HttpSession无需修改HttpServletRequest.getSession()和HttpSession API为Shiro Session API。
Shiro Session完全实现了Servlet Session的标准以此支持Shiro Session在Web应用中的使用。在使用了Shiro Native Session后任何对HttpServletRequest和HttpSession API的调用都会被Shiro拦截Shiro会用Shiro Native Session API来代理这些请求。

故而当想要在Web环境中使用Shiro Session API时无需变动Web环境先前未使用Shiro Session API时的任何代码。