切换默认shell到wsl
This commit is contained in:
@@ -1,96 +1,96 @@
|
||||
- [Apache Shiro Authentication](#apache-shiro-authentication)
|
||||
- [Apache Shiro Authentication简介](#apache-shiro-authentication简介)
|
||||
- [Apache Authentication概念](#apache-authentication概念)
|
||||
- [subject](#subject)
|
||||
- [principals](#principals)
|
||||
- [credentials](#credentials)
|
||||
- [realms](#realms)
|
||||
- [Shiro Authentication过程](#shiro-authentication过程)
|
||||
- [Shiro框架的Authentication过程](#shiro框架的authentication过程)
|
||||
- [收集用户的principals和credentials](#收集用户的principals和credentials)
|
||||
- [将收集的principals和credentials提交给认证系统](#将收集的principals和credentials提交给认证系统)
|
||||
- [身份认证后对访问进行allow/retry authentication/block](#身份认证后对访问进行allowretry-authenticationblock)
|
||||
- [rememberMe支持](#rememberme支持)
|
||||
- [remembered和authenticated的区别](#remembered和authenticated的区别)
|
||||
- [logging out](#logging-out)
|
||||
|
||||
# Apache Shiro Authentication
|
||||
## Apache Shiro Authentication简介
|
||||
Authentication是一个对用户进行身份认证的过程,在认证过程中用户需要向应用提供用于证明用户的凭据。
|
||||
## Apache Authentication概念
|
||||
### subject
|
||||
在应用的角度,subject即是一个用户
|
||||
### principals
|
||||
主体,用于标识一个用户,可以是username、social security nubmer等
|
||||
### credentials
|
||||
凭据,在用户认证过程中用于认证用户的身份,可以是密码、生物识别数据(如指纹、面容等)
|
||||
### realms
|
||||
专用于security的dao对象,用于和后端的datasource进行沟通。
|
||||
## Shiro Authentication过程
|
||||
### Shiro框架的Authentication过程
|
||||
1. 收集用户的principals和credentials
|
||||
2. 向应用的认证系统提交用户的principals和credentials
|
||||
3. 认证结束之后,根据认证结果允许访问、重试访问请求或者阻塞访问
|
||||
### 收集用户的principals和credentials
|
||||
可以通过UsernamePasswordToken来存储用户提交的username和password,并可以调用UsernamePasswordToken.rememberMe方法来启用Shiro的“remember-me”功能。
|
||||
```java
|
||||
//Example using most common scenario:
|
||||
//String username and password. Acquire in
|
||||
//system-specific manner (HTTP request, GUI, etc)
|
||||
UsernamePasswordToken token = new UsernamePasswordToken( username, password );
|
||||
|
||||
//”Remember Me” built-in, just do this:
|
||||
token.setRememberMe(true);
|
||||
```
|
||||
### 将收集的principals和credentials提交给认证系统
|
||||
在收集完用户的principals和credentials之后,需要将其提交给应用的认证系统。
|
||||
在Shiro中,代表认证系统的是Realm,其从存放安全数据的datasource中获取数据,并且对用户提交的principals和credentials进行校验。
|
||||
在Shiro中,该过程用如下代码表示:
|
||||
```java
|
||||
//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
|
||||
//into the login method
|
||||
currentUser.login(token);
|
||||
```
|
||||
> 在Shiro中,subject可以被看做是用户,**在当前执行的线程中永远有一个subject与其相关联。**
|
||||
> **可以通过SecurityUtils.getSubject()方法来获取当前执行线程相关联的subject。**
|
||||
|
||||
> 在获取当前执行线程关联subject之后,需要对当前subject进行身份认证,通过subject.login(token)来对用户提交的principals和credentials进行Authentication
|
||||
|
||||
### 身份认证后对访问进行allow/retry authentication/block
|
||||
在调用subject.login(token)之后,如果身份认证成功,用户将在seesion的生命周期内维持他们的identity。但是如果身份认证失败,可以为抛出的异常指定不同的异常处理逻辑来定义登录失败之后的行为。
|
||||
```java
|
||||
try {
|
||||
currentUser.login(token);
|
||||
} catch ( UnknownAccountException uae ) { ...
|
||||
} catch ( IncorrectCredentialsException ice ) { ...
|
||||
} catch ( LockedAccountException lae ) { ...
|
||||
} catch ( ExcessiveAttemptsException eae ) { ...
|
||||
} ... your own ...
|
||||
} catch ( AuthenticationException ae ) {
|
||||
//unexpected error?
|
||||
}
|
||||
//No problems, show authenticated view…
|
||||
```
|
||||
## rememberMe支持
|
||||
Apache Shiro除了正常的Authentication流程外,还支持rememberMe功能。
|
||||
Shiro中Subject对象拥有两个方法,isRemembered()和isAuthenticated()。
|
||||
> - 一个remembered subject,其identity和principals自上次session成功认证后就被记住
|
||||
> - 一个authenticated subject,其identity只在本次会话中有效
|
||||
|
||||
### remembered和authenticated的区别
|
||||
在Shiro中,一个remembered subject并不代表该subject已经被authenticated。如果一个subject被remembered,仅仅会向系统提示该subject可能是系统的某个用户,但是不会对subject的身份提供保证。但是如果subject被authenticated,该subject的identity在当前会话中已经被认证。
|
||||
> 故而,isRemembered校验可以用来执行一些非敏感的操作,如用户自定义界面视图等。但是,敏感性操作如金额信息和变动操作等,必须通过isAuthenticated校验而不是isRemembered校验,敏感性操作的用户身份必须得到认证。
|
||||
|
||||
## logging out
|
||||
在Shiro中,登出操作可以通过如下代码实现
|
||||
```java
|
||||
currentUser.logout(); //removes all identifying information and invalidates their session too.
|
||||
```
|
||||
当执行登出操作时,Shiro会关闭当前session,并且会移除当前subject的任何identity。如果在web环境中使用rememberMe,logout默认会从浏览器中删除rememberMe cookie。
|
||||
|
||||
|
||||
- [Apache Shiro Authentication](#apache-shiro-authentication)
|
||||
- [Apache Shiro Authentication简介](#apache-shiro-authentication简介)
|
||||
- [Apache Authentication概念](#apache-authentication概念)
|
||||
- [subject](#subject)
|
||||
- [principals](#principals)
|
||||
- [credentials](#credentials)
|
||||
- [realms](#realms)
|
||||
- [Shiro Authentication过程](#shiro-authentication过程)
|
||||
- [Shiro框架的Authentication过程](#shiro框架的authentication过程)
|
||||
- [收集用户的principals和credentials](#收集用户的principals和credentials)
|
||||
- [将收集的principals和credentials提交给认证系统](#将收集的principals和credentials提交给认证系统)
|
||||
- [身份认证后对访问进行allow/retry authentication/block](#身份认证后对访问进行allowretry-authenticationblock)
|
||||
- [rememberMe支持](#rememberme支持)
|
||||
- [remembered和authenticated的区别](#remembered和authenticated的区别)
|
||||
- [logging out](#logging-out)
|
||||
|
||||
# Apache Shiro Authentication
|
||||
## Apache Shiro Authentication简介
|
||||
Authentication是一个对用户进行身份认证的过程,在认证过程中用户需要向应用提供用于证明用户的凭据。
|
||||
## Apache Authentication概念
|
||||
### subject
|
||||
在应用的角度,subject即是一个用户
|
||||
### principals
|
||||
主体,用于标识一个用户,可以是username、social security nubmer等
|
||||
### credentials
|
||||
凭据,在用户认证过程中用于认证用户的身份,可以是密码、生物识别数据(如指纹、面容等)
|
||||
### realms
|
||||
专用于security的dao对象,用于和后端的datasource进行沟通。
|
||||
## Shiro Authentication过程
|
||||
### Shiro框架的Authentication过程
|
||||
1. 收集用户的principals和credentials
|
||||
2. 向应用的认证系统提交用户的principals和credentials
|
||||
3. 认证结束之后,根据认证结果允许访问、重试访问请求或者阻塞访问
|
||||
### 收集用户的principals和credentials
|
||||
可以通过UsernamePasswordToken来存储用户提交的username和password,并可以调用UsernamePasswordToken.rememberMe方法来启用Shiro的“remember-me”功能。
|
||||
```java
|
||||
//Example using most common scenario:
|
||||
//String username and password. Acquire in
|
||||
//system-specific manner (HTTP request, GUI, etc)
|
||||
UsernamePasswordToken token = new UsernamePasswordToken( username, password );
|
||||
|
||||
//”Remember Me” built-in, just do this:
|
||||
token.setRememberMe(true);
|
||||
```
|
||||
### 将收集的principals和credentials提交给认证系统
|
||||
在收集完用户的principals和credentials之后,需要将其提交给应用的认证系统。
|
||||
在Shiro中,代表认证系统的是Realm,其从存放安全数据的datasource中获取数据,并且对用户提交的principals和credentials进行校验。
|
||||
在Shiro中,该过程用如下代码表示:
|
||||
```java
|
||||
//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
|
||||
//into the login method
|
||||
currentUser.login(token);
|
||||
```
|
||||
> 在Shiro中,subject可以被看做是用户,**在当前执行的线程中永远有一个subject与其相关联。**
|
||||
> **可以通过SecurityUtils.getSubject()方法来获取当前执行线程相关联的subject。**
|
||||
|
||||
> 在获取当前执行线程关联subject之后,需要对当前subject进行身份认证,通过subject.login(token)来对用户提交的principals和credentials进行Authentication
|
||||
|
||||
### 身份认证后对访问进行allow/retry authentication/block
|
||||
在调用subject.login(token)之后,如果身份认证成功,用户将在seesion的生命周期内维持他们的identity。但是如果身份认证失败,可以为抛出的异常指定不同的异常处理逻辑来定义登录失败之后的行为。
|
||||
```java
|
||||
try {
|
||||
currentUser.login(token);
|
||||
} catch ( UnknownAccountException uae ) { ...
|
||||
} catch ( IncorrectCredentialsException ice ) { ...
|
||||
} catch ( LockedAccountException lae ) { ...
|
||||
} catch ( ExcessiveAttemptsException eae ) { ...
|
||||
} ... your own ...
|
||||
} catch ( AuthenticationException ae ) {
|
||||
//unexpected error?
|
||||
}
|
||||
//No problems, show authenticated view…
|
||||
```
|
||||
## rememberMe支持
|
||||
Apache Shiro除了正常的Authentication流程外,还支持rememberMe功能。
|
||||
Shiro中Subject对象拥有两个方法,isRemembered()和isAuthenticated()。
|
||||
> - 一个remembered subject,其identity和principals自上次session成功认证后就被记住
|
||||
> - 一个authenticated subject,其identity只在本次会话中有效
|
||||
|
||||
### remembered和authenticated的区别
|
||||
在Shiro中,一个remembered subject并不代表该subject已经被authenticated。如果一个subject被remembered,仅仅会向系统提示该subject可能是系统的某个用户,但是不会对subject的身份提供保证。但是如果subject被authenticated,该subject的identity在当前会话中已经被认证。
|
||||
> 故而,isRemembered校验可以用来执行一些非敏感的操作,如用户自定义界面视图等。但是,敏感性操作如金额信息和变动操作等,必须通过isAuthenticated校验而不是isRemembered校验,敏感性操作的用户身份必须得到认证。
|
||||
|
||||
## logging out
|
||||
在Shiro中,登出操作可以通过如下代码实现
|
||||
```java
|
||||
currentUser.logout(); //removes all identifying information and invalidates their session too.
|
||||
```
|
||||
当执行登出操作时,Shiro会关闭当前session,并且会移除当前subject的任何identity。如果在web环境中使用rememberMe,logout默认会从浏览器中删除rememberMe cookie。
|
||||
|
||||
|
||||
|
||||
@@ -1,94 +1,94 @@
|
||||
- [Apache Shiro Authorization](#apache-shiro-authorization)
|
||||
- [Authorization简介](#authorization简介)
|
||||
- [Authorization的核心元素](#authorization的核心元素)
|
||||
- [Permission](#permission)
|
||||
- [权限粒度级别](#权限粒度级别)
|
||||
- [Roles](#roles)
|
||||
- [Role分类](#role分类)
|
||||
- [User](#user)
|
||||
- [在Apache Shiro中实行Authorization](#在apache-shiro中实行authorization)
|
||||
- [通过java code实现authorization](#通过java-code实现authorization)
|
||||
- [基于String的权限鉴定](#基于string的权限鉴定)
|
||||
- [通过注解实现Authorization](#通过注解实现authorization)
|
||||
|
||||
# Apache Shiro Authorization
|
||||
## Authorization简介
|
||||
Authorization(访问控制),分配访问某资源的特定权限。
|
||||
## Authorization的核心元素
|
||||
### Permission
|
||||
Permission是最原子级别的安全策略,用来控制用户与应用进行交互时可以执行哪些操作。**格式良好的permission描述了资源的类型和与该资源交互时可以执行的操作。**
|
||||
对于与数据相关的资源,权限通常有create、read、update、delete(CRUD)。
|
||||
#### 权限粒度级别
|
||||
在Shiro中,可以在任何粒度对permission进行定义。如下是permission粒度的一些定义:
|
||||
1. Resource级别:该级别是最广泛和最容易构建的粒度级别,在该级别用户可以对资源执行特定的操作。**在Resource级别,该资源类型被指定,但是没有限制用户操作特定的资源实例(即用户可以对该Resource类型的所有实例进行操作)**
|
||||
2. Instance级别:该级别限定了Permission可以操作的Resource Instance,在该级别用户只能够对特定的Resource实例进行操作。
|
||||
3. Attribute级别:该级别比限定了Permission可以操作Resouce类型或Resource实例的某个属性
|
||||
### Roles
|
||||
Roles是一个Permission的集合,用于简化权限和用户管理过程。用户可以被授予特定的角色来获得操作某些资源的权限。
|
||||
#### Role分类
|
||||
1. Role不实际关联具体的Permission,当你具有banker的角色时,其角色隐含你可以对账户进行操作的权限;当你具有waiter的角色时,默认可以对厨房的door进行open/close操作
|
||||
2. Role实际关联具体的Permission,在该情况下Role即为一系列Permission的集合,你可以对银行账号进行create、delete操作,因为操作银行账号是你已分配的admin角色的一个下属权限
|
||||
### User
|
||||
在Shiro中,User即是一个Subject实例。在Shiro中,Subject可以是任何与系统进行交互的主体,可以是浏览器、客户端、crond定时任务等。
|
||||
## 在Apache Shiro中实行Authorization
|
||||
在Apache Shiro中,Authorization可以通过如下方式执行:
|
||||
1. 通过代码实现:即在java程序中通过代码实现访问控制
|
||||
2. jdk注解:可以在你的方法上加上authorization注解
|
||||
3. jsp/gsp taglibs
|
||||
### 通过java code实现authorization
|
||||
可以通过如下代码进行角色鉴定:
|
||||
```java
|
||||
//get the current Subject
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
|
||||
if (currentUser.hasRole("administrator")) {
|
||||
//show a special button
|
||||
} else {
|
||||
//don’t show the button?)
|
||||
}
|
||||
```
|
||||
可以通过如下代码实现对权限的鉴定操作:
|
||||
```java
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
|
||||
Permission printPermission = new PrinterPermission("laserjet3000n","print");
|
||||
|
||||
If (currentUser.isPermitted(printPermission)) {
|
||||
//do one thing (show the print button?)
|
||||
} else {
|
||||
//don’t show the button?
|
||||
}
|
||||
```
|
||||
#### 基于String的权限鉴定
|
||||
如果不想构造Permission对象,可以通过构造一个字符串来代表权限。该字符串可以是任何格式,只要你的Realm能够识别该格式并且与权限进行交互。
|
||||
```java
|
||||
String perm = "printer:print:laserjet4400n";
|
||||
|
||||
if(currentUser.isPermitted(perm)){
|
||||
//show the print button?
|
||||
} else {
|
||||
//don’t show the button?
|
||||
}
|
||||
```
|
||||
### 通过注解实现Authorization
|
||||
可以通过java注解来实现Authorization过程,**在使用注解之前,必须先开启aop**。
|
||||
如果在执行openAccount之前,当前Subject必须拥有account:create权限,那么可以通过如下方式来实现权限鉴定。如果当前用户未被直接授予或通过role间接授予该权限,那么会抛出AuthorizationException异常。
|
||||
```java
|
||||
//Will throw an AuthorizationException if none
|
||||
//of the caller’s roles imply the Account
|
||||
//'create' permission
|
||||
@RequiresPermissions("account:create")
|
||||
public void openAccount( Account acct ) {
|
||||
//create the account
|
||||
}
|
||||
```
|
||||
如果要在执行方法之前进行角色校验,可以通过如下方式加上注解达到预期功能。
|
||||
```java
|
||||
//Throws an AuthorizationException if the caller
|
||||
//doesn’t have the ‘teller’ role:
|
||||
@RequiresRoles( "teller" )
|
||||
public void openAccount( Account acct ) {
|
||||
//do something in here that only a teller
|
||||
//should do
|
||||
}
|
||||
- [Apache Shiro Authorization](#apache-shiro-authorization)
|
||||
- [Authorization简介](#authorization简介)
|
||||
- [Authorization的核心元素](#authorization的核心元素)
|
||||
- [Permission](#permission)
|
||||
- [权限粒度级别](#权限粒度级别)
|
||||
- [Roles](#roles)
|
||||
- [Role分类](#role分类)
|
||||
- [User](#user)
|
||||
- [在Apache Shiro中实行Authorization](#在apache-shiro中实行authorization)
|
||||
- [通过java code实现authorization](#通过java-code实现authorization)
|
||||
- [基于String的权限鉴定](#基于string的权限鉴定)
|
||||
- [通过注解实现Authorization](#通过注解实现authorization)
|
||||
|
||||
# Apache Shiro Authorization
|
||||
## Authorization简介
|
||||
Authorization(访问控制),分配访问某资源的特定权限。
|
||||
## Authorization的核心元素
|
||||
### Permission
|
||||
Permission是最原子级别的安全策略,用来控制用户与应用进行交互时可以执行哪些操作。**格式良好的permission描述了资源的类型和与该资源交互时可以执行的操作。**
|
||||
对于与数据相关的资源,权限通常有create、read、update、delete(CRUD)。
|
||||
#### 权限粒度级别
|
||||
在Shiro中,可以在任何粒度对permission进行定义。如下是permission粒度的一些定义:
|
||||
1. Resource级别:该级别是最广泛和最容易构建的粒度级别,在该级别用户可以对资源执行特定的操作。**在Resource级别,该资源类型被指定,但是没有限制用户操作特定的资源实例(即用户可以对该Resource类型的所有实例进行操作)**
|
||||
2. Instance级别:该级别限定了Permission可以操作的Resource Instance,在该级别用户只能够对特定的Resource实例进行操作。
|
||||
3. Attribute级别:该级别比限定了Permission可以操作Resouce类型或Resource实例的某个属性
|
||||
### Roles
|
||||
Roles是一个Permission的集合,用于简化权限和用户管理过程。用户可以被授予特定的角色来获得操作某些资源的权限。
|
||||
#### Role分类
|
||||
1. Role不实际关联具体的Permission,当你具有banker的角色时,其角色隐含你可以对账户进行操作的权限;当你具有waiter的角色时,默认可以对厨房的door进行open/close操作
|
||||
2. Role实际关联具体的Permission,在该情况下Role即为一系列Permission的集合,你可以对银行账号进行create、delete操作,因为操作银行账号是你已分配的admin角色的一个下属权限
|
||||
### User
|
||||
在Shiro中,User即是一个Subject实例。在Shiro中,Subject可以是任何与系统进行交互的主体,可以是浏览器、客户端、crond定时任务等。
|
||||
## 在Apache Shiro中实行Authorization
|
||||
在Apache Shiro中,Authorization可以通过如下方式执行:
|
||||
1. 通过代码实现:即在java程序中通过代码实现访问控制
|
||||
2. jdk注解:可以在你的方法上加上authorization注解
|
||||
3. jsp/gsp taglibs
|
||||
### 通过java code实现authorization
|
||||
可以通过如下代码进行角色鉴定:
|
||||
```java
|
||||
//get the current Subject
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
|
||||
if (currentUser.hasRole("administrator")) {
|
||||
//show a special button
|
||||
} else {
|
||||
//don’t show the button?)
|
||||
}
|
||||
```
|
||||
可以通过如下代码实现对权限的鉴定操作:
|
||||
```java
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
|
||||
Permission printPermission = new PrinterPermission("laserjet3000n","print");
|
||||
|
||||
If (currentUser.isPermitted(printPermission)) {
|
||||
//do one thing (show the print button?)
|
||||
} else {
|
||||
//don’t show the button?
|
||||
}
|
||||
```
|
||||
#### 基于String的权限鉴定
|
||||
如果不想构造Permission对象,可以通过构造一个字符串来代表权限。该字符串可以是任何格式,只要你的Realm能够识别该格式并且与权限进行交互。
|
||||
```java
|
||||
String perm = "printer:print:laserjet4400n";
|
||||
|
||||
if(currentUser.isPermitted(perm)){
|
||||
//show the print button?
|
||||
} else {
|
||||
//don’t show the button?
|
||||
}
|
||||
```
|
||||
### 通过注解实现Authorization
|
||||
可以通过java注解来实现Authorization过程,**在使用注解之前,必须先开启aop**。
|
||||
如果在执行openAccount之前,当前Subject必须拥有account:create权限,那么可以通过如下方式来实现权限鉴定。如果当前用户未被直接授予或通过role间接授予该权限,那么会抛出AuthorizationException异常。
|
||||
```java
|
||||
//Will throw an AuthorizationException if none
|
||||
//of the caller’s roles imply the Account
|
||||
//'create' permission
|
||||
@RequiresPermissions("account:create")
|
||||
public void openAccount( Account acct ) {
|
||||
//create the account
|
||||
}
|
||||
```
|
||||
如果要在执行方法之前进行角色校验,可以通过如下方式加上注解达到预期功能。
|
||||
```java
|
||||
//Throws an AuthorizationException if the caller
|
||||
//doesn’t have the ‘teller’ role:
|
||||
@RequiresRoles( "teller" )
|
||||
public void openAccount( Account acct ) {
|
||||
//do something in here that only a teller
|
||||
//should do
|
||||
}
|
||||
```
|
||||
@@ -1,92 +1,92 @@
|
||||
- [Apache Shiro Quick Start](#apache-shiro-quick-start)
|
||||
- [Apache Shiro常用API](#apache-shiro常用api)
|
||||
- [获取当前用户](#获取当前用户)
|
||||
- [设置用户Session](#设置用户session)
|
||||
- [通过用户名和密码对用户进行身份认证](#通过用户名和密码对用户进行身份认证)
|
||||
- [对身份认证失败的情况进行异常处理](#对身份认证失败的情况进行异常处理)
|
||||
- [对已经登录的用户进行role检验](#对已经登录的用户进行role检验)
|
||||
- [检测某用户是否具有某项特定权限](#检测某用户是否具有某项特定权限)
|
||||
- [在实例级别对用户的权限进行检测](#在实例级别对用户的权限进行检测)
|
||||
- [用户登出](#用户登出)
|
||||
|
||||
# Apache Shiro Quick Start
|
||||
## Apache Shiro常用API
|
||||
### 获取当前用户
|
||||
在任何环境中,都可以通过如下代码来获取当前执行的用户:
|
||||
```java
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
```
|
||||
### 设置用户Session
|
||||
可以通过如下代码获取用户的Shiro Session,并可以向Session中设置属性和值,设置的值在用户会话期间内都可以使用。
|
||||
**Shiro Session在使用时并不要求当前位于HTTP环境下**
|
||||
```java
|
||||
Session session = currentUser.getSession();
|
||||
session.setAttribute( "someKey", "aValue" );
|
||||
```
|
||||
> 如果当前应用部署于Web环境下,那么Shiro Session默认会使用HttpSession,但是如果当前应用部署在非Web环境下时,Shiro Session会使用其Enterprise Session Management。
|
||||
|
||||
### 通过用户名和密码对用户进行身份认证
|
||||
通过如下代码,可以通过UsernamePasswordToken来对未认证的用户进行身份认证。
|
||||
```java
|
||||
if ( !currentUser.isAuthenticated() ) {
|
||||
//collect user principals and credentials in a gui specific manner
|
||||
//such as username/password html form, X509 certificate, OpenID, etc.
|
||||
//We'll use the username/password example here since it is the most common.
|
||||
//(do you know what movie this is from? ;)
|
||||
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
|
||||
//this is all you have to do to support 'remember me' (no config - built in!):
|
||||
token.setRememberMe(true);
|
||||
currentUser.login(token);
|
||||
}
|
||||
```
|
||||
### 对身份认证失败的情况进行异常处理
|
||||
如果在身份认证的过程中失败,可以通过如下代码捕获认证失败抛出的异常,并对异常进行异常处理
|
||||
```java
|
||||
try {
|
||||
currentUser.login( token );
|
||||
//if no exception, that's it, we're done!
|
||||
} catch ( UnknownAccountException uae ) {
|
||||
//username wasn't in the system, show them an error message?
|
||||
} catch ( IncorrectCredentialsException ice ) {
|
||||
//password didn't match, try again?
|
||||
} catch ( LockedAccountException lae ) {
|
||||
//account for that username is locked - can't login. Show them a message?
|
||||
}
|
||||
... more types exceptions to check if you want ...
|
||||
} catch ( AuthenticationException ae ) {
|
||||
//unexpected condition - error?
|
||||
}
|
||||
```
|
||||
### 对已经登录的用户进行role检验
|
||||
如果用户已经登录,如果要检测该用户是否被授予某role权限,可以通过如下代码进行检验
|
||||
```java
|
||||
if ( currentUser.hasRole( "schwartz" ) ) {
|
||||
log.info("May the Schwartz be with you!" );
|
||||
} else {
|
||||
log.info( "Hello, mere mortal." );
|
||||
}
|
||||
```
|
||||
### 检测某用户是否具有某项特定权限
|
||||
如果要对已经登录的用户执行检测,检测其是否被授予某项特定的前线,可以通过如下方式进行检测。
|
||||
```java
|
||||
if ( currentUser.isPermitted( "lightsaber:wield" ) ) {
|
||||
log.info("You may use a lightsaber ring. Use it wisely.");
|
||||
} else {
|
||||
log.info("Sorry, lightsaber rings are for schwartz masters only.");
|
||||
}
|
||||
```
|
||||
### 在实例级别对用户的权限进行检测
|
||||
在Shiro中,可以检测用户是否对某实例具有特定权限,通过如下代码:
|
||||
```java
|
||||
if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
|
||||
log.info("You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. " +
|
||||
"Here are the keys - have fun!");
|
||||
} else {
|
||||
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
|
||||
}
|
||||
```
|
||||
### 用户登出
|
||||
如果已经登录的用户想要执行登出操作,可以通过如下代码进行登录:
|
||||
```java
|
||||
currentUser.logout();
|
||||
- [Apache Shiro Quick Start](#apache-shiro-quick-start)
|
||||
- [Apache Shiro常用API](#apache-shiro常用api)
|
||||
- [获取当前用户](#获取当前用户)
|
||||
- [设置用户Session](#设置用户session)
|
||||
- [通过用户名和密码对用户进行身份认证](#通过用户名和密码对用户进行身份认证)
|
||||
- [对身份认证失败的情况进行异常处理](#对身份认证失败的情况进行异常处理)
|
||||
- [对已经登录的用户进行role检验](#对已经登录的用户进行role检验)
|
||||
- [检测某用户是否具有某项特定权限](#检测某用户是否具有某项特定权限)
|
||||
- [在实例级别对用户的权限进行检测](#在实例级别对用户的权限进行检测)
|
||||
- [用户登出](#用户登出)
|
||||
|
||||
# Apache Shiro Quick Start
|
||||
## Apache Shiro常用API
|
||||
### 获取当前用户
|
||||
在任何环境中,都可以通过如下代码来获取当前执行的用户:
|
||||
```java
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
```
|
||||
### 设置用户Session
|
||||
可以通过如下代码获取用户的Shiro Session,并可以向Session中设置属性和值,设置的值在用户会话期间内都可以使用。
|
||||
**Shiro Session在使用时并不要求当前位于HTTP环境下**
|
||||
```java
|
||||
Session session = currentUser.getSession();
|
||||
session.setAttribute( "someKey", "aValue" );
|
||||
```
|
||||
> 如果当前应用部署于Web环境下,那么Shiro Session默认会使用HttpSession,但是如果当前应用部署在非Web环境下时,Shiro Session会使用其Enterprise Session Management。
|
||||
|
||||
### 通过用户名和密码对用户进行身份认证
|
||||
通过如下代码,可以通过UsernamePasswordToken来对未认证的用户进行身份认证。
|
||||
```java
|
||||
if ( !currentUser.isAuthenticated() ) {
|
||||
//collect user principals and credentials in a gui specific manner
|
||||
//such as username/password html form, X509 certificate, OpenID, etc.
|
||||
//We'll use the username/password example here since it is the most common.
|
||||
//(do you know what movie this is from? ;)
|
||||
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
|
||||
//this is all you have to do to support 'remember me' (no config - built in!):
|
||||
token.setRememberMe(true);
|
||||
currentUser.login(token);
|
||||
}
|
||||
```
|
||||
### 对身份认证失败的情况进行异常处理
|
||||
如果在身份认证的过程中失败,可以通过如下代码捕获认证失败抛出的异常,并对异常进行异常处理
|
||||
```java
|
||||
try {
|
||||
currentUser.login( token );
|
||||
//if no exception, that's it, we're done!
|
||||
} catch ( UnknownAccountException uae ) {
|
||||
//username wasn't in the system, show them an error message?
|
||||
} catch ( IncorrectCredentialsException ice ) {
|
||||
//password didn't match, try again?
|
||||
} catch ( LockedAccountException lae ) {
|
||||
//account for that username is locked - can't login. Show them a message?
|
||||
}
|
||||
... more types exceptions to check if you want ...
|
||||
} catch ( AuthenticationException ae ) {
|
||||
//unexpected condition - error?
|
||||
}
|
||||
```
|
||||
### 对已经登录的用户进行role检验
|
||||
如果用户已经登录,如果要检测该用户是否被授予某role权限,可以通过如下代码进行检验
|
||||
```java
|
||||
if ( currentUser.hasRole( "schwartz" ) ) {
|
||||
log.info("May the Schwartz be with you!" );
|
||||
} else {
|
||||
log.info( "Hello, mere mortal." );
|
||||
}
|
||||
```
|
||||
### 检测某用户是否具有某项特定权限
|
||||
如果要对已经登录的用户执行检测,检测其是否被授予某项特定的前线,可以通过如下方式进行检测。
|
||||
```java
|
||||
if ( currentUser.isPermitted( "lightsaber:wield" ) ) {
|
||||
log.info("You may use a lightsaber ring. Use it wisely.");
|
||||
} else {
|
||||
log.info("Sorry, lightsaber rings are for schwartz masters only.");
|
||||
}
|
||||
```
|
||||
### 在实例级别对用户的权限进行检测
|
||||
在Shiro中,可以检测用户是否对某实例具有特定权限,通过如下代码:
|
||||
```java
|
||||
if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
|
||||
log.info("You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. " +
|
||||
"Here are the keys - have fun!");
|
||||
} else {
|
||||
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
|
||||
}
|
||||
```
|
||||
### 用户登出
|
||||
如果已经登录的用户想要执行登出操作,可以通过如下代码进行登录:
|
||||
```java
|
||||
currentUser.logout();
|
||||
```
|
||||
@@ -1,133 +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方法会被调用,用来检测待检测权限是否隐含在其中
|
||||
- [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方法会被调用,用来检测待检测权限是否隐含在其中
|
||||
|
||||
@@ -1,175 +1,175 @@
|
||||
- [Apache Shiro](#apache-shiro)
|
||||
- [Shiro简介](#shiro简介)
|
||||
- [Shiro中常用的概念](#shiro中常用的概念)
|
||||
- [Subject](#subject)
|
||||
- [SecurityManager](#securitymanager)
|
||||
- [realms](#realms)
|
||||
- [Authentication](#authentication)
|
||||
- [Authorization](#authorization)
|
||||
- [Session Management](#session-management)
|
||||
- [Shiro Session可在任何应用中使用](#shiro-session可在任何应用中使用)
|
||||
- [Shiro加密](#shiro加密)
|
||||
- [shiro hash](#shiro-hash)
|
||||
- [Shiro Ciphers](#shiro-ciphers)
|
||||
- [Shiro框架的Web支持](#shiro框架的web支持)
|
||||
- [Web Session管理](#web-session管理)
|
||||
- [Shiro Native Session](#shiro-native-session)
|
||||
|
||||
# Apache Shiro
|
||||
## Shiro简介
|
||||
Shiro是一个简单易用且功能强大的Java安全框架,用于实现认证、授权、加密、session管理等场景,并且Shiro可以被用于任何应用,包括命令行应用、移动应用、大型web应用或是企业应用。
|
||||
Shiro在如下方面提供Security API:
|
||||
- Authentication:为用户提供身份认证
|
||||
- Authorization:为应用提供用户的访问控制
|
||||
- 加密:避免应用数据处于明文状态
|
||||
- session管理:每个用户的时间敏感状态
|
||||
## Shiro中常用的概念
|
||||
Shiro框架的结构主要分为三个部分:Subject、SecurityManager、Realms
|
||||
### Subject
|
||||
Subject:Subject是一个安全术语,通常意味着当前执行的用户。
|
||||
```java
|
||||
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。
|
||||
|
||||
对每个应用中,只存在一个SecurityManager,SecurityManager是应用范围内的单例。默认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. 如果提交的凭据和系统中该主体对应的凭据相同,那么该用户会被认为是“通过认证的”。如果提交的凭据不符,那么该用户会被认为是“认证失败的”。
|
||||
```java
|
||||
// 认证流程代码
|
||||
//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,可以通过捕获该异常来对失败的认证进行处理。
|
||||
```java
|
||||
//3. Login:
|
||||
try {
|
||||
currentUser.login(token);
|
||||
} catch (IncorrectCredentialsException ice) { …
|
||||
} catch (LockedAccountException lae) { …
|
||||
}
|
||||
…
|
||||
catch (AuthenticationException ae) {…
|
||||
}
|
||||
```
|
||||
**当用户通过身份认证之后,其被认为是“通过身份认证的”,并且被允许使用应用。但是,通过身份认证并不意味着可以在系统中执行任何操作。通过授权,可以决定用户能够在系统中执行哪些操作。**
|
||||
|
||||
## Authorization
|
||||
Authorization的实质是访问控制,通常用于控制用户在系统中能够使用哪些资源。大多数情况下,可以通过role或permission等形式来实现访问控制,用户通过分配给他们的角色或者权限来执行操作。通过检查用户的role或者permission,系统可以决定将哪些功能暴露给用户。
|
||||
```java
|
||||
// 通过如下代码,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?
|
||||
}
|
||||
```
|
||||
权限控制甚至支持非常细粒度的权限,譬如实例级别的权限控制。
|
||||
```java
|
||||
// 如下代码检测用户是否拥有删除jsmith用户的权限
|
||||
if ( subject.isPermitted(“user:delete:jsmith”) ) {
|
||||
//delete the ‘jsmith’ user
|
||||
} else {
|
||||
//don’t 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客户端时很有用)。
|
||||
```java
|
||||
/**
|
||||
* 用户获取Session
|
||||
*/
|
||||
// 如果当前用户存在会话,则获取已存在会话,
|
||||
// 如果当前用户不存在会话,则创建新的会话
|
||||
Session session = subject.getSession();
|
||||
// 接受一个boolean值,该boolean值标识如果当前用户不存在会话,是否创建一个新的会话
|
||||
Session session = subject.getSession(boolean create);
|
||||
```
|
||||
### Shiro Session可在任何应用中使用
|
||||
```java
|
||||
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值,可以通过如下方式快速计算:
|
||||
```java
|
||||
String hex = new Md5Hash(myFile).toHex();
|
||||
```
|
||||
在shiro hash中,如果想要对密码进行sha-512进行hash操作并且对结果进行base64编码成可打印字符串,可以进行如下操作:
|
||||
```java
|
||||
String encodedPassword =
|
||||
new Sha512Hash(password, salt, count).toBase64();
|
||||
```
|
||||
Shiro框架在很大程度上简化了hash和编码。
|
||||
### Shiro Ciphers
|
||||
Ciphers是一种算法,可以通过指定的key将加密后的数据还原到加密之前(不同于hash操作,hash操作通常是不可逆的)。通常用Ciphers来保证数据传输时的安全,防止数据在传输时被窥探。
|
||||
Shiro通过引入CipherService API来简化加密的流程,**CipherService是一个简单,无状态并且线程安全的API,可以对应用数据进行加密或者解密操作。**
|
||||
```java
|
||||
// Shiro CipherService对数据进行加密操作
|
||||
|
||||
AesCipherService cipherService = new AesCipherService();
|
||||
cipherService.setKeySize(256);
|
||||
//create a test key:
|
||||
byte[] testKey = cipherService.generateNewKey();
|
||||
|
||||
//encrypt a file’s 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时的任何代码。**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- [Apache Shiro](#apache-shiro)
|
||||
- [Shiro简介](#shiro简介)
|
||||
- [Shiro中常用的概念](#shiro中常用的概念)
|
||||
- [Subject](#subject)
|
||||
- [SecurityManager](#securitymanager)
|
||||
- [realms](#realms)
|
||||
- [Authentication](#authentication)
|
||||
- [Authorization](#authorization)
|
||||
- [Session Management](#session-management)
|
||||
- [Shiro Session可在任何应用中使用](#shiro-session可在任何应用中使用)
|
||||
- [Shiro加密](#shiro加密)
|
||||
- [shiro hash](#shiro-hash)
|
||||
- [Shiro Ciphers](#shiro-ciphers)
|
||||
- [Shiro框架的Web支持](#shiro框架的web支持)
|
||||
- [Web Session管理](#web-session管理)
|
||||
- [Shiro Native Session](#shiro-native-session)
|
||||
|
||||
# Apache Shiro
|
||||
## Shiro简介
|
||||
Shiro是一个简单易用且功能强大的Java安全框架,用于实现认证、授权、加密、session管理等场景,并且Shiro可以被用于任何应用,包括命令行应用、移动应用、大型web应用或是企业应用。
|
||||
Shiro在如下方面提供Security API:
|
||||
- Authentication:为用户提供身份认证
|
||||
- Authorization:为应用提供用户的访问控制
|
||||
- 加密:避免应用数据处于明文状态
|
||||
- session管理:每个用户的时间敏感状态
|
||||
## Shiro中常用的概念
|
||||
Shiro框架的结构主要分为三个部分:Subject、SecurityManager、Realms
|
||||
### Subject
|
||||
Subject:Subject是一个安全术语,通常意味着当前执行的用户。
|
||||
```java
|
||||
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。
|
||||
|
||||
对每个应用中,只存在一个SecurityManager,SecurityManager是应用范围内的单例。默认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. 如果提交的凭据和系统中该主体对应的凭据相同,那么该用户会被认为是“通过认证的”。如果提交的凭据不符,那么该用户会被认为是“认证失败的”。
|
||||
```java
|
||||
// 认证流程代码
|
||||
//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,可以通过捕获该异常来对失败的认证进行处理。
|
||||
```java
|
||||
//3. Login:
|
||||
try {
|
||||
currentUser.login(token);
|
||||
} catch (IncorrectCredentialsException ice) { …
|
||||
} catch (LockedAccountException lae) { …
|
||||
}
|
||||
…
|
||||
catch (AuthenticationException ae) {…
|
||||
}
|
||||
```
|
||||
**当用户通过身份认证之后,其被认为是“通过身份认证的”,并且被允许使用应用。但是,通过身份认证并不意味着可以在系统中执行任何操作。通过授权,可以决定用户能够在系统中执行哪些操作。**
|
||||
|
||||
## Authorization
|
||||
Authorization的实质是访问控制,通常用于控制用户在系统中能够使用哪些资源。大多数情况下,可以通过role或permission等形式来实现访问控制,用户通过分配给他们的角色或者权限来执行操作。通过检查用户的role或者permission,系统可以决定将哪些功能暴露给用户。
|
||||
```java
|
||||
// 通过如下代码,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?
|
||||
}
|
||||
```
|
||||
权限控制甚至支持非常细粒度的权限,譬如实例级别的权限控制。
|
||||
```java
|
||||
// 如下代码检测用户是否拥有删除jsmith用户的权限
|
||||
if ( subject.isPermitted(“user:delete:jsmith”) ) {
|
||||
//delete the ‘jsmith’ user
|
||||
} else {
|
||||
//don’t 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客户端时很有用)。
|
||||
```java
|
||||
/**
|
||||
* 用户获取Session
|
||||
*/
|
||||
// 如果当前用户存在会话,则获取已存在会话,
|
||||
// 如果当前用户不存在会话,则创建新的会话
|
||||
Session session = subject.getSession();
|
||||
// 接受一个boolean值,该boolean值标识如果当前用户不存在会话,是否创建一个新的会话
|
||||
Session session = subject.getSession(boolean create);
|
||||
```
|
||||
### Shiro Session可在任何应用中使用
|
||||
```java
|
||||
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值,可以通过如下方式快速计算:
|
||||
```java
|
||||
String hex = new Md5Hash(myFile).toHex();
|
||||
```
|
||||
在shiro hash中,如果想要对密码进行sha-512进行hash操作并且对结果进行base64编码成可打印字符串,可以进行如下操作:
|
||||
```java
|
||||
String encodedPassword =
|
||||
new Sha512Hash(password, salt, count).toBase64();
|
||||
```
|
||||
Shiro框架在很大程度上简化了hash和编码。
|
||||
### Shiro Ciphers
|
||||
Ciphers是一种算法,可以通过指定的key将加密后的数据还原到加密之前(不同于hash操作,hash操作通常是不可逆的)。通常用Ciphers来保证数据传输时的安全,防止数据在传输时被窥探。
|
||||
Shiro通过引入CipherService API来简化加密的流程,**CipherService是一个简单,无状态并且线程安全的API,可以对应用数据进行加密或者解密操作。**
|
||||
```java
|
||||
// Shiro CipherService对数据进行加密操作
|
||||
|
||||
AesCipherService cipherService = new AesCipherService();
|
||||
cipherService.setKeySize(256);
|
||||
//create a test key:
|
||||
byte[] testKey = cipherService.generateNewKey();
|
||||
|
||||
//encrypt a file’s 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时的任何代码。**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user