Shiro 安全框架(二)

Shiro 权限框架(二)

上一章节学了 Shiro 框架的简单结构和两个重要的方法的,接下来要学习如何使用框架的认证授权

自定义Realm

一般我们的用户名和密码数据都是来自数据库中的,所以自定义 Realm 读取数据库中的数据是非常有必要的。

按照我们流程分析,我们自定义的 Realm 要继承 AuthorizingRealm 这个类,并重写两个方法:

  1. doGetAuthticationInfo() 认证
  2. doGetAuthorizationInfo() 授权

认证——doGetAuthticationInfo()

代码实现:

@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 获取用户名
        String principal = (String) authenticationToken.getPrincipal();
        System.out.println(principal);

        // 开始根据用户名查询数据库中用户名所对应的密码。
        if ("xiaocheng".equals(principal)){
            // 此处数据用于连接数据库得到的数据

            // SimpleAuthenticationInfo 继承 AuthenticationInfo
            // AuthenticationInfo 保存了正确用户的基本信息
            // 参数1:用户名    参数2:密码     参数3:当前的 realm 名(this.getName())
            return new SimpleAuthenticationInfo(principal, "123", this.getName());
        }
        return null;
    }

MD5 + salt 加密

我们有时候不能明文的在数据库中保存用户的密码,我们需要对其进行加密处理。

同样也是要自定义 Realm 的。

MD5 加密

Realm 的 doGetAuthenticationInfo():

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 获取用户名
    String principal = (String) authenticationToken.getPrincipal();
    // 查询数据库比较

    if (principal.equals("xiaocheng")){
        // md5
        return new SimpleAuthenticationInfo(principal,"202cb962ac59075b964b07152d234b70",this.getName());
    }
    return null;
}

test:

// 自定义 realm 使用 CustomMd5Realm md5realm
CustomMd5Realm realm = new CustomMd5Realm();

// 创建 hash 凭证认证器,密码认证是使用 --> HashedCredentialsMatcher 是 SimpleCredentialsMatcher 的子类,如果使用 hash,就是简单的 equals 比较。
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用 md5 算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");

// 设置 realm 使用 hash 凭证匹配器
realm.setCredentialsMatcher(hashedCredentialsMatcher);

自此就在自定义的 realm 中添加了 md5 加密认证器,在匹配 subject 时,就会按照匹配器来匹配用户。

md5 + salt

Realm 的 doGetAuthenticationInfo():

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 获取用户名
    String principal = (String) authenticationToken.getPrincipal();
    // 查询数据库比较

    if (principal.equals("xiaocheng")){
        // md5 + salt
        // 参数三 : salt,需要借助 ByteSource.Util.bytes() 完成加盐操作
        return new SimpleAuthenticationInfo(principal,"cc1e39955047cba6c751ad1a6214af26",ByteSource.Util.bytes("salt"),this.getName());
    }
    return null;
}

test:

// 自定义 realm 使用 CustomMd5Realm md5realm
CustomMd5Realm realm = new CustomMd5Realm();

// 创建 hash 凭证认证器,密码认证是使用 --> HashedCredentialsMatcher 是 SimpleCredentialsMatcher 的子类,如果使用 hash,就是简单的 equals 比较。
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用 md5 算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");

// 设置 hash 散列次数
hashedCredentialsMatcher.setHashIterations(1024);
// 设置 realm 使用 hash 凭证匹配器
realm.setCredentialsMatcher(hashedCredentialsMatcher);

加盐处理就在 doGetAuthenticationInfo() 中的返回值中添加参数,但是加盐处理使用的是一个工具类 ByteSource.Utile.bytes("salt")

md5 + salt + hash散列

Realm 的 doGetAuthenticationInfo():

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 获取用户名
    String principal = (String) authenticationToken.getPrincipal();
    // 查询数据库比较

    if (principal.equals("xiaocheng")){
        // md5 + salt + 散列
        return new SimpleAuthenticationInfo(principal,"d10ce44fb96e787ef5a57c1aed5da166",ByteSource.Util.bytes("salt"),this.getName());
    }
    return null;
}

test:

// 自定义 realm 使用 CustomMd5Realm md5realm
CustomMd5Realm realm = new CustomMd5Realm();

// 创建 hash 凭证认证器,密码认证是使用 --> HashedCredentialsMatcher 是 SimpleCredentialsMatcher 的子类,如果使用 hash,就是简单的 equals 比较。
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用 md5 算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");

// 设置 hash 散列次数
hashedCredentialsMatcher.setHashIterations(1024);
// 设置 realm 使用 hash 凭证匹配器
realm.setCredentialsMatcher(hashedCredentialsMatcher);

在设置认证凭证的时候设置 hash 散列次数.

小结

都是使用 hash 凭证认证器 ==> HashedCredentialsMatcher

  • md5 加密: hashedCredentialsMatcher.setHashAlgorithmName("md5") 发生在认证器上。
  • md5 + salt 加密: return SimpleAuthenticationInfo(principal,"password",ByteSource.Util.bytes("salt"),this.getName()) 发生在自定义 Realm 上。
  • md5 + salt + hash 加密: hashedCredentialsMatcher.setHashIterations(1023) 发生在认证器上。

合理的使用加密可以让我们的认证更加安全。这一节需要了解到加密的操作都具体在那些地方,并不是都发生在认证器中,加盐操作就在 Realm 中完成。认证通过后就要进入下一个步骤——授权

授权——doGetAuthorizationInfo()

当用户通过认证为合法用户时,此时进入到授权过程。

其实授权就是:规定谁对什么资源进行怎么样的操作

每一个合法用户都有自己在当前项目中的权限,权限是和资源绑定在一起的。

授权方式

主流授权方式分为两种RBAC:

  • 基于角色进行访问控制
    对每一个角色进行权限划分,也就是说会给用户分配角色,每一个角色都只能访问它的权限范围。

  • 基于资源进行访问控制
    以资源为核心进行访问控制,规定资源能被谁操作,如何操作。

权限字符串

权限字符串的规则是:资源标识符:操作:资源实例标识符
例子:

  • 用户创建权限:user:createuser:create:*
  • 用户修改实例test的权限:user:update:test
  • 用户操作test的所有权限:user:*:test

可以使用通配符 *

授权实现

在自定义 Realm 中,我们上面实现了 doGetAuthenticationInfo() 方法来认证,还有一个 doGetAuthoriztionInfo() 就是用来授权的。

所以完成授权我们就要在这个方法中完成。

当用户请求授权时,需要查询数据库中用户对应的具体权限完成授权。

Realm 配置:

// 授权配置
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

    // 打印用户的主角色
    String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
    System.out.println("用户名:"+primaryPrincipal);


    // 创建授权信息 —— AuthorizationInfo,可以使用 SimpleAuthorizationInfo
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

    // 将数据库中查询的角色信息赋值给权限对象
    simpleAuthorizationInfo.addRole("admin");
    simpleAuthorizationInfo.addRole("user");

    // 将数据库中查询的权限信息赋值给权限对象
    simpleAuthorizationInfo.addStringPermission("admin:*");
    simpleAuthorizationInfo.addStringPermission("user:create");

    return simpleAuthorizationInfo;
}

请求授权:

// 认证通过
if (subject.isAuthenticated()){
    // 1. 基于角色权限控制
    System.out.println(subject.hasRole("admin")); // 查询主体是否有 admin 这个角色

    // 2. 多角色权限控制
    boolean b = subject.hasAllRoles(Arrays.asList("admin","user")); // 查询主体是否有 admin、user 这两个角色,如果没有返回false
    System.out.println(b);

    // 3. 查看主体分别有那些角色

    boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "super", "user"));
    for (boolean aBoolean : booleans) {
        System.out.println(aBoolean);
    }

    System.out.println("================基于权限字符串的访问控制================");

    // 4. 基于权限字符串的访问控制  资源标识符:操作:资源类型

    // 查看主体是否具有 对 admin 下的 001 资源具有 create 操作的权限
    System.out.println(subject.isPermitted("admin:create:001"));

    // 查询主体分别对 "admin:create:*"、"user:*:001" 是否具有权限
    boolean[] permitted = subject.isPermitted("admin:create:*", "user:*:001","user:create:002");
    for (boolean b1 : permitted) {
        System.out.println(b1);
    }

    // 查看主体是否同时具有权限
    System.out.println(subject.isPermittedAll("admin:create:*", "user:update:001","user:create:002"));
}

小结

本章学习了自定义 Realm 继承 AuthorizingRealm 中的认证和授权方法的使用。还有认证过程中的数据加密。下面就要学习 Spring Boot 结合数据库整合 Shiro 去使用。

posted @ 2021-12-28 23:45  CN_DADA  阅读(47)  评论(0编辑  收藏  举报