示例

  • 调用链:开始用户验证login()->AbstractAuthenticaticator.authenticate()->子类ModularRealmAuthenticator.doAuthenticate()判断有几个Realm,然后一个个执行->getAuthenticationInfo()->AuthenticatingRealm.getAuthenticationInfo()->MyRealm.doGetAuthenticationInfo()到此完成用户验证->
  • 开始授权BitAndWildPermissionResolver.resolvePermission()解析所需权限->MyRealm.doGetAuthorizationInfo()获取用户权限->BitAndWildPermissionResolver.resolvePermission()解析用户权限中存在的字符串权限+user2+10、user2:*->MyRolePermissionResolver.resolvePermissionInRole()解析用户角色role1、role2的权限,返回menu:*
  • 根据调用链从外到内
  • AuthorizerTest:测试类

    public class AuthorizerTest extends BaseTest {
        /**
         * @Author haien
         * @Description 测试MyRealm自己设置角色权限的授权方式
         * @Date 2019/2/19
         * @Param []
         * @return void
         **/
        @Test
        public void testIsPermitted(){
            login("classpath:config/shiro-jdbc-authorizer.ini","zhang","123");
            Assert.assertTrue(subject().isPermitted("user1:update"));
            Assert.assertTrue(subject().isPermitted("user2:update"));
            Assert.assertTrue(subject().isPermitted("+user1+2")); //新增
            Assert.assertTrue(subject().isPermitted("+user1+8")); //查看
            Assert.assertTrue(subject().isPermitted("+user2+10")); //新增与查看
            Assert.assertFalse(subject().isPermitted("+user1+4")); //没有删除权限
            //MyRolePermissionResolver解析得到的权限
            Assert.assertTrue(subject().isPermitted("menu:view"));
        }
    }
    
  • MyRealm:doGetAuthenticationInfo()被login()调用,完成用户验证

    public class MyRealm extends AuthorizingRealm { //继承AuthorizingRealm实现Realm接口
        /**
         * @Author haien
         * @Description 获取身份验证信息(和MyRealm1一样)
         * @Date 2019/2/19
         * @Param [authenticationToken]
         * @return org.apache.shiro.authc.AuthenticationInfo
         **/
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken authenticationToken) throws AuthenticationException {
            String username=(String)authenticationToken.getPrincipal(); //得到用户名
            String password=new String((char[])authenticationToken.getCredentials()); //得到密码
            //String password=(String)authenticationToken.getCredentials();
            //若用户名错误
            if(!"zhang".equals(username)){
                throw new UnknownAccountException();
            }
            //若密码错误
            if(!"123".equals(password)){
                throw new IncorrectCredentialsException();
            }
            //身份验证成功,返回一个AuthenticationInfo实现
            return new SimpleAuthenticationInfo(username,password,getName());
        }
    }
    
  • 如果想用数据库而不是自己准备用户和权限的话,可以配置shiro-jdbc-authorizer.ini,替换shiro-authorizer.ini和MyRealm类

    [main]
    ;自定义Authorizer
    authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
    ;自定义permissionResolver
    permissionResolver=com.haien.shiroHelloWorld.permission.BitAndWildPermissionResolver
    authorizer.permissionResolver=$permissionResolver
    ;自定义rolePermissionResolver
    rolePermissionResolver=com.haien.shiroHelloWorld.permission.MyRolePermissionResolver
    authorizer.rolePermissionResolver=$rolePermissionResolver
    
    securityManager.authorizer=$authorizer
    ;以上都和shiro-authorizer.ini一样
    
    ;自定义JdbcRealm;
        ;一定要放在SecurityManager。authorizer,因为调用setRealms()会将realms设置给Authorizer,
        ;并将前面给Authorizer设置的属性permissionResolver和RolePermissionResolver设置给realms
    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=123456
    jdbcRealm.dataSource=$dataSource
    jdbcRealm.permissionsLookupEnabled=true //开启权限从数据库查找功能
    securityManager.realms=$jdbcRealm
    
  • BitAndWildPermissionResolver:解析权限字符串(用户拥有的权限和要求的权限)

    /**
     * @Author haien
     * @Description 解析权限字符串:根据字符串是否以+开头解析为BitPermission或WildcardPermission
     * @Date 2019/2/19
     **/
    public class BitAndWildPermissionResolver implements PermissionResolver {
        /**
         * @Author haien
         * @Description 根据字符串是否以+开头解析为BitPermission或WildcardPermission
         * @Date 2019/2/19
         * @Param [s]
         * @return org.apache.shiro.authz.Permission
         **/
        @Override
        public Permission resolvePermission(String permissionString) {
            if(permissionString.startsWith("+")){
                return new BitPermission(permissionString);
            }
            return new WildcardPermission(permissionString);
        }
    }
    
  • 其中BitPermission实现了Permission接口

    • Permission接口
public interface Permission {
    //权限匹配
    boolean implies(Permission var1);
}
  • this是用户拥有的权限,var1是所需权限,当this是user:*或user,var1是user:create或两者完全相同时,返回true。

  • BitPermission

    /**
     * @Author haien
     * @Description 实现位移方式的权限定义,解析要求的权限。被BitAndWildPermissionResolver调用。
     *              规则:+资源+权限位。以+开头,中间通过+分隔;
     *              权限位:0-all权限(二进制0000),1-新增(0001),2-修改(0010),
     *              4-删除(0100)、8-查看(1000);如,+user+10,由于10二进制为1010,
     *              1位置与2、8相同,按位与返回二进制数非0,所以表示拥有修改、查看权限
     * @Date 2019/2/18
     **/
    public class BitPermission implements Permission {
        //资源
        private String resourceIdentify;
        //权限位
        private int permissionBit;
        //实例id
        private String instanceId;
    
        public BitPermission(String permissionString) {
            //解析要求的权限字符串
            String[] array=permissionString.split("\\+"); //加//防止+被解析
            if(array.length>1){
                resourceIdentify=array[1]; //第0个为空
            }
            if(StringUtils.isEmpty(resourceIdentify)){ //开头两个+相连,或者length<=1,即根本没定义权限
                resourceIdentify="*";
            }
            if(array.length>2){
                permissionBit=Integer.valueOf(array[2]);
            }//peimissionBit为空的话即默认值0,不用赋*给它,后面用0来判断即可
            if(array.length>3){
                instanceId=array[3];
            }
            if(StringUtils.isEmpty(instanceId)){
                instanceId="*";
            }
        }
    
        /**
         * @Author haien
         * @Description 判断用户是否拥有权限
         * @Date 2019/2/18
         * @Param [p所需权限](this为用户权限)
         * @return boolean
         **/
        @Override
        public boolean implies(Permission p) {
            if(!(p instanceof BitPermission)) {
                return false;
            }
    
            BitPermission other=(BitPermission)p;
            if(!("*".equals(this.resourceIdentify) //this:用户拥有的权限,other:所需权限
                 ||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;
        }
    }
    
  • MyRealm:doGetAuthorizationInfo()获取用户权限。

    public class MyRealm extends AuthorizingRealm { //继承AuthorizingRealm实现Realm接口
        /**
         * @Author haien
         * @Description 根据用户身份获取授权信息
         * @Date 2019/2/19
         * @Param [principalCollection]
         * @return org.apache.shiro.authz.AuthorizationInfo
         **/
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
            /*不从数据库查找,自己设置权限进去*/
            //用户拥有两个角色
            authorizationInfo.addRole("role1"); //添加角色后调用MyRolePermissionResolver解析出权限
            authorizationInfo.addRole("role2"); //解析出来后应该是添加回authorizationInfo的权限集合
            //四个权限
            authorizationInfo.addObjectPermission(new BitPermission("+user1+10"));
            authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
            authorizationInfo.addStringPermission("+user2+10"); //10代表2新增与8查看
            authorizationInfo.addStringPermission("user2:*"); //字符串权限,等下会调用BitAndWildPermissionResolver解析
    
            return authorizationInfo;
        }
    }
    
  • 其中,Principle原本是AuthenticationToken的属性之一,表示用户名。

  • MyRolePermissionResolver:解析角色字符串到用户权限集合

    public class MyRolePermissionResolver implements RolePermissionResolver {
        /**
         * @Author haien
         * @Description 如果用户拥有role1,则返回menu:*权限,添加到用户权限集合中
         * @Date 2019/2/19
         * @Param [roleString]
         * @return java.util.Collection<org.apache.shiro.authz.Permission>
         **/
        @Override
        public Collection<Permission> resolvePermissionsInRole(String roleString) {
            if("role1".equals(roleString)){
                //role1的权限(在MyRealm中添加也一样)
                /*
                return Arrays.asList(new WildcardPermission("menu:*"),
                        new BitPermission("+user1+10"),
                        new WildcardPermission("user1:*"),
                        new BitPermission("+user2+10"),
                        new WildcardPermission("user2:*"));
                */
                return Arrays.asList((Permission)new WildcardPermission("menu:*"));
            }
            return null;
        }
    }
    
  • 代码实例:ideaProjects/shiroHelloWorld
  • 《跟我学shiro第三章