Files
rikako-note/spring/Apache Shiro/Apache Shiro Realm.md
2023-01-10 21:59:13 +08:00

134 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

- [Apache Shiro Realm](#apache-shiro-realm)
- [Realm简介](#realm简介)
- [Realm配置](#realm配置)
- [Realm Authentication](#realm-authentication)
- [支持Authentication](#支持authentication)
- [处理AuthenticationToken](#处理authenticationtoken)
- [credentials匹配](#credentials匹配)
- [简单比较是否相等](#简单比较是否相等)
- [Hash Credentials](#hash-credentials)
- [通过sha-256算法来生成账户信息](#通过sha-256算法来生成账户信息)
- [指定HashedCredentialsMatcher](#指定hashedcredentialsmatcher)
- [SaltedAuthenticationInfo](#saltedauthenticationinfo)
- [关闭Realm的Authentication](#关闭realm的authentication)
- [Realm Authorization](#realm-authorization)
- [基于role的authorization](#基于role的authorization)
- [基于permission的authorization](#基于permission的authorization)
# Apache Shiro Realm
## Realm简介
Realm是一个组件用来访问针对特定应用的安全数据例如user、role或permission。Realm负责将这些安全信息翻译为Shiro能够理解的格式。
由于大多数数据源都会同时存储authentication和authorization信息故而Realm能够同时执行authentication和authorization操作。
## Realm配置
对于Realm的配置可以在ini文件中进行如下配置
```ini
fooRealm = com.company.foo.Realm
barRealm = com.company.another.Realm
bazRealm = com.company.baz.Realm
; 如下指定的顺序会影响Authentication/Authorization过程中的顺序
securityManager.realms = $fooRealm, $barRealm, $bazRealm
```
## Realm Authentication
### 支持Authentication
在Realm被询问去执行Authentication时首先会调用该Realm的supports方法如果supports方法的返回值是true时getAuthenticationInfo方法才会被调用。
通常情况下Realm会对提交的Token类型进行检测并查看当前Realm是否能对该类型Token进行处理。
### 处理AuthenticationToken
如果Realm支持该提交的Token类型那么Authenticator会调用Realm的getAuthenticationInfo方法该方法代表了通过Realm数据库来进行认证尝试。
该方法会按照如下顺序进行执行:
1. 查看Token中存储的principals信息
2. 根据Token中的principals在data source中查找对应的账户信息
3. 确保提交Token中的credentials和data source中查找出的credentials相匹配
4. 如果credentials匹配那么会将用户账户的信息封装到AuthenticationInfo中并返回
5. 如果credentials不匹配会抛出AuthenticationException
### credentials匹配
为了确保在credentials匹配的过程中该过程是可插入pluggable和可自定义的customizableAuthenticationRealm支持CredentialsMatcher的概念通过CredentialsMatcher来进行credentials的比较。
在从data source中查询到账户数据之后会将其和提交Token中的credentials一起传递给CredentialsMatcher由CredentialsMatcher来判断credentials是否相等。
可以通过如下方式来定义CredentialsMatcher的比较逻辑
```java
Realm myRealm = new com.company.shiro.realm.MyRealm();
CredentialsMatcher customMatcher = new com.company.shiro.realm.CustomCredentialsMatcher();
myRealm.setCredentialsMatcher(customMatcher);
```
```ini
[main]
...
customMatcher = com.company.shiro.realm.CustomCredentialsMatcher
myRealm = com.company.shiro.realm.MyRealm
myRealm.credentialsMatcher = $customMatcher
...
```
### 简单比较是否相等
Shiro中所有开箱即用的Realm其实现都默认使用SimpleCredentialsMatcherSimpleCredentialsMatcher简单会对存储在data source中的principals和提交Token中的credentials进行比较相等操作。
### Hash Credentials
将用户提交的principals不做任何转换直接存储在data source中是一种不安全的做法通常是将其进行单向hash之后再存入数据库。
这样可以确保用户的credentials不会以raw text的方式存储再data source中即使数据库数据被泄露用户credentials的原始值也不会被任何人知道。
为了支持Token中credentials和data source中hash之后credentials的比较Shiro提供了HashedCredentialsMatcher实现可以通过配置HashedCredentialsMatcher来取代SimpleCredentialsMatcher。
#### 通过sha-256算法来生成账户信息
```java
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
...
//We'll use a Random Number Generator to generate salts. This
//is much more secure than using a username as a salt or not
//having a salt at all. Shiro makes this easy.
//
//Note that a normal app would reference an attribute rather
//than create a new RNG every time:
RandomNumberGenerator rng = new SecureRandomNumberGenerator();
Object salt = rng.nextBytes();
//Now hash the plain-text password with the random salt and multiple
//iterations and then Base64-encode the value (requires less space than Hex):
String hashedPasswordBase64 = new Sha256Hash(plainTextPassword, salt, 1024).toBase64();
User user = new User(username, hashedPasswordBase64);
//save the salt with the new account. The HashedCredentialsMatcher
//will need it later when handling login attempts:
user.setPasswordSalt(salt);
userDAO.create(user);
```
#### 指定HashedCredentialsMatcher
可以通过如下方式来指定特定HashedCredentialsMatcher实现类。
```ini
[main]
...
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
# base64 encoding, not hex in this example:
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashIterations = 1024
# This next property is only needed in Shiro 1.0\. Remove it in 1.1 and later:
credentialsMatcher.hashSalted = true
...
myRealm = com.company.....
myRealm.credentialsMatcher = $credentialsMatcher
...
```
#### SaltedAuthenticationInfo
如果制定了HashedCredentialsMatcher那么Realm.getAuthenticationInfo必须返回一个SaltedAuthenticationInfo实例而不是普通的Authentication实例。该SaltedAuthenticationInfo确保在创建用户信息时使用的salt可以在CredentialsMatcher中被使用。
HashedCredentialsMatcher在对Token中提交的credentials进行hash时需要使用到salt值来将用户提交的credentials进行和创建用户时相同的散列。
### 关闭Realm的Authentication
如果对某个Realm想要对该realm不执行Authentication可以将其实现类的supports方法只返回false此时该realm在authentication过程中绝对不会被询问。
## Realm Authorization
SecurityManager将校验permission和role的工作委托给了Authorizer默认是ModularRealmAuthorizer。
### 基于role的authorization
当subject的hasRoles或checkRoles被调用其具体的执行流程如下
1. subject将校验role的任务委托给SecurityManager
2. SecurityManager将任务委托给Authorizer
3. Authorizier会调用所有的Authorizing Realm直到该role被分配给subject。如果所有realm都没有授予subject该role那么访问失败返回false。
4. Authorizing Realm的AuthorizationInfo.getRoles方法会获取所有分配给该subject的role
5. 如果待检测的role在getRoles返回的role list中那么授权成功subject可以对该资源进行访问
### 基于permission的authorization
当subject的isPermitted或checkPermission方法被调用时其执行流程如下
1. subject将检测Permission的任务委托给SecurityManager
2. SecurityManager将该任务委托给Authorizer
3. Authorizer会以此访问所有的Authorizer Realm直到Permission被授予。如果所有的realm都没有授予该subject权限那么subject授权失败。
4. Realm按照如下顺序来检测Permission是否被授予
1. 其会调用AuthorizationInfo的getObjectPermissions方法和getStringPermissions方法并聚合结果从而获取直接分配给该subject的所有权限
2. 如果RolePermissionRegister被注册那么会根据subject被授予的role来获取role相关的permission根据RolePermissionResolver.resolvePermissionsInRole()方法
3. 对于上述返回的权限集合implies方法会被调用用来检测待检测权限是否隐含在其中