【Shiro】3 Shiro授权流程

授权

授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。

授权的关键对象

授权可简单理解为Who对What / Which进行How操作。

  • Who:主体(Subject),主体需要访问系统中的资源。
  • What / Which:资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型资源实例,比如商品信息为资源类型,类型为Type01的商品为资源实例,编号为001的商品信息也属于资源实例。
  • How:权限(Permission),规定了主体对资源的操作许可。权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。

授权流程

授权一定是基于认证通过后才执行的操作。

20210927094653.png

授权方式(RBAC)

RBAC主要包含两种授权模式:

  • 基于角色的访问控制(Role-Based Access Control):以角色为中心进行访问控制。

伪代码可表示为:

1
2
3
java复制代码if (subject.hasRole("admin")) {
// 执行操作
}
  • 基于资源的访问控制(Resource-Based Access Control):以资源为中心进行访问控制。

伪代码可表示为:

1
2
3
java复制代码if (subject.isPermitted("user:find:*")) {
// 执行操作
}

isPermission 传入参数是一个权限字符串,其格式为 “资源类型 : 操作 : 资源实例”

“ user : find : * “ 表示的是Subject对所有User实例具有查询的权限(操作类型)。

“ user : * : 001 “ 表示的是Subject对ID为001的User实例具有所有的权限(操作实例)。

Shiro权限字符串

1. 组成规则

在Shiro中使用权限字符串必须按照Shiro指定的规则。

权限字符串组合规则为:“资源类型标识符 : 操作 : 资源实例标识符”

  • 资源类型标识符: 一般会按模块,对系统划分资源。比如user模块,product模块,order模块等,对应的资源类型标识符就是:user,product,order。
  • 操作: 一般为增删改查(create,delete,update,find),还有 * 标识统配。
  • 资源实例标识符: 如果Subject控制的是资源类型,那么资源实例标识符就是 “*“ ;如果Subject控制的是资源实例,那么就需要在资源实例标识符就是该资源的唯一标识(ID等)。

2. 示例

“ * : * : * “ 表示Subject对所有类型的所有实例有所有操作权限,相当于超级管理员。

“ user : create : * “ 表示Subject对user类型的所有实例有创建的权限,可以简写为:” user : create “。

“ user : update : 001 “ 表示Subject对ID为001的user实例有修改的权限。

“ user : * : 001 “ 表示Subject对ID为001的user实例有所有权限。

Shiro授权方式

Shiro中对于后台授权提供了三种实现方式:

  • 编程式
  • 注解式
  • 标签式(已淘汰,只能在JSP,Thymeleaf等模板引擎中使用)

1. 编程式

1
2
3
4
5
6
java复制代码Subject subject = SecurityUtils.getSubject();
if (subject.hasRole("admin")) {
// 有权限
} else {
// 无权限
}

2. 注解式

1
2
3
4
java复制代码@RequiresRoles("admin")
public void find() {
// 有权限
}

Shiro授权源码

SimpleAccountRealm 类就是Shiro默认完成认证和授权的底层操作,其中 doGetAuthenticationInfo 方法处理认证 ,doGetAuthorizationInfo 方法处理授权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
java复制代码public class SimpleAccountRealm extends AuthorizingRealm {

...

// 认证处理
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
SimpleAccount account = getUser(upToken.getUsername());

if (account != null) {

if (account.isLocked()) {
throw new LockedAccountException("Account [" + account + "] is locked.");
}
if (account.isCredentialsExpired()) {
String msg = "The credentials for account [" + account + "] are expired";
throw new ExpiredCredentialsException(msg);
}

}

return account;
}

// 授权处理
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = getUsername(principals);
USERS_LOCK.readLock().lock();
try {
return this.users.get(username);
} finally {
USERS_LOCK.readLock().unlock();
}
}

}

Shiro授权Demo

授权必须在认证的基础上,因此本Demo基于前文中MD5加密认证的Demo实现授权。

1. 基于角色访问控制

在定义Realm中的授权操作中给Subject添加角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
java复制代码public class UserMD5Realm extends AuthorizingRealm {

// 授权操作
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取身份信息(用户名)
String username = (String) principals.getPrimaryPrincipal();

// 根据身份信息从数据库中该用户的角色信息装载进入SimpleAuthorizationInfo
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");

// 返回权限信息对象
return simpleAuthorizationInfo;
}

// 认证操作
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
...

return null;
}
}

授权流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
java复制代码public static void main(String[] args) {

// 认证操作
...

// 如果认证成功,可以进行授权操作
if (subject.isAuthenticated()) {

// 验证Subject是否具有admin角色
if (subject.hasRole("admin")) {
// 具体授权操作
}

// 验证Subject是否同时具有admin角色和user角色
if (subject.hasAllRoles(Arrays.asList("admin", "user"))) {
// 具体授权操作
}

// 验证Subject是否具有以下角色中的一种或者多种
boolean[] hasRoles = subject.hasRoles(Arrays.asList("admin", "user"));
for (boolean hasRole : hasRoles) {
if (hasRole) {
// 具体授权操作
}
}
}
}

2. 基于权限访问控制

在定义Realm中的授权操作中给Subject添加授权信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
java复制代码public class UserMD5Realm extends AuthorizingRealm {

// 授权操作
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取身份信息(用户名)
String username = (String) principals.getPrimaryPrincipal();

// 根据身份信息从数据库中该用户的权限信息装载进入SimpleAuthorizationInfo
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermission("user:create:001");
simpleAuthorizationInfo.addStringPermission("user:update:*");

// 返回权限信息对象
return simpleAuthorizationInfo;
}

// 认证操作
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
...

return null;
}
}

授权流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
java复制代码public static void main(String[] args) {

// 认证操作
...

// 如果认证成功,可以进行授权操作
if (subject.isAuthenticated()) {

// 判断Subject是否有创建所有user实例的权限
if (subject.isPermitted("user:create:*")) {
// 具体授权操作
}

// 判断Subject同时具有创建所有user实例的权限和修改001号user实例的权限
if (subject.isPermittedAll("user:create:*", "user:update:001")) {
// 具体授权操作
}

// 判断Subject是否具有以下权限中的一种或多种
boolean[] permissions = subject.isPermitted("user:create:*", "user:update:001");
for (boolean permission : permissions) {
if (permission) {
// 具体授权操作
}
}
}
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%