【Shiro】 二、授权验证

声明

博文的内容学习自B站UP主 编程不良人 (UID:352224540)的教程,感谢能有这么好的教程 

https://www.bilibili.com/video/BV1uz4y197Zm?p=9

 

Shiro

shiro是一个基于java的安全框架,主要提供认证与授权服务。

 

认证——概念

在shiro框架中,系统的访问者被称为 Subject (主体)

  • Subject  的用户名属于 Principal (身份信息)
  • Subject  的密码属于 Credential(凭证信息)
  • Token (令牌) =  Principal  +  Credential  +   ...

每次访问系统时,Subject  都会带着它的令牌给 Shiro 进行 Authentication (身份认证),

Shiro 会派出 SecurityManager(安全管理器) 来处理 Subject 的令牌,

SecurityManager  会到 Realm(数据域)中核实令牌的信息,如果核实通过,意味着认证成功了,则被允许进入系统

 

授权——概念

Subject(主体) 通过了认证,进入到系统之后,在访问资源(比如说增删改查的功能)之前,还需要被 Shiro 验证 Subject 是否对某些资源拥有权限,即是否 Authorization(授权)。
授权的方式有两种:

  • RBAC(Role-Based Access Control  基于角色的访问控制

这个比较好理解,就是说什么样的角色拥有什么样的权限,例如:角色是admin时,拥有增删改查的资源的权限;而角色时user时,只有查的资源的权限。

  • RBAC(Role-Based Access Control  基于资源的访问控制

这个相对于上面的角色访问控制要细分一点(除了角色的概念),就是不仅仅要讨论资源,还有讨论什么样的资源实例。比如说查的资源有多种资源实例(查xx1,查xx2....),当用户user要查xx1时,Shiro会验证他是否拥有查权限中的查xx1的权限。在Shiro,会用 权限字符串 来表达用户与权限的关系。

"user:select:*"    //拥有user的select的所有权限

"user:select:xx1"  //拥有user的select xx1的权限

 

实操

工程沿用上一章做的自定义 MD5 Realm 例子

目录结构:

Shiro
--shiro-java
----src
------main
--------java
----------com
------------xilo
--------------realm
----------------CustomerRealm.java
----------------CustomerMD5Realm.java(MD5Realm)
--------------shiro
----------------TestAuthenticator.java
----------------TestCustomerRealmAuthenticator.java
----------------TestCustomerMD5RealmAuthenticator.java(使用自定义MD5Realm的shiro)
--------resources
----------shiro.ini
----pom.xml
--pom.xml
 

CustomerMD5Realm.java 

package com.xilo.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

public class CustomerMD5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String)token.getPrincipal();
        System.out.println("doGetAuthenticationInfo---用户名:"+principal);
        if("jack".equals(principal)){
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo("jack","923391846b52b2391ba3a3cfca311ab9", ByteSource.Util.bytes("01*1334px"),this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

 TestCustomerMD5RealmAuthenticator.java

package com.xilo.shiro;

import com.xilo.realm.CustomerMD5Realm;
import com.xilo.realm.CustomerRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;

import java.util.ArrayList;
import java.util.Arrays;

public class TestCustomerMD5RealmAuthenticator {
    public static void main(String[] args) {
        //1、创建安全管理器对象
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        //2、创建Realm
        CustomerMD5Realm realm = new CustomerMD5Realm();

        //3、设置Realm使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5");
        //散列1024次
        credentialsMatcher.setHashIterations(1024);
        realm.setCredentialsMatcher(credentialsMatcher);

        //4、给安全管理器设置【自定义realm】
        securityManager.setRealm(realm);
        //5、给全局安全工具类设置安全管理器
        SecurityUtils.setSecurityManager(securityManager);
        //6、关键对象 Subject 主体
        Subject subject = SecurityUtils.getSubject();
        //7、创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("jack","001");

        //用户登录(进行验证)
        try{
            subject.login(token);
            System.out.println("认证状态:"+subject.isAuthenticated());
        }catch (UnknownAccountException e){
            System.out.println("用户"+token.getUsername()+"不存在");
        }catch (IncorrectCredentialsException e){
            System.out.println("用户"+token.getUsername()+"密码错误");
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

当我们在完成 Subject 身份验证之后,就要开始验证用户的授权情况了,首先谈基于角色的访问控制,我们先查一下当前 Subject 拥有那些角色

package com.xilo.shiro;

import com.xilo.realm.CustomerMD5Realm;
import com.xilo.realm.CustomerRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;

import java.util.ArrayList;
import java.util.Arrays;

public class TestCustomerMD5RealmAuthenticator {
    public static void main(String[] args) {
        //1、创建安全管理器对象
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        //2、创建Realm
        CustomerMD5Realm realm = new CustomerMD5Realm();

        //3、设置Realm使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5");
        //散列1024次
        credentialsMatcher.setHashIterations(1024);
        realm.setCredentialsMatcher(credentialsMatcher);

        //4、给安全管理器设置【自定义realm】
        securityManager.setRealm(realm);
        //5、给全局安全工具类设置安全管理器
        SecurityUtils.setSecurityManager(securityManager);
        //6、关键对象 Subject 主体
        Subject subject = SecurityUtils.getSubject();
        //7、创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("jack","001");

        //用户登录(进行验证)
        try{
            subject.login(token);
            System.out.println("认证状态:"+subject.isAuthenticated());
        }catch (UnknownAccountException e){
            System.out.println("用户"+token.getUsername()+"不存在");
        }catch (IncorrectCredentialsException e){
            System.out.println("用户"+token.getUsername()+"密码错误");
        }catch (Exception e){
            e.printStackTrace();
        }

        //验证授权
        if(subject.isAuthenticated()){
            //查看 subject 是否有 admin 角色
            System.out.println(subject.hasRole("admin"));
            //查看 subject 是否有 user1 和 user2 角色
            System.out.println(subject.hasAllRoles(Arrays.asList("user1","user2")));
            //查看 subject 是否有 admin 或 user1 或 user2 角色
            boolean[] booleans = subject.hasRoles(Arrays.asList("admin","user1","user2"));
            for(boolean bo : booleans){
                System.out.println(bo);
            }

        }
    }
}

运行:

doGetAuthenticationInfo---用户名:jack
认证状态:true
false
false
false
false
false

什么角色都没有,因为当前 Subject 没有被授权任何角色,在上一章中我们知道当 Subject 进行认证时会进入到 Realm 中去调用 doGetAuthenticationInfo 进行身份认证相关的操作,类似的, Subject 要进行授权时,也要进入到 Realm 的 doGetAuthorizationInfo 中去进行授权认证的相关操作。下面我们可以验证一下

package com.xilo.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

public class CustomerMD5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("doGetAuthorizationInfo---"+"进行认证:"+principalCollection.getPrimaryPrincipal());
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String)token.getPrincipal();
        System.out.println("doGetAuthenticationInfo---用户名:"+principal);
        if("jack".equals(principal)){
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo("jack","923391846b52b2391ba3a3cfca311ab9", ByteSource.Util.bytes("01*1334px"),this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

运行之后,可以看到,每次进行角色验证时,都会调用到 Realm 中的 doGetAuthorizationInfo

doGetAuthenticationInfo---用户名:jack
认证状态:true
doGetAuthorizationInfo---进行认证:jack
false
doGetAuthorizationInfo---进行认证:jack
false
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
false
false
false

接下来,我们就模拟给 Subject 赋予角色

package com.xilo.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

public class CustomerMD5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("doGetAuthorizationInfo---"+"进行认证:"+principalCollection.getPrimaryPrincipal());

        //添加角色
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user1");

        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String)token.getPrincipal();
        System.out.println("doGetAuthenticationInfo---用户名:"+principal);
        if("jack".equals(principal)){
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo("jack","923391846b52b2391ba3a3cfca311ab9", ByteSource.Util.bytes("01*1334px"),this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

运行之后,可以查到当前 Subject 拥有 admin 和 user1的角色

doGetAuthenticationInfo---用户名:jack
认证状态:true
doGetAuthorizationInfo---进行认证:jack
true
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
false
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
true
true
false

讲完了 基于角色的访问控制 的设置,下面来说 基于角色的访问控制 的设置,也就是以 权限字符串(资源标识符:操作:资源类型) 的形式描述权限信息

我们先在 Realm doGetAuthorizationInfo Subject 赋值权限信息

        //添加角色
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user1");

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

然后我们在 TestCustomerMD5RealmAuthenticator 查询 Subject 是否拥有该资源权限

       //基于权限字符串的访问控制(资源标识符:操作:资源类型)
       System.out.println("权限:"+subject.isPermitted("user1:select:xx1"));
       System.out.println("权限:"+subject.isPermittedAll("user1:select:xx1","user1:create:xx1"));
       booleans = subject.isPermitted("user1:select:xx1","user1:create:xx1","user1:delete:*");
       for(boolean bo : booleans){
           System.out.println(bo);
       }

运行结果:

doGetAuthenticationInfo---用户名:jack
认证状态:true
doGetAuthorizationInfo---进行认证:jack
true
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
false
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
true
true
false
doGetAuthorizationInfo---进行认证:jack
权限:true
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
权限:false
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
doGetAuthorizationInfo---进行认证:jack
true
false
true

Process finished with exit code 0

到此为止,基于javaSE使用Shiro的流程已经结束了,虽然还没用到数据库,只是模拟的数据,但是对于Shiro的运行基本流程有了个整体清晰的认识,后面将会整合到SpringBoot去做实操。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值