Shrio-3-授权解析

关于授权

授权模块一般包含两块:角色和资源
先简单看下配置文件

jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=riversky
jdbcRealm.dataSource=$dataSource
jdbcRealm.permissionsLookupEnabled=true
;\#指定securityManager的authenticator实现
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator
;\#指定securityManager.authenticator的authenticationStrategy
allSuccessfulStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy
;授权
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver
authorizer.permissionResolver=$permissionResolver
securityManager.authorizer=$authorizer
;\#指定securityManager的realms实现
securityManager.realms=$jdbcRealm

Shrio中SecurityManager中需要在配置文件中指定Authorizer来进行授权的处理。而Authorizer中至关重要的两个部分就是角色和资源,其中对于Authorizer的实现类主要是角色解析和资源解析。

角色解析

RolePermissionResolver 用于根据角色字符串来解析得到权限集合。使用时不是必须的实现的。

public class MyRolePermissionResolver implements RolePermissionResolver {
    @Override
    public Collection<Permission> resolvePermissionsInRole(String roleString) {
        if("role1".equals(roleString)) {
            return Arrays.asList((Permission)new WildcardPermission("menu:*"));
        }
        return null;
    }
}

资源解析

何为资源?

一般在应用中用户所看到的和可以进行操作的各个 url对应的页面,模块,业务方法,数据的增删改查,和功能都可称之为资源。一般的表现形式大多靠get或者post返回的数据,以及get,post方式对应的url资源进行指定。
shiro的资源格式如下“资源:权限:实例”。
如user:delete:1表示对于user资源具有删除该资源下id为1的权限。
资源:可以进行任何方式的指定包含菜单、页面、按钮,以及业务数据的操作。
权限:view,delete,update,create
实例:id
并且为了达到资源的充分权限的方便配置。也可以用 * 来表示所有的意思。

SecurityManager的资源解析:

所有的权限最终都是与资源相结合的。因此当需要优化资源解析器的实时需要替代默认的WildcardPermissionResolver(PermissionResolver接口的实现类),进行自定义的权限解析。一般是基于比特位的认证方式,可根据自己的需求进行设计,一般用户量不是太大没必要进行该模块的设计,这里提供一个参考的实现

public class BitPermission implements Permission {
    private String resourceIdentify;
    private int permissionBit;
    private String instanceId;
    public BitPermission(String permissionString) {
        String[] array = permissionString.split("\\+");
        if(array.length > 1) {
            resourceIdentify = array[1];
        }
        if(StringUtils.isEmpty(resourceIdentify)) {
            resourceIdentify = "*";
        }
        if(array.length > 2) {
            permissionBit = Integer.valueOf(array[2]);
        }
        if(array.length > 3) {
            instanceId = array[3];
        }
        if(StringUtils.isEmpty(instanceId)) {
            instanceId = "*";
        }
    }
    @Override
    public boolean implies(Permission p) {
        if(!(p instanceof BitPermission)) {
            return false;
        }
        BitPermission other = (BitPermission) p;
        if(!("*".equals(this.resourceIdentify) || this.resourceIdentify.equals(other.resourceIdentify))) {
            return false;
        }
        if(!(this.permissionBit ==0 || (this.permissionBit & other.permissionBit) != 0)) {
            return false;
        }
        if(!("*".equals(this.instanceId) || this.instanceId.equals(other.instanceId))) {
            return false;
        }
        return true;
    }
}
public class BitAndWildPermissionResolver implements PermissionResolver {
    @Override
    public Permission resolvePermission(String permissionString) {
        if(permissionString.startsWith("+")) {
            return new BitPermission(permissionString);
        }
        return new WildcardPermission(permissionString);
    }
}

综合认证和授权–AuthorizingRealm 抽象类

通过继承该抽象类后实现其中的认证和授权模块,可以灵活的对Realm进行实现(摆脱固定的数据库形式,按照自己的解析方式进行认证和授权)

public class MyRealm extends AuthorizingRealm {
//认证模块
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
          String username= (String) authenticationToken.getPrincipal();
        String password=new String((char[])authenticationToken.getCredentials());
        if(!"zhang".equals(username)){
            //用户名错误
            throw new UnknownAccountException();
        }
        if(!"123".equals(password)){
            //认证错误
            throw new IncorrectCredentialsException();
        }
        return new SimpleAuthenticationInfo(username,password,getName())  
}
//授权模块
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRole("role1");
        authorizationInfo.addRole("role2");
        authorizationInfo.addObjectPermission(new BitPermission("+user1+10"));
        authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
        authorizationInfo.addStringPermission("+user2+10");
        authorizationInfo.addStringPermission("user2:*");
        return authorizationInfo;
    }
}

常规的数据库方式使用–最简单的数据库使用方式

因为该方式按照官方的数据库字段和资源字段的定义,因此有局限性。当然使用上也是最简便的。
第一步maven依赖

 <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.25</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>0.2.23</version>
    </dependency>

第二步创建数据
数据库创建和资源的定义(mysql中存储)

drop database if exists shiro;
create database shiro;
use shiro;

create table users (
  id bigint auto_increment,
  username varchar(100),
  password varchar(100),
  password_salt varchar(100),
  constraint pk_users primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_users_username on users(username);

create table user_roles(
  id bigint auto_increment,
  username varchar(100),
  role_name varchar(100),
  constraint pk_user_roles primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_user_roles on user_roles(username, role_name);

create table roles_permissions(
  id bigint auto_increment,
  role_name varchar(100),
  permission varchar(100),
  constraint pk_roles_permissions primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_roles_permissions on roles_permissions(role_name, permission);

insert into users(username,password)values('zhang','123');

# --------------------------授权阶段,提供角色信息和其他的资源等-------------------------------
delete from users;
delete from user_roles;
delete from roles_permissions;
insert into users(username, password, password_salt) values('zhang', '123', null);
insert into user_roles(username, role_name) values('zhang', 'role1');
insert into user_roles(username, role_name) values('zhang', 'role2');
insert into roles_permissions(role_name, permission) values('role1', '+user1+10');
insert into roles_permissions(role_name, permission) values('role1', 'user1:*');
insert into roles_permissions(role_name, permission) values('role1', '+user2+10');
insert into roles_permissions(role_name, permission) values('role1', 'user2:*');

第三步:SecurityManager配置(注意jdbcRealm.permissionsLookupEnabled设置为true,不然进行资源的授权解析)

jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=riversky
jdbcRealm.dataSource=$dataSource
jdbcRealm.permissionsLookupEnabled=true
;\#指定securityManager的authenticator实现
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator
;\#指定securityManager.authenticator的authenticationStrategy
allSuccessfulStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy
;授权
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver
authorizer.permissionResolver=$permissionResolver
securityManager.authorizer=$authorizer
;\#指定securityManager的realms实现
securityManager.realms=$jdbcRealm

第四步:测试

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

/**
 * @author riversky E-mail:riversky@126.com
 * @version 创建时间 : 2018/1/23.
 */
public class LoginController {
    public void login(String configFile,String username,String pwd){
        Factory<SecurityManager> factory=new IniSecurityManagerFactory(configFile);
        SecurityManager manager=factory.getInstance();
        SecurityUtils.setSecurityManager(manager);
        Subject subject=SecurityUtils.getSubject();
        UsernamePasswordToken token=new UsernamePasswordToken(username,pwd);
        subject.login(token);
    }
}
import cn.riversky.controller.LoginController;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

/**
 * @author riversky E-mail:riversky@126.com
 * @version 创建时间 : 2018/1/24.
 */
public class TestChar4 {
    private LoginController loginController;
    @Before
    public void init(){
        loginController=new LoginController();
    }
    @Test
    public void shou(){
        loginController.login("classpath:shiro-realm.ini","zhang","123");
        Subject subject= SecurityUtils.getSubject();
        System.out.println(subject.hasRole("role1"));
        System.out.println(subject.isPermitted("user:create"));
//        System.out.println(subject.isPermitted("user+2"));
//判断拥有权限:user:update and user:delete

        System.out.println(subject.isPermittedAll("user:update", "user:delete"));
        //判断没有权限:user:view
        System.out.println(subject.isPermitted("user:view"));
    }
}

授权测试

授权扩展–自定义方式的测试

这里数据库数据就现在自己在授权和验证中模拟数据源(mysql或者其他配置文件的数据)
我们的目的是随心所遇的创建自己的角色解析方式(基于角色的授权)和资源解析方式(按照字节优化的方式)
这里需要关注的事件主要有-角色解析(通过自定义角色解析器然后注入到配置文件),资源解析(通过自定的资源解析其注入到配置文件)、Relm(通过继承AuthorizingRealm实现认证以及权限验证赋予过程),配置文件(组件SecurityManager),+测试文件

  • 角色解析
package cn.riversky.controller;

import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.RolePermissionResolver;
import org.apache.shiro.authz.permission.WildcardPermission;

import java.util.Arrays;
import java.util.Collection;

/**
 * 通过该方式可以实现粗粒度的基于角色的权限赋予
 * @author riversky E-mail:riversky@126.com
 * @version 创建时间 : 2018/1/24.
 */
public class MyRolePermissionResolver implements RolePermissionResolver{

    @Override
    public Collection<Permission> resolvePermissionsInRole(String s) {
        //这里其实应该从dao中进行角色查询,然后添加权限的。这里进行了写死的权限赋予
        if("role1".equals(s)) {
            return Arrays.asList((Permission)new WildcardPermission("menu:*"));
        }
        return null;
    }
}
  • 资源解析
package cn.riversky.controller;

import com.alibaba.druid.util.StringUtils;
import org.apache.shiro.authz.Permission;

/**
 * 优化权限系统(可以根据需求进行分割符号的指定)
 * 权限字符串格式:+ 资源字符串 + 权限位 + 实例 ID;以 + 开头中间通过 + 分割;权限:0 表示所有权限;1 新增(二进制:0001)、2 修改(二进制:0010)、4 删除(二进制:0100)、8 查看(二进制:1000);如 +user+10 表示对资源 user 拥有修改 / 查看权限。
 * @author riversky E-mail:riversky@126.com
 * @version 创建时间 : 2018/1/24.
 */
public class BitPermission implements Permission {

    private String resourceIdentify;
    private int permissionBit;
    private String instanceId;
    public BitPermission(String permissionString) {
        String[] array = permissionString.split("\\+");
        if(array.length > 1) {
            resourceIdentify = array[1];
        }
        if(StringUtils.isEmpty(resourceIdentify)) {
            resourceIdentify = "*";
        }
        if(array.length > 2) {
            permissionBit = Integer.valueOf(array[2]);
        }
        if(array.length > 3) {
            instanceId = array[3];
        }
        if(StringUtils.isEmpty(instanceId)) {
            instanceId = "*";
        }
    }
    @Override
    public boolean implies(Permission p) {
        if(!(p instanceof BitPermission)) {
            return false;
        }
        BitPermission other = (BitPermission) p;
        if(!("*".equals(this.resourceIdentify) || this.resourceIdentify.equals(other.resourceIdentify))) {
            return false;
        }
        if(!(this.permissionBit ==0 || (this.permissionBit & other.permissionBit) != 0)) {
            return false;
        }
        if(!("*".equals(this.instanceId) || this.instanceId.equals(other.instanceId))) {
            return false;
        }
        return true;
    }
}
package cn.riversky.controller;

import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.authz.permission.WildcardPermission;

/**
 * @author riversky E-mail:riversky@126.com
 * @version 创建时间 : 2018/1/24.
 */
public class BitAndWildPermissionResolver implements PermissionResolver {
    @Override
    public Permission resolvePermission(String s) {
        if(s.startsWith("+")) {
            return new BitPermission(s);
        }
        return new WildcardPermission(s);
    }
}
  • Relm
import com.alibaba.druid.util.StringUtils;
import org.apache.shiro.authc.*;
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 java.util.ArrayList;
import java.util.List;

/**
 * @author riversky E-mail:riversky@126.com
 * @version 创建时间 : 2018/1/24.
 */
public class MyReamlm extends AuthorizingRealm {
    /**
     * 添加角色
     * Role自己设计
     * 思路:应该通过Dao查询到role,并且通过Role.getRoleName()进行角色查询
     * @param username
     * @param info
     */
    private void addRole(String username, SimpleAuthorizationInfo info) {
//        List<Role> roles = roleDao.findByUser(username);
        List<String> roles = new ArrayList<String>();
        roles.add("role1");
        roles.add("role2");
        if(roles!=null&&roles.size()>0){
            for (String role : roles) {
                info.addRole(role);
            }
        }
    }

    /**
     * 添加权限
     * 资源类Permission需要自己设计
     * @param username
     * @param info
     * @return
     */
    private SimpleAuthorizationInfo addPermission(String username,SimpleAuthorizationInfo info) {
//        List<Permission> permissions = permissionDao.findPermissionByName(username);
        List<String> permissions = new ArrayList<String>();
        permissions.add("+user2+10");
        permissions.add("user3:*");
        //具有user1的修改和查看权限
        permissions.add("+user1+10");
        for (String permission : permissions) {
            info.addStringPermission(permission);//添加权限
//            info.addStringPermission(permission.getUrl());//添加权限
        }
        return info;
    }
    //认证模块
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username= (String) token.getPrincipal();
        String password=new String((char[])token.getCredentials());
        //用户名错误zhang--应该通过Service层进行校验
        if(!"zhang".equals(username)){
            throw new UnknownAccountException();
        }
        //认证错误--应该通过Service层进行校验
        if(!"123".equals(password)){

            throw new IncorrectCredentialsException();
        }
        return new SimpleAuthenticationInfo(username,password,getName());
    }
    //授权模块
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username= (String) principals.fromRealm(getName()).iterator().next();
        if(!StringUtils.isEmpty(username)){
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            addRole(username,authorizationInfo);
            addPermission(username,authorizationInfo);
            return authorizationInfo;
        }
        return null;
    }
}
  • 配置文件

authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
;权限解析
permissionResolver= cn.riversky.controller.BitAndWildPermissionResolver
authorizer.permissionResolver=$permissionResolver
securityManager.authorizer=$authorizer
;角色解析--基于角色的权限赋予
rolePermissionResolver= cn.riversky.controller.MyRolePermissionResolver
authorizer.rolePermissionResolver=$rolePermissionResolver
;\#指定securityManager的realms实现
realm= cn.riversky.controller.MyReamlm
securityManager.realms=$realm
  • 测试文件
package cn.riversky.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

/**
 * @author riversky E-mail:riversky@126.com
 * @version 创建时间 : 2018/1/23.
 */
public class LoginController {
    public void login(String configFile,String username,String pwd){
        Factory<SecurityManager> factory=new IniSecurityManagerFactory(configFile);
        SecurityManager manager=factory.getInstance();
        SecurityUtils.setSecurityManager(manager);
        Subject subject=SecurityUtils.getSubject();
        UsernamePasswordToken token=new UsernamePasswordToken(username,pwd);
        subject.login(token);
    }
}
 @Test
    public void demo(){
        loginController.login("classpath:shirio-ream.ini","zhang","123");
        Subject subject= SecurityUtils.getSubject();
        System.out.println(subject.hasRole("role1"));
        //基于角色的验证方式
        System.out.println(subject.isPermitted("menu:create:1"));
        //基于资源的验证方式
        System.out.println(subject.isPermitted("user3:view"));
        System.out.println(subject.isPermitted("+user2+10"));//新增和查看权限
    }

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值