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

关键对象

授权可简单理解为who对what(which)进行How操作:

Who,即主体(Subject),主体需要访问系统中的资源。

What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型资源实例,比如商品信息为资源类型,类型为t01的商品为资源实例,编号为001的商品信息也属于资源实例。

How,权限/许可(Permission),规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。

授权流程

image-20211214204047080

授权方式

基于角色的访问控制

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

1
2
3
if(subject.hasRole("admin")){
//操作什么资源
}

基于资源的访问控制

RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制

1
2
3
4
5
6
if(subject.isPermission("user:update:01")){ //资源实例
//对01用户进行修改
}
if(subject.isPermission("user:update:*")){ //资源类型
//对01用户进行修改
}

权限字符串

​ 权限字符串的规则是:资源标识符:操作:资源实例标识符,意思是对哪个资源的哪个实例具有什么操作,:是资源/操作/实例的分割符,权限字符串也可以使用*通配符。

例子:

  • 用户创建权限:user:create,或user:create:*
  • 用户修改实例001的权限:user:update:001
  • 用户实例001的所有权限:user:*:001

shiro中授权编程实现方式

编程式

1
2
3
4
5
6
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}

注解式

1
2
3
4
@RequiresRoles("admin")
public void hello() {
//有权限
}

标签式

1
2
3
4
5
JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:
<shiro:hasRole name="admin">
<!— 有权限—>
</shiro:hasRole>
注意: Thymeleaf 中使用shiro需要额外集成!

开发授权

image-20211215100738926

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
37
38
39
40
41
42
/**
* 使用自定义realm 加入 md5 + salt + hash
*/
public class CustomerMd5Realm extends AuthorizingRealm {


@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("MD5授权-----------");
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
System.out.println("身份信息:" + primaryPrincipal);

//根据身份信息 用户名 获取当前用户的角色信息,已经权限信息 kylin admin user
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//将数据库中查询角色信息赋值给权限对象
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");
simpleAuthorizationInfo.addStringPermission("user:*:01");
simpleAuthorizationInfo.addStringPermission("product:create");
return simpleAuthorizationInfo;
}

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取身份信息
String principal = (String) authenticationToken.getPrincipal();
//根据用户名查询数据库
if ("kylin".equals(principal)) {
//md5+salt "123"
return new SimpleAuthenticationInfo(
//数据库用户名
principal,
//数据库md5+salt之后的密码
"e4f9bf3e0c58f045e62c23c533fcf633",
//随机盐
ByteSource.Util.bytes("X0*7ps"),
//realm的名字
this.getName());
}
return null;
}
}

添加了两个角色

  1. admin

  2. user

两个权限字符串

  1. user:*:01
  2. product:create

当用户通过了认证,则会给该用户添加两个角色,两个权限字符串。

image-20211215102127035

public class TestCustomerMd5RealmAuthenticator {

    public static void main(String[] args) {
        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //注入realm
        CustomerMd5Realm realm = new CustomerMd5Realm();
        defaultSecurityManager.setRealm(realm);

        //设置realms使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5");
        credentialsMatcher.setHashIterations(1024);
        realm.setCredentialsMatcher(credentialsMatcher);

        //将安全管理器注入安全工具
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //通过安全工具类获取subject
        Subject subject = SecurityUtils.getSubject();

        //认证
        UsernamePasswordToken token = new UsernamePasswordToken("kylin", "123");

        subject.login(token);

        //认证用户进行授权
        if (subject.isAuthenticated()) {
            //基于角色权限控制
            System.out.println(subject.hasRole("admin"));

            //基于多角色的权限控制
            System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user")));

            //是否具有其中一个角色
            boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "super", "user"));
            for (boolean aBoolean : booleans) {
                System.out.println(aBoolean);
            }

            //基于权限字符串的访问控制 资源标识符:操作:资源类型
            System.out.println("权限:" + subject.isPermitted("user:update:01"));
            System.out.println("权限:" + subject.isPermitted("product:create:03"));

            //分别具有哪些权限
            boolean[] permitted = subject.isPermitted("user:*:01", "order:*:10");
            for (boolean b : permitted) {
                System.out.println(b);
            }
            //同时具有哪些权限
            boolean permittedAll = subject.isPermittedAll("user:*:01", "product:create:01");
            System.out.println(permittedAll);
        }
    }
}