zoukankan      html  css  js  c++  java
  • Shiro安全框架【认证】+【授权】

    1、Shiro的核心架构

    img

    • Subject:程序主体
    • Security Manager:安全管理器
    • Authentication:做认证
    • Authorizer:做授权
    • Session Manager:会话管理器(管理整个Web会话核心)
    • Session Manager:GRUD管理(增删改查)
    • Cache Manager:缓存管理器(减小资源压力)
    • Pluggable Realms:获取认证和授权的相应数据并完成相应的认证和授权操作
    • Cryptography:算法生成器,密码生成器

    2、Shiro中的认证

    • Subject:主体
      • 访问系统的用户,主体可以是用户、程序等
    • Principal:身份信息
      • 主体(subject)进行身份认证的标识,标识具有唯一性,可以有多个身份,但是必须得有一个主身份(Primary Principal)
    • Credential:凭证信息
      • 只有主体自己知道的安全信息

    认证流程

    image-20210130020958415

    编码实现:

    public class TestAuthentication {
        //实现认证
        public static void main(String[] args) {
            //0、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager 
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            //1、创建安全管理器
            SecurityManager securityManager = factory.getInstance();
            //2、SecurityUtils 给全局安全工具类设置安全管理器
            SecurityUtils.setSecurityManager(securityManager);
    
            //4、关键对象subject主体
            Subject subject = SecurityUtils.getSubject();
    
            //5、创建令牌(Token)
            UsernamePasswordToken token = new UsernamePasswordToken("xiaocheng", "123");
    
            try {
                //认证状态
                System.out.println("认证状态:" + subject.isAuthenticated());
                subject.login(token);//用户认证
                //认证状态
                System.out.println("认证状态:" + subject.isAuthenticated());
            } catch (UnknownAccountException e) {
                System.out.println("认证失败:{用户不存在}");
            } catch (IncorrectCredentialsException e) {
                System.out.println("认证失败:{密码不存在}");
            }
        }
    }
    
    
    image-20210130164308800
    • 最终执行用户名比较的类SimpleAccountRealm
      • 在该类中的doGetAuthenticationInfo方法中完成了用户名的校验
        后续的方法中,我们会在这个方法中来进行完成数据库的认证
    • 最终执行密码校验的类AuthenticatingRealm
      • 在该类中的assertCredentialsMatch方法中完成了密码的校验
        密码则是在自动返回信息后自动进行校验的

    总结:

    AuthenticatingRealm 认证realm	doGetAuthenticationInfo
    AuthorizeingRealm	授权realm	doGetAuthorizationInfo
    
    
    如果要自定义Realm的话自己写一个Realm然后继承AuthorizeingRealm然后可以重写两个类中的doGetAuthenticationInfo和doGetAuthorizationInfo方法
    

    3、自定义认证

    /**
     * 使用自定义realm来进行认证
     */
    public class TestCustomerRealmAuthenticator {
        public static void main(String[] args) {
            //创建安全管理器securityManager
            DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
            //设置自定义realm
            defaultSecurityManager.setRealm(new CustomerRealm());
            //将安全工具类设置安全管理器
            SecurityUtils.setSecurityManager(defaultSecurityManager);
            //获取通过安全工具了获取subject
            Subject subject = SecurityUtils.getSubject();
            //创建token
            UsernamePasswordToken token = new UsernamePasswordToken("xiaocheng", "123");
            try{
                subject.login(token);
                System.out.println("认证成功!");
            }catch (IncorrectCredentialsException e){
                System.out.println("密码错误");
            }catch (UnknownAccountException e){
                System.out.println("用户名错误");
            }
        }
    }
    
    /**
     * 自定义认证Realm
     * 将认证/授权的数据的来源转为模拟数据库的实现
     */
    public class CustomerRealm extends AuthorizingRealm {
        //授权方法
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            return null;
        }
    
        //认证方法
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //在token中获取用户名
            Object principal = authenticationToken.getPrincipal();
            System.out.println(principal);
            //根据身份信息去使用jdbc获取相关的数据库
            if ("xiaocheng".equals(principal)) {
                //参数1代表返回数据库中正确的用户名
                //参数2代表返回数据库中正确的密码
                //参数3提供当前Realm的名字 this.getName()
                //模拟数据库的实现
                return new SimpleAuthenticationInfo(principal,"123", this.getName());
            }
            return null;
        }
    }
    

    4、使用MD5和Salt(盐)加密明文数据

    可以理解为给MD5加盐,齁得慌。所以加密的后的数据就更复杂了

    md5(明文密码+盐) => {复杂密文} => md5(复杂密文+盐) => {复杂密文plush}
    

    MD5算法一般用来做加密和签名,特点是不可逆的。只能根据明文来生成密文而不能通过密文来反推明文。如果内容相同无论执行多少次MD5生产的结果始终是一致的

    MD5生成结果特点:

    始终是一个16进制32位长度的字符串

    使用方式

    public class TestShiroMD5 {
        public static void main(String[] args) {
            //使用MD5
            Md5Hash md5Hash = new Md5Hash("123");
            System.out.println(md5Hash.toHex());
            //使用MD5+salt加盐处理
            Md5Hash md5Hash1 = new Md5Hash("123", "X0*7ps");
            System.out.println(md5Hash1.toHex());
            //使用MD5 + salt + hash散列
            Md5Hash md5Hash2 = new Md5Hash("123", "X0*7ps", 1024);
            System.out.println(md5Hash2.toHex());
        }
    }
    

    改造Shiro认证流程已经加密的处理

    public class TestCustromerMd5RealmAuthenication {
        public static void main(String[] args) {
            //创建安全管理器
            DefaultSecurityManager securityManager = new DefaultSecurityManager();
            //注入自定义的realm
            CustomerMd5Realm realm = new CustomerMd5Realm();
    
            //设置realm使用hash凭证匹配器
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            credentialsMatcher.setHashAlgorithmName("md5");
            //hash散列1024次
            credentialsMatcher.setHashIterations(1024);
            realm.setCredentialsMatcher(credentialsMatcher);
            securityManager.setRealm(realm);
    
            //将安全管理器注入安全工具类
            SecurityUtils.setSecurityManager(securityManager);
            //通过安全工具类获取Subject
            Subject subject = SecurityUtils.getSubject();
            //认证
            UsernamePasswordToken token =
                    new UsernamePasswordToken("xiaocheng", "123");
    
            try {
                subject.login(token);
                System.out.println("验证成功");
            } catch (UnknownAccountException e) {
                System.out.println("用户名错误");
            } catch (IncorrectCredentialsException e) {
                System.out.println("密码错误");
            }
        }
    }
    
    /**
     * 使用自定义realm去加入md5+salt+hash
     */
    public class CustomerMd5Realm extends AuthorizingRealm {
        //授权方法
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            return null;
        }
    
        //认证方法
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //获取身份信息
            String principal = (String) authenticationToken.getPrincipal();
            //根据用户名查询数据库
            if ("xiaocheng".equals(principal)) {
                //参数1:用户名
                //参数2:md5+salt之后的密码
                //参数3:注册时的随机盐
                //参数4:realm的名字
                return new SimpleAuthenticationInfo(principal,
                        "e4f9bf3e0c58f045e62c23c533fcf633",
                        ByteSource.Util.bytes("X0*7ps"),
                        this.getName());
            }
            return null;
        }
    }
    

    5、Shiro中的授权

    授权就是访问控制,控制谁能访问和修改哪些资源

    对认证之后的用户来进行权限的赋予

    image-20210130211510827

    授权方式

    • 基于角色的访问控制

      • RBAC(Role-Based Access Control)基于角色的访问控制

      • if(subject.hasRole("admin")){ //资源实例
            //操作资源
        }
        
    • 基于资源的访问控制

      • RBAC(Resource-Based Access Control)是以资源为中心进行控制访问

      • if(subject.IsPermissions("user:find:*")){ //资源类型
            //对所有用户具有查询权限
        }
        

    权限字符串

    规则

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

    通过编码实现授权

    public class CustomerMd5Realm extends AuthorizingRealm {
        //授权方法
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("========开始授权========");
            //获取身份信息,使用身份信息在数据库中查看具有哪些身份信息和权限
            String principal = (String) principalCollection.getPrimaryPrincipal();
            System.out.println("主身份 " + principal);
            //将数据库中查询的角色信息赋值给权限对象(模拟)
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.addRole("admin");
            simpleAuthorizationInfo.addRole("user");
            //将数据库中的权限信息赋值给权限对象
            //当前用户只对01用户有操作权限
            simpleAuthorizationInfo.addStringPermission("user:*:01");
            //对商品具有创建权限
            simpleAuthorizationInfo.addStringPermission("produce:create");
            return simpleAuthorizationInfo;
        }
    
        //认证方法
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //获取身份信息
            String principal = (String) authenticationToken.getPrincipal();
            //根据用户名查询数据库
            if ("xiaocheng".equals(principal)) {
                //参数1:用户名
                //参数2:md5+salt之后的密码
                //参数3:注册时的随机盐
                //参数4:realm的名字
                return new SimpleAuthenticationInfo(principal,
                        "e4f9bf3e0c58f045e62c23c533fcf633",
                        ByteSource.Util.bytes("X0*7ps"),
                        this.getName());
            }
            return null;
        }
    }
    
    public class TestCustromerMd5RealmAuthenication {
        public static void main(String[] args) {
            //创建安全管理器
            DefaultSecurityManager securityManager = new DefaultSecurityManager();
            //注入自定义的realm
            CustomerMd5Realm realm = new CustomerMd5Realm();
    
            //设置realm使用hash凭证匹配器
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            credentialsMatcher.setHashAlgorithmName("md5");
            //hash散列1024次
            credentialsMatcher.setHashIterations(1024);
            realm.setCredentialsMatcher(credentialsMatcher);
            securityManager.setRealm(realm);
    
            //将安全管理器注入安全工具类
            SecurityUtils.setSecurityManager(securityManager);
            //通过安全工具类获取Subject
            Subject subject = SecurityUtils.getSubject();
            //认证
            UsernamePasswordToken token =
                    new UsernamePasswordToken("xiaocheng", "123");
    
            try {
                subject.login(token);
                System.out.println("验证成功");
            } catch (UnknownAccountException e) {
                System.out.println("用户名错误");
            } catch (IncorrectCredentialsException e) {
                System.out.println("密码错误");
            }
    
            //认证的用户进行授权
            if (subject.isAuthenticated()) {
                //1.基于角色的权限控制,查看是否有admin权限
                System.out.println("是否具有多角色admin : " + subject.hasRole("admin"));
                //2.基于多角色的权限控制
                System.out.println("是否具有多角色: " + subject.hasAllRoles(Arrays.asList("admin", "user")));
                //3.是否具有其中一个角色
                boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "super", "user"));
                for (boolean aBoolean : booleans) {
                    System.out.println(aBoolean);
                }
                System.out.println("+++++++++++++++++++++++=");
                //4.基于权限字符串的访问控制  资源标识符:操作:资源类型/实例
                //是否具有整个user的操作权限
                System.out.println("权限:" + subject.isPermitted("user:*:01"));
                //判断对01用户有没有修改操作
                System.out.println("用户修改权限:" + subject.isPermitted("user:update:01"));
                //判断对所有商品是否具有修改权限
                System.out.println("商品修改权限:" + subject.isPermitted("produce:update"));
                //判断对所有商品是否具有创建权限
                System.out.println("商品修改权限:" + subject.isPermitted("produce:create"));
                //判断对所有商品是否具有创建01号创建权限
                System.out.println("商品修改权限:" + subject.isPermitted("produce:create:02"));
                //分别具有哪些权限
                boolean[] booleans1 = subject.isPermitted("user:*:01", "order:*:10");
                for (boolean b : booleans1) {
                    System.out.println("分别具有哪些权限: " + b);
                }
                //同时具有哪些权限
                boolean all = subject.isPermittedAll("user:*:01", "produce:create:01");
                System.out.println("同时具有权限: " + all);
            }
        }
    }
    
  • 相关阅读:
    领域驱动和MVVM应用于UWP开发的一些思考
    UWP中实现自定义标题栏
    UWP中新加的数据绑定方式x:Bind分析总结
    MVVM框架从WPF移植到UWP遇到的问题和解决方法
    UWP学习目录整理
    MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息
    MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信
    MVVM设计模式和WPF中的实现(四)事件绑定
    MVVM模式解析和在WPF中的实现(三)命令绑定
    MVVM模式和在WPF中的实现(二)数据绑定
  • 原文地址:https://www.cnblogs.com/MineLSG/p/14350817.html
Copyright © 2011-2022 走看看