• Shiro第二篇【介绍Shiro、认证流程、自定义realm、自定义realm支持md5】


    什么是Shiro

    shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权

    spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。
    shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。

    Shiro架构:

    这里写图片描述

    • subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
    • securityManager:安全管理器,主体进行认证和授权都 是通过securityManager进行。
    • authenticator:认证器,主体进行认证最终通过authenticator进行的。
    • authorizer:授权器,主体进行授权最终通过authorizer进行的。
    • sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套session管理的方式。
    • SessionDao: 通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao。
    • cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。
    • realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据。

    cryptography:密码管理,提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

    • 比如md5散列算法。

    为什么使用Shiro

    我们在使用URL拦截的时候,要将所有的URL都配置起来,繁琐、不易维护

    而我们的Shiro实现系统的权限管理,有效提高开发效率,从而降低开发成本。

    Shiro认证

    导入jar包

    我们使用的是Maven的坐标就行了

    
        <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-web</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-ehcache</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-quartz</artifactId>
                <version>1.2.3</version>
            </dependency>

    当然了,我们也可以把Shiro相关的jar包全部导入进去

    
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-all</artifactId>
        <version>1.2.3</version>
    </dependency>
    
    

    Shiro认证流程

    这里写图片描述

    通过配置文件创建工厂

    这里写图片描述

    
        // 用户登陆和退出
        @Test
        public void testLoginAndLogout() {
    
            // 创建securityManager工厂,通过ini配置文件创建securityManager工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                    "classpath:shiro-first.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) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            // 是否认证通过
            boolean isAuthenticated = subject.isAuthenticated();
    
            System.out.println("是否认证通过:" + isAuthenticated);
    
            // 退出操作
            subject.logout();
    
            // 是否认证通过
            isAuthenticated = subject.isAuthenticated();
    
            System.out.println("是否认证通过:" + isAuthenticated);
    
        }

    这里写图片描述

    小结

    ModularRealmAuthenticator作用进行认证,需要调用realm查询用户信息(在数据库中存在用户信息)
    ModularRealmAuthenticator进行密码对比(认证过程)。
    realm:需要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),如果查到用户返回认证信息,如果查询不到返回null

    自定义realm

    从第一个认证程序我们可以看见,我们所说的流程,是认证器去找realm去查询我们相对应的数据。而默认的realm是直接去与配置文件来比对的,一般地,我们在开发中都是让realm去数据库中比对。
    因此,我们需要自定义realm

    这里写图片描述

    
    public class CustomRealm extends AuthorizingRealm {
    
        // 设置realm的名称
        @Override
        public void setName(String name) {
            super.setName("customRealm");
        }
    
        // 用于认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
    
            // token是用户输入的
            // 第一步从token中取出身份信息
            String userCode = (String) token.getPrincipal();
    
            // 第二步:根据用户输入的userCode从数据库查询
            // ....
    
    
            // 如果查询不到返回null
            //数据库中用户账号是zhangsansan
            /*if(!userCode.equals("zhangsansan")){//
                return null;
            }*/
    
    
            // 模拟从数据库查询到密码
            String password = "111112";
    
            // 如果查询到返回认证信息AuthenticationInfo
    
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                    userCode, password, this.getName());
    
            return simpleAuthenticationInfo;
        }
    
        // 用于授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(
                PrincipalCollection principals) {
            // TODO Auto-generated method stub
            return null;
        }
    
    }

    配置realm

    需要在shiro-realm.ini配置realm注入到securityManager中。

    这里写图片描述

    测试自定义realm

    同上边的入门程序,需要更改ini配置文件路径:

    
    同上边的入门程序,需要更改ini配置文件路径:
    Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                    "classpath:shiro-realm.ini");

    散列算法

    我们如果知道md5,我们就会知道md5是不可逆的,但是如果设置了一些安全性比较低的密码:111111…即时是不可逆的,但还是可以通过暴力算法来得到md5对应的明文…

    建议对md5进行散列时加salt(盐),进行加密相当 于对原始密码+盐进行散列。

    正常使用时散列方法:

    • 在程序中对原始密码+盐进行散列,将散列值存储到数据库中,并且还要将盐也要存储在数据库中。

    测试:

    
    public class MD5Test {
    
        public static void main(String[] args) {
    
            //原始 密码 
            String source = "111111";
            //盐
            String salt = "qwerty";
            //散列次数
            int hashIterations = 2;
            //上边散列1次:f3694f162729b7d0254c6e40260bf15c
            //上边散列2次:36f2dfa24d0a9fa97276abbe13e596fc
    
    
            //构造方法中:
            //第一个参数:明文,原始密码 
            //第二个参数:盐,通过使用随机数
            //第三个参数:散列的次数,比如散列两次,相当 于md5(md5(''))
            Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations);
    
            String password_md5 =  md5Hash.toString();
            System.out.println(password_md5);
            //第一个参数:散列算法 
            SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations);
            System.out.println(simpleHash.toString());
        }
    
    }

    自定义realm支持md5

    自定义realm

    
    public class CustomRealmMd5 extends AuthorizingRealm {
    
        // 设置realm的名称
        @Override
        public void setName(String name) {
            super.setName("customRealmMd5");
        }
    
        // 用于认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
    
            // token是用户输入的
            // 第一步从token中取出身份信息
            String userCode = (String) token.getPrincipal();
    
            // 第二步:根据用户输入的userCode从数据库查询
            // ....
    
            // 如果查询不到返回null
            // 数据库中用户账号是zhangsansan
            /*
             * if(!userCode.equals("zhangsansan")){// 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) {
            // TODO Auto-generated method stub
            return null;
        }
    
    }

    配置文件:

    这里写图片描述

    测试:

    
    // 自定义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",
                    "222222");
    
            try {
                // 执行认证提交
                subject.login(token);
            } catch (AuthenticationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            // 是否认证通过
            boolean isAuthenticated = subject.isAuthenticated();
    
            System.out.println("是否认证通过:" + isAuthenticated);
    
        }
  • 相关阅读:
    贝叶斯在机器学习中的应用(一)
    使用ajax发送的请求实现页面跳转
    关于welcom-file-list 失效
    在新的线程中使用session 出现的问题
    关于innerHtml= 与 html() 区别
    request.getAttribute()和 request.getParameter()有何区别?
    __STL_VOLATILE
    声明一个返回特定类型函数指针的函数
    关于vue的基础概念
    Vue.js结合vue-router和webpack编写单页路由项目
  • 原文地址:https://www.cnblogs.com/zhong-fucheng/p/7554346.html
Copyright © 2020-2023  润新知