日常提交
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
- [通过java code实现authorization](#通过java-code实现authorization)
|
||||
- [基于String的权限鉴定](#基于string的权限鉴定)
|
||||
- [通过注解实现Authorization](#通过注解实现authorization)
|
||||
|
||||
# Apache Shiro Authorization
|
||||
## Authorization简介
|
||||
Authorization(访问控制),分配访问某资源的特定权限。
|
||||
|
||||
133
spring/Apache Shiro/Apache Shiro Realm.md
Normal file
133
spring/Apache Shiro/Apache Shiro Realm.md
Normal file
@@ -0,0 +1,133 @@
|
||||
- [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)和可自定义的(customizable)的,AuthenticationRealm支持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,其实现都默认使用SimpleCredentialsMatcher,SimpleCredentialsMatcher简单会对存储在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方法会被调用,用来检测待检测权限是否隐含在其中
|
||||
Reference in New Issue
Block a user