A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始



自定义Realm支持散列算法
实际应用中是将盐和散列后的值存在数据库中,自动Realm从数据库中取出盐和加密后的值,由shiro完成密码校验。

新建Realm(CustomRealmMd5)
[java] view plain copy print?
  • // 用于认证  
  •     @Override  
  •     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  
  •   
  •         // token保存了用户输入的身份信息userName和password  
  •         // 第一步:从token中取出身份信息  
  •         String userCode = (String) token.getPrincipal();  
  •   
  •         // 第二步:根据用户输入的userCode从数据库查询  
  •         // ....  
  •         // 如果查询不到返回null 假设用户输入的账号是zhansgan  
  •         // 模拟从数据库中查询账号是zhangsan的用户  
  •         if (!userCode.equals("zhangsan")) {  
  •             return null;  
  •         }  
  •   
  •         // 模拟从数据库中查询到密码(散列值)  
  •         // 按照固定规则加密的结果,此密码是在数据库中存储的,原始密码是123456 盐是qwerty  
  •         String password = "48474f975022f960bc2afbe49be581e8";  
  •         // 盐,随机字符串,此随机字符串也是在数据库中存储的,模拟从数据库中获取  
  •         String salt = "qwerty";  
  •   
  •         // 如果查询到则返回认证信息AuthenticationInfo  
  •         SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userCode, password,  
  •                 ByteSource.Util.bytes(salt), this.getName());  
  •          
  •         return simpleAuthenticationInfo;  
  •     }  

[java] view plain copy print?
// 用于认证  
    @Override  
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  
  
        // token保存了用户输入的身份信息userName和password  
        // 第一步:从token中取出身份信息  
        String userCode = (String) token.getPrincipal();  
  
        // 第二步:根据用户输入的userCode从数据库查询  
        // ....  
        // 如果查询不到返回null 假设用户输入的账号是zhansgan  
        // 模拟从数据库中查询账号是zhangsan的用户  
        if (!userCode.equals("zhangsan")) {  
            return null;  
        }  
  
        // 模拟从数据库中查询到密码(散列值)  
        // 按照固定规则加密的结果,此密码是在数据库中存储的,原始密码是123456 盐是qwerty  
        String password = "48474f975022f960bc2afbe49be581e8";  
        // 盐,随机字符串,此随机字符串也是在数据库中存储的,模拟从数据库中获取  
        String salt = "qwerty";  
  
        // 如果查询到则返回认证信息AuthenticationInfo  
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userCode, password,  
                ByteSource.Util.bytes(salt), this.getName());  
         
        return simpleAuthenticationInfo;  
    }
配置散列Realm
在classpath下配置shiro-realm-md5.ini
[plain] view plain copy print?
  • [main]  
  • #定义凭证匹配器  
  • credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher  
  • #散列算法  
  • credentialsMatcher.hashAlgorithmName=md5  
  • #散列次数  
  • credentialsMatcher.hashIterations=1  
  •   
  • #将凭证匹配器设置到Realm  
  • customRealm=liuxun.test.shiro.realm.CustomRealmMd5  
  • customRealm.credentialsMatcher=$credentialsMatcher  
  • #将Realm设置到securityManager  
  • securityManager.realms=$customRealm  

[plain] view plain copy print?
  • [main]  
  • #定义凭证匹配器  
  • credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher  
  • #散列算法  
  • credentialsMatcher.hashAlgorithmName=md5  
  • #散列次数  
  • credentialsMatcher.hashIterations=1  
  •   
  • #将凭证匹配器设置到Realm  
  • customRealm=liuxun.test.shiro.realm.CustomRealmMd5  
  • customRealm.credentialsMatcher=$credentialsMatcher  
  • #将Realm设置到securityManager  
  • securityManager.realms=$customRealm  

[main]#定义凭证匹配器credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher#散列算法credentialsMatcher.hashAlgorithmName=md5#散列次数credentialsMatcher.hashIterations=1 #将凭证匹配器设置到RealmcustomRealm=liuxun.test.shiro.realm.CustomRealmMd5customRealm.credentialsMatcher=$credentialsMatcher#将Realm设置到securityManagersecurityManager.realms=$customRealm
测试代码同上,修改ini文件路径即可

shiro授权
授权有三个核心元素:权限、角色和用户。
shiro权限声明通常是使用冒号分隔的表达式,权限表达式示例如下:
user:view        可查询用户数据
user:view,edit  可查询或编辑用户数据
user:*或user    可对用户数据进行所有操作
user:edit:123   可编辑id为123的用户数据

Shiro支持两种角色模式:
1、传统角色:一个角色代表着一系列的操作,当需要对某一操作进行授权验证时,只需判断是否是该角色即可。这种角色权限相对简单、模糊,不利于扩展。
2、权限角色:一个角色拥有一个权限的集合。授权验证时,需要判断当前角色是否拥有该权限。这种角色权限可以对该角色进行详细的权限描述,适合更复杂的权限设计。

shiro内部授权处理机制
1.在应用程序中调用授权验证方法(Subject的isPermitted*或hasRole*等)
2.Subject的实例通常是DelegatingSubject(或子类)的实例对象,在认证开始时,会委托应用程序设置的securityManager实例调用相应的isPermitted*或hasRole*方法。
3.接下来SecurityManager会委托内置的Authorizer的实例(默认是ModularRealmAuthorizer类的实例,类似于认证实例,它同样支持一个或多个Realm实例认证)调用相应的授权方法。
4.每一个Realm将检查是否实现了相同的Authorizer接口。然后,将调用Realm自己相应的授权验证方法。

注意:
当使用多个Realm时,不同于认证策略处理方式,在授权处理过程中:
(1) 当调用Realm出现异常时,将立即抛出异常,结束授权验证。
(2) 只要有一个Realm验证成功,那么将认为授权成功,立即返回,结束认证。
Shiro有3中认证策略的具体实现:
(1) AtLeastOneSuccessfulStrategy  只要有一个(或更多)的Realm验证成功,那么认证将被视为成功                     
(2) FirstSuccessfulStrategy  第一个Realm验证成功,整体认证将被视为成功,且后续Realm将被忽略                       
(3) AllSuccessfulStrategy 所有Realm成功,认证才视为成功

shiro授权方式
shiro支持三种方式的授权:编程式、注解式、标签式
方式一:编程式,通过写if/else授权代码块完成
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")){
   //有权限
}else{
   // 无权限
}
方式二:注解式,通过在执行的Java方法上放置相应的注解完成。
@RequiresRoles("admin")
public void hello(){
   //有权限
}
方式三:JSP/GSP标签,页面通过相应的标签完成
<shiro:hasRole name="admin">
  <!--  有权限   -->
</shiro:hasole>

授权测试
shiro-permission.ini
创建存放权限的配置文件shiro-permission.ini 如下:
[plain] view plain copy print?
  • #用户  
  • [users]  
  • #用户zhang的密码是123,此用户具有role1和role2两个角色  
  • zhang=123,role1,role2  
  • wang=123,role2  
  •   
  • #权限  
  • [roles]  
  • #角色role1对资源user拥有create、update权限  
  • role1=user:create,user:update  
  • #角色role2对资源user拥有create、delete权限  
  • role2=user:create,user:delete  
  • #role3对资源user拥有create权限  
  • role3=user:create  

[plain] view plain copy print?
  • #用户  
  • [users]  
  • #用户zhang的密码是123,此用户具有role1和role2两个角色  
  • zhang=123,role1,role2  
  • wang=123,role2  
  •   
  • #权限  
  • [roles]  
  • #角色role1对资源user拥有create、update权限  
  • role1=user:create,user:update  
  • #角色role2对资源user拥有create、delete权限  
  • role2=user:create,user:delete  
  • #role3对资源user拥有create权限  
  • role3=user:create  

#用户[users]#用户zhang的密码是123,此用户具有role1和role2两个角色zhang=123,role1,role2wang=123,role2 #权限[roles]#角色role1对资源user拥有create、update权限role1=user:create,user:update#角色role2对资源user拥有create、delete权限role2=user:create,user:delete#role3对资源user拥有create权限role3=user:create
在ini文件中用户、角色、权限的配置规则是:
" 用户名=密码,角色1,角色2 .... "
“ 角色=权限1,权限2......”
首先根据用户名查找角色,再根据角色查找权限,角色是权限的集合

权限字符串规则
权限字符串的规则是:“资源标识符:操作:资源实例标识符”,意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用*通配符。
例子:
用户创建权限:user:create,或user:create:*
用户修改实例001的权限:user:update:001
用户实例001的所有权限:user:*:001  

权限测试代码
测试代码同认证代码,注意ini地址改为shiro-permission.ini,主要掌握其授权方法
注意:在用户认证通过后才能执行下边的授权






    [java] view plain copy print?
  • // 角色授权、资源授权测试  
  •     @Test  
  •     public void testAuthorization() {  
  •   
  •         // 创建SecurityManager工厂  
  •         Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");  
  •   
  •         // 创建SecurityManager  
  •         SecurityManager securityManager = factory.getInstance();  
  •   
  •         // 将SecurityManager设置到系统运行环境,和Spring整合后将SecurityManager配置在Spring容器中,一般单例管理  
  •         SecurityUtils.setSecurityManager(securityManager);  
  •   
  •         // 创建subject  
  •         Subject subject = SecurityUtils.getSubject();  
  •   
  •         // 创建token令牌  
  •         UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");  
  •   
  •         // 执行认证  
  •         try {  
  •             subject.login(token);  
  •         } catch (AuthenticationException e) {  
  •             e.printStackTrace();  
  •         }  
  •   
  •         System.out.println("认证状态:" + subject.isAuthenticated());  
  •   
  •         // 认证通过后执行授权  
  •   
  •         // 基于角色的授权  
  •         // hasRole传入角色标识  
  •         boolean ishasRole = subject.hasRole("role1");  
  •         System.out.println("单个角色判断 " + ishasRole);  
  •         // hasAllRoles 是否拥有多个角色  
  •         boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1", "role2", "role3"));  
  •         System.out.println("多个角色判断 " + hasAllRoles);  
  •          
  •         //使用check方法进行授权,如果授权不通过会抛出异常,用于断言  
  •         subject.checkRole("role2");  
  •          
  •         //基于资源的权限  
  •         //isPermitted传入权限标识符  
  •         boolean isPermitted = subject.isPermitted("user:create:1");  
  •         System.out.println("单个权限判断 "+isPermitted);  
  •          
  •         boolean isPermittedAll = subject.isPermittedAll("user:create:1","user:delete");  
  •         System.out.println("多个权限判断 "+isPermittedAll);  
  •          
  •         //使用check方法进行授权测试,如果授权不通过会抛出异常  
  •         subject.checkPermission("item:delete");  
  •     }  

[java] view plain copy print?
  • // 角色授权、资源授权测试  
  •     @Test  
  •     public void testAuthorization() {  
  •   
  •         // 创建SecurityManager工厂  
  •         Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");  
  •   
  •         // 创建SecurityManager  
  •         SecurityManager securityManager = factory.getInstance();  
  •   
  •         // 将SecurityManager设置到系统运行环境,和Spring整合后将SecurityManager配置在Spring容器中,一般单例管理  
  •         SecurityUtils.setSecurityManager(securityManager);  
  •   
  •         // 创建subject  
  •         Subject subject = SecurityUtils.getSubject();  
  •   
  •         // 创建token令牌  
  •         UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");  
  •   
  •         // 执行认证  
  •         try {  
  •             subject.login(token);  
  •         } catch (AuthenticationException e) {  
  •             e.printStackTrace();  
  •         }  
  •   
  •         System.out.println("认证状态:" + subject.isAuthenticated());  
  •   
  •         // 认证通过后执行授权  
  •   
  •         // 基于角色的授权  
  •         // hasRole传入角色标识  
  •         boolean ishasRole = subject.hasRole("role1");  
  •         System.out.println("单个角色判断 " + ishasRole);  
  •         // hasAllRoles 是否拥有多个角色  
  •         boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1", "role2", "role3"));  
  •         System.out.println("多个角色判断 " + hasAllRoles);  
  •          
  •         //使用check方法进行授权,如果授权不通过会抛出异常,用于断言  
  •         subject.checkRole("role2");  
  •          
  •         //基于资源的权限  
  •         //isPermitted传入权限标识符  
  •         boolean isPermitted = subject.isPermitted("user:create:1");  
  •         System.out.println("单个权限判断 "+isPermitted);  
  •          
  •         boolean isPermittedAll = subject.isPermittedAll("user:create:1","user:delete");  
  •         System.out.println("多个权限判断 "+isPermittedAll);  
  •          
  •         //使用check方法进行授权测试,如果授权不通过会抛出异常  
  •         subject.checkPermission("item:delete");  
  •     }  
使用check方法测试授权失败会抛出异常:org.apache.shiro.authz.UnauthorizedException

自定义Realm授权
上边的程序通过shiro-permission.ini对权限信息进行静态配置,实际开发中从数据库中获取权限数据。就需要自定义Realm,由Realm从数据库查询权限数据。Realm根据用户身份信息查询权限数据,将权限数据返回给authorizer(授权器)

自定义Realm授权代码
在原来自定义的Realm类中完善doGetAuthorizationInfo方法,此方法需要完成以下功能:根据用户身份信息从数据库中查询权限字符串,由shiro进行授权。

[java] view plain copy print?
  • // 用于授权  
  •     @Override  
  •     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  •   
  •         //从principals获取身份信息  
  •         //将getPrimaryPrincipal方法返回值转为真实类型  
  •         //(在上边的doGetAuthenticationInfo认证通过后填充到SimpleAuthenticationInfo中身份类型)  
  •         String userCode = (String) principals.getPrimaryPrincipal();  
  •          
  •         //根据身份信息从数据库中获取权限信息  
  •         //模拟从数据库中取到的数据  
  •         List<String>  permissions = new ArrayList<String>();  
  •         permissions.add("user:create");//用户创建  
  •         permissions.add("items:add");//商品添加权限  
  •         //....  
  •          
  •         //查询到权限数据,返回授权信息(要包括上边的permissions)  
  •         SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();  
  •         //将上边查询到的授权信息填充到simpleAuthorizationInfo对象中  
  •         simpleAuthorizationInfo.addStringPermissions(permissions);  
  •          
  •         return simpleAuthorizationInfo;  
  •     }  

[java] view plain copy print?
  • // 用于授权  
  •     @Override  
  •     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  •   
  •         //从principals获取身份信息  
  •         //将getPrimaryPrincipal方法返回值转为真实类型  
  •         //(在上边的doGetAuthenticationInfo认证通过后填充到SimpleAuthenticationInfo中身份类型)  
  •         String userCode = (String) principals.getPrimaryPrincipal();  
  •          
  •         //根据身份信息从数据库中获取权限信息  
  •         //模拟从数据库中取到的数据  
  •         List<String>  permissions = new ArrayList<String>();  
  •         permissions.add("user:create");//用户创建  
  •         permissions.add("items:add");//商品添加权限  
  •         //....  
  •          
  •         //查询到权限数据,返回授权信息(要包括上边的permissions)  
  •         SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();  
  •         //将上边查询到的授权信息填充到simpleAuthorizationInfo对象中  
  •         simpleAuthorizationInfo.addStringPermissions(permissions);  
  •          
  •         return simpleAuthorizationInfo;  
  •     }  

shiro-realm.ini
ini配置文件还使用认证阶段用的,不用改变(即在shiro-realm.ini中配置自定义realm,将realm设置到securityManager)。注意:shiro-realm是配置自定义的Realm,是从数据库中获取权限数据,所以不需要再配置[roles]了。
[plain] view plain copy print?
  • [main]  
  • #自定义realm  
  • customRealm=liuxun.test.shiro.realm.CustomRealm  
  • #将realm设置到SecurityManager,相当于Spring中的注入  
  • securityManager.realms=$customRealm  

[plain] view plain copy print?
  • [main]  
  • #自定义realm  
  • customRealm=liuxun.test.shiro.realm.CustomRealm  
  • #将realm设置到SecurityManager,相当于Spring中的注入  
  • securityManager.realms=$customRealm  
[main]#自定义realmcustomRealm=liuxun.test.shiro.realm.CustomRealm#将realm设置到SecurityManager,相当于Spring中的注入securityManager.realms=$customRealm


测试代码
同上边的授权测试代码,注意ini地址为shiro-realm.ini

授权执行流程总结
1.对subject进行授权,调用isPermitted("permission串")
2.SecurityManager执行授权,通过ModularRealmAuthorizer执行授权
3.ModularRealmAuthorizer执行realm(自定义的CustomRealm)从数据库查询权限数据(调用realm的授权方法doGetAuthorizationInfo)
4.realm从数据库查询权限数据,返回给ModularRealmAuthorizer
5.ModularRealmAuthorizer调用PermissionResolver进行权限串比对
6.如果比对后,isPermitted中"permission串" 在realm查询到的权限数据中,说明用户访问permission串有权限,否则没有权限,抛出异常。

点击下方标题查看相关学习资料

1 个回复

倒序浏览
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马