• 【shiro】(4)---Shiro认证、授权案例讲解


    Shiro认证、授权案例讲解

    一、认证

     1、 认证流程

       

    2、用户密码已经加密、加盐的用户认证

     (1)测试类

      // 用户登陆和退出,这里我自定了一个realm(开发肯定需要自定义realm获取数据库密码和权限)
        @Test
        public void testCustomRealmMd5() {
    
            // 创建securityManager工厂,通过ini配置文件创建securityManager工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                    "classpath:shiro-realm-md5.ini");
    
            // 创建SecurityManager
            SecurityManager securityManager = factory.getInstance();
    
            // 将securityManager设置当前的运行环境中
            SecurityUtils.setSecurityManager(securityManager);
    
            // 从SecurityUtils里边创建一个subject
            Subject subject = SecurityUtils.getSubject();
    
            // 在认证提交前准备token(令牌)
            // 这里的账号和密码 将来是由用户输入进去
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "111111");
    
            try {
                // 执行认证提交
                subject.login(token);
            } catch (AuthenticationException e) {
                e.printStackTrace();
            }
    
            // 是否认证通过
            boolean isAuthenticated = subject.isAuthenticated();
    
            System.out.println("加密后是否认证通过:" + isAuthenticated);
            // 退出操作
            subject.logout();
            // 是否认证通过
              isAuthenticated = subject.isAuthenticated();
          System.out.println("退出后是否认证通过:" + isAuthenticated);
        }

    (2)shiro-realm-md5.ini (有关散列加密下面会举例子)

        我在ini配置了加密的规则,这个规则要和用户注册保存的密码加密规则一致。

    [main]
    #定义凭证匹配器
    credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
    #散列算法
    credentialsMatcher.hashAlgorithmName=md5
    #散列次数
    credentialsMatcher.hashIterations=1
    
    #将凭证匹配器设置到realm
    customRealm=com.jincou.shiro.realm.CustomRealmMd5
    customRealm.credentialsMatcher=$credentialsMatcher
    securityManager.realms=$customRealm

    (3)自定义CustomRealmMd5类

    /**
     *自定义Realm特点
     *(1)继承AuthorizingRealm类
     *(2)重写AuthenticationInfo(认证)
     *     doGetAuthorizationInfo(授权)两个方法
     * 注意:
     * 1:这里password = "f3694f162729b7d0254c6e40260bf15c"肯定也是通过明文(这里指zhangsan)+盐(这里是qwerty)进行加密后的字符串
     * 2:实际应用是将盐和散列后的值存在数据库中,自动realm从数据库取出盐和加密后的值由shiro完成密码校验。
     * 3:这里要注意它们两的加密规则一定要一致,否则无法比较。
     */
    public class CustomRealmMd5 extends AuthorizingRealm {
    
        // 设置realm的名称(任意和其它也无关联)
        @Override
        public void setName(String name) {
            super.setName("customRealmMd5");
        }
    
        // 用于认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
            
             System.out.println("测试..自定义CustomRealmMd5方法里面......");
             
            // token是用户输入的
            // 第一步从token中取出身份信息
            String userCode = (String) token.getPrincipal();
    
            // 第二步:根据用户输入的userCode从数据库查询
            // ....
    
            //如果数据库查询不到zhangsan用户信息,则返回null
                /*if(user==null){
                        return null;
                }*/
    
            // 模拟从数据库查询到密码,散列值
            String password = "f3694f162729b7d0254c6e40260bf15c";
            // 从数据库获取salt
            String salt = "qwerty";
            //上边散列值和盐对应的明文:111111
    
            // 如果查询到返回认证信息AuthenticationInfo,这里的密码加密判断交给底层处理
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                    userCode, password, ByteSource.Util.bytes(salt), this.getName());
    
            return simpleAuthenticationInfo;
        }
    
        // 用于授权(后面写)
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(
                PrincipalCollection principals) {
            return null;
        }
    }

    运行效果

    结论:subject.login(token)我在提交认证,它就会去调用我自定义的CustomRealm,因为我在ini进行配置自定义CustomRealm

     3、加密算法

     因为在ini里面配置的加密规则,那我第一次用户注册的时候,肯定也需要按它的规则进行加密后,把密码保存到数据库

      /**
      * 加密算法
      */
    public class MD5Test {    
        public static void main(String[] args) {     
            //原始 密码 
            String source = "111111";
            //
            String salt = "qwerty";
            
            //散列次数(和ini中credentialsMatcher.hashIterations=1一致)
            int hashIterations = 1;
            //上边散列1次:f3694f162729b7d0254c6e40260bf15c
            //上边散列2次:36f2dfa24d0a9fa97276abbe13e596fc
            
            //第一个参数:散列算法 
            //这里的散列算法是md5要和ini中credentialsMatcher.hashAlgorithmName=md5一致)
            //第二个参数:明文,原始密码 
            //第三个参数:盐,通过使用随机数
            //第四个参数:散列的次数,比如散列两次,相当 于md5(md5(''))
            SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations);
            System.out.println(simpleHash.toString());    
        }
    }
      //这个结果:f3694f162729b7d0254c6e40260bf15c就是在自定义realm中的密文也就是该用户保存到数据库中的密文。

     二、授权(基于资源的授权)

     1、授权流程

     

    2、授权方式

    Shiro 支持三种方式的授权:
    (1)编程式:通过写if/else 授权代码块完成:

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)) {
    //有权限
    } else {
    //无权限
    }

     (2)注解式:通过在执行的Java方法上放置相应的注解完成

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

     (3)JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成

    <shiro:hasRole name="admin">
    <!— 有权限—>
    </shiro:hasRole>

     这里授权测试使用第一种编程方式,实际与web系统集成使用后两种方式。

     3、授权测试

     (1)测试类

    // 自定义realm进行资源授权测试
        @Test
        public void testAuthorizationCustomRealm() {
    
            // 创建SecurityManager工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                    "classpath:shiro-realm.ini");
    
            // 创建SecurityManager
            SecurityManager securityManager = factory.getInstance();
    
            // 将SecurityManager设置到系统运行环境,和spring后将SecurityManager配置spring容器中,一般单例管理
            SecurityUtils.setSecurityManager(securityManager);
    
            // 创建subject
            Subject subject = SecurityUtils.getSubject();
    
            // 创建token令牌
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
                    "111111");
    
            // 执行认证
            try {
                subject.login(token);
            } catch (AuthenticationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            System.out.println("认证状态:" + subject.isAuthenticated());
            // 认证通过后执行授权
    
            // 基于资源的授权,调用isPermitted方法会调用CustomRealm从数据库查询正确权限数据
            // isPermitted传入权限标识符,判断user:create:1是否在CustomRealm查询到权限数据之内
            boolean isPermitted = subject.isPermitted("user:create:1");
            System.out.println("单个权限判断" + isPermitted);
    
            boolean isPermittedAll = subject.isPermittedAll("user:create:1",
                    "user:create");
            System.out.println("多个权限判断" + isPermittedAll);
    
            // 使用check方法进行授权,如果授权不通过会抛出异常
            subject.checkPermission("items:add:1");
    
        }

    (2)shiro-realm.ini

    [main]
    #自定义 realm
    customRealm=com.jincou.shiro.realm.CustomRealm
    #将realm设置到securityManager
    securityManager.realms=$customRealm

    (3)自定义CustomRealm类

    public class CustomRealm extends AuthorizingRealm {
    
        // 用于认证(上面写过)
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
            return null;
        }
    
        // 用于授权
        @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;
        }
    }

    运行结果:

     想太多,做太少,中间的落差就是烦恼。想没有烦恼,要么别想,要么多做。少校【5】

  • 相关阅读:
    'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set.
    flask os.environ 的作用
    flask 中xx.init_app(app)方法
    win10安装ubuntu双系统遇到的问题
    福大软工 · 最终作业
    福大软工 · 第十二次作业
    Beta 冲刺(7/7)
    Beta 冲刺(6/7)
    常用正则
    使用elementUI动态增减表单项 且 使用自定义校验
  • 原文地址:https://www.cnblogs.com/qdhxhz/p/9152904.html
Copyright © 2020-2023  润新知