Shiro中的授权

授权

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

关键对象

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

授权方式

基于角色的访问控制:

  • RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制
if(subject.hasRole("admin")){ 
//操作什么资源
}

·基于资源的访问控制

  • RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制
if(subject.isPermisson("user:*:find"))
{
    //对所有用户具有查询权限
}

if(subject.isPermisson("user:update:01"))
{
    //对01用户进行修改
}

权限字符串

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

例子:

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

Shiro中授权编程实现方式

1.编程式

Subject subject=SecurityUtils.getSubject();
if(subject.hashRole("admin")){
    //有权限
}else{
    //无权限
}

2.注解式

@RequiresRoles("admin")
public void hello(){
    //有权限
}

3.标签式

JSP/GSP标签:在JSP/GSP页面通过相应的标签完成
<shiro:hasRole name="admin">
    <!-有权限->
</shiro:hasRole>

注意:Thymeleaf中使用shiro需要额外的集成!

授权Demo

1.realm的实现

package org.syc.realm;

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
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;

/**
 * 自定义realm,加盐处理
 * 加入 md5+salt+hash散列
 */
public class CustomerMd5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        System.out.println("进入授权方法++++++++++++++++++++");
        //可以根据身份信息principalCollection获取当前用户的角色信息(从数据库),以及权限信息
        //假设xiaochen 有两个角色  管理员admin      用户user
       /* String primaryPrincipal = (String)principalCollection.getPrimaryPrincipal();
        System.out.println("身份信息:"+primaryPrincipal);*/
        SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();

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

        //将数据库中查询的权限信息赋值给权限对象
        //以下使用假数据"user:*:01" 即:当前用户对01实例具有一切操作权限
        //"product:create"   对product具有创建权限
        simpleAuthorizationInfo.addStringPermission("user:*:01");
        simpleAuthorizationInfo.addStringPermission("product:create");
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取身份信息
        String principal=(String) token.getPrincipal();
        if("xiaochen".equals(principal)){
            //参数1:返回数据库中正确的用户名;
            //参数2:返回数据库中的正确md5+salt处理后的密码(可根据用户名principal在数据库中查出,
            //下面先使用假数据 :"123"经过md5+salt+hash处理后得到的32位字符串,其中sale为"X^^#DD",散列次数为1024);
            //参数3:注册时处理密码的随机盐
            //参数4:提供当前realm的名字,this.getName();
            SimpleAuthenticationInfo info= new SimpleAuthenticationInfo
                    (principal,
                    "cf4eb5b9c210015849a5da931f0a3f37",
                     ByteSource.Util.bytes("X^^#DD"),
                     this.getName());
            return info;
        }
        return null;
    }
}

2.测试类:

package org.syc;

import com.sun.org.apache.bcel.internal.generic.FSUB;
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 org.syc.realm.CustomerMd5Realm;
import org.syc.realm.CustomerRealm;

import java.util.ArrayList;
import java.util.List;

public class TestCustomerMD5RealmAuthenticator {
    public static void main(String[] args) {
        //创建securityManager
        DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
        //设置自定义realm
        CustomerMd5Realm realm=new CustomerMd5Realm();
        /**
         * 修改shiro的默认密码校验策略
         * 设置realm使用hash凭证匹配器
         */
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5");//设置凭证匹配器的匹配策略,采用的算法
        credentialsMatcher.setHashIterations(1024);//散列次数
        realm.setCredentialsMatcher(credentialsMatcher);
        defaultSecurityManager.setRealm(realm);
        //给安全工具类设置将安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //通过安全工具类获取subject
        Subject subject=SecurityUtils.getSubject();
        //创建token
        UsernamePasswordToken token=new UsernamePasswordToken("xiaochen","123");
        //认证
        try {
            subject.login(token);
            System.out.println("登录成功了");
            System.out.println(subject.isAuthenticated());
        }
        catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("认证失败:用户名不存在");
        }
        catch ( IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("认证失败:密码错误");
        }


        // 认证用户的授权

        // 1.基于单角色的授权
       if(subject.isAuthenticated()){
            System.out.println("是否具有管理员角色:"+subject.hasRole("admin"));
            System.out.println("是否具有用户角色:"+subject.hasRole("user"));
        //2.基于多角色的授权
           ArrayList list=new ArrayList();
           list.add("user");
           list.add("admin");
        System.out.println("是否同时具有管理员和用户两个角色:"+ subject.hasAllRoles(list));
           //3.基于是否具有其中一个角色的授权
           boolean[] booleans=subject.hasRoles(Arrays.asList("user","admin","super"));
           for (boolean b:booleans
                ) {
               System.out.println("是否具有管理员和用户和超级管理员其中的一个角色:"+ b);
      }System.out.println("================================================================================");

           //4.基于权限字符串的访问控制  →   资源访问符:操作:资源类型
           System.out.println("当前用户是否具有对01的更新权限:"+subject.isPermitted("user:update:01"));
           System.out.println("当前用户是否具有product创建权限:"+subject.isPermitted("product:create"));
           System.out.println("当前用户是否同时具有对01的更新权限和product创建权限:"+subject.isPermittedAll("user:update:01","product:create"));
       }
    }
}

3.运行结果:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
登录成功了
true
进入授权方法++++++++++++++++++++
是否具有管理员角色:true
进入授权方法++++++++++++++++++++
是否具有用户角色:true
进入授权方法++++++++++++++++++++
进入授权方法++++++++++++++++++++
是否同时具有管理员和用户两个角色:true
进入授权方法++++++++++++++++++++
进入授权方法++++++++++++++++++++
进入授权方法++++++++++++++++++++
是否具有管理员和用户和超级管理员其中的一个角色:true
是否具有管理员和用户和超级管理员其中的一个角色:true
是否具有管理员和用户和超级管理员其中的一个角色:false
================================================================================
进入授权方法++++++++++++++++++++
当前用户是否具有对01的更新权限:true
进入授权方法++++++++++++++++++++
当前用户是否具有product创建权限:true
进入授权方法++++++++++++++++++++
进入授权方法++++++++++++++++++++
当前用户是否同时具有对01的更新权限和product创建权限:true

Process finished with exit code 0