• shiro3


    shiro介绍

    1.1 什么是shiro

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

    spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。

    shiro不依赖于springshiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro

    shiro可以实现粗粒度和细粒度的权限管理。

    1.2 shiro架构

     

    subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
    securityManager:安全管理器,主体进行认证和授权都 是通过securityManager进行。securityManager是一个集合,真正做事的不是securityManager而是它里面的东西。
    authenticator:认证器,主体进行认证最终通过authenticator进行的。
    authorizer:授权器,主体进行授权最终通过authorizer进行的。
    sessionManager:web应用中一般是用web容器(中间件tomcat)对session进行管理,shiro也提供一套session管理的方式。shiro不仅仅可以用于web管理也可以用于cs管理,所以他不用web容器的session管理。
    SessionDao:  通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao (如果用tomcat管理session就不用SessionDao,如果要分布式的统一管理session就要用到SessionDao)。
    cache Manager:缓存管理器,主要对session和授权数据进行缓存(权限管理框架主要就是对认证和授权进行管理,session是在服务器缓存中的),比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理(redis是缓存框架)。
    realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据(原来是通过数据库取的)。
    注意:authenticator认证器和authorizer授权器调用realm中存储授权和认证的数据和逻辑。
    cryptography:密码管理,比如md5加密,提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。比如 md5散列算法(md5只有加密没有解密)。
    

      

    1.1jar包
    与其它java开源框架类似,将shiro的jar包加入项目就可以使用shiro提供的功能了。shiro-core是核心包必须选用,还提供了与web整合的shiro-web、与spring整合的shiro-spring、与任务调度quartz整合的shiro-quartz等,下边是shiro各jar包的maven坐标。
    Maven:
    	<dependency>  核心包
    			<groupId>org.apache.shiro</groupId>
    			<artifactId>shiro-core</artifactId>
    			<version>1.2.3</version>
    		</dependency>
    		<dependency>   用于web系统的权限管理
    			<groupId>org.apache.shiro</groupId>
    			<artifactId>shiro-web</artifactId>
    			<version>1.2.3</version>
    		</dependency>
    		<dependency>  和spring的整合
    			<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-all包括shiro所有的包:不要用这种方式,加多了会冲突。
    	<dependency>
    			<groupId>org.apache.shiro</groupId>
    			<artifactId>shiro-all</artifactId>
    			<version>1.2.3</version>
    		</dependency>
    参考lib目录 :
    

      

    1shiro认证
    1.1shiro认证流程
    首先构造环境,主题login认证是通过securityManager进行认证,securityManager认证是通过authenticator认证器认证,最后是根据realm来获取验证信息认证。

    1.1 shiro入门程序工程 环境

    jar包:shiro-core.jar

    工程结构:一个认证一个授权

    1.1shiro认证入门程序
    1.1.1shiro-first.ini
    通过此配置文件创建securityManager工厂。
    需要修改eclipse的ini的编辑器 :  window---perferences

    配置数据:

    1.1.1入门程序代码
    // 用户登陆和退出
    /**
     * Description: 认证测试
     */
    public class AuthenticationTest {
        // 用户登陆和退出
        @Test
        public void testLoginAndLogout() {
            // 创建securityManager工厂,通过ini配置文件创建securityManager工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                    "classpath:shiro-first.ini");
            /*ini文件可以实现分组:property文件不能分组。
            [users]
            zhangsan=111111
            lisi=22222  
    */
            // 创建SecurityManager
            SecurityManager securityManager = factory.getInstance();
            // 将securityManager设置当前的运行环境中
            SecurityUtils.setSecurityManager(securityManager);
            // 从SecurityUtils里边创建一个subject,以后一个http请求就是一个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);//是否认证通过:true
            // 退出操作
            subject.logout();
            // 是否认证通过
            isAuthenticated = subject.isAuthenticated();//是否认证通过:false
            System.out.println("是否认证通过:" + isAuthenticated);
        }

    1.1.1执行流程
    1、通过ini配置文件创建securityManager
    2、调用subject.login方法主体提交认证,提交的token,内部securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。
    4、ModularRealmAuthenticator调用IniRealm(给realm传入token) 去ini配置文件中查询用户信息(IniRealm也可以从数据库查询)
    5、IniRealm根据输入的token(UsernamePasswordToken手动写的token)从 shiro-first.ini查询用户信息,根据账号查询用户信息(账号和密码)
        如果查询到用户信息,就给ModularRealmAuthenticator返回用户信息(账号和密码)
        如果查询不到,就给ModularRealmAuthenticator返回null
    6、ModularRealmAuthenticator接收IniRealm返回Authentication认证信息
        如果返回的认证信息是null,ModularRealmAuthenticator抛出异常(org.apache.shiro.authc.UnknownAccountException)
    
        如果返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码 (在ini文件中存在)和 token中的密码 进行对比,如果不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException)
    1.1.1小结:
    ModularRealmAuthenticator作用进行认证,需要调用realm查询用户信息(在数据库中存在用户信息)
    ModularRealmAuthenticator进行密码对比,就是认证过程。账号不存在不是认证。查账号交给realm。
    realm需要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),如果查到用户返回认证信息,如果查询不到返回null。
    自定义realm
    将来实际开发需要realm从数据库中查询用户信息,所以要自定义realm。
    1.1.2realm接口

    1.1.1 自定义realm

    自定义realm:
    package cn.itcast.shiro.realm;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    /**
     * 自定义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) {
            return null;
        }
    }

    1.1.1 配置realm

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

    1.1.1测试
        // 自定义realm
        @Test
        public void testCustomRealm() {
            // 创建securityManager工厂,通过ini配置文件创建securityManager工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                    "classpath:shiro-realm.ini");//现在用的realm是我们自己自定义的realm,
            //用户名密码是从数据库读取,不再是从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);
        }
    1.1散列算法
    通常需要对密码 进行散列,常用的有md5、sha, 
    
    对md5密码,如果知道散列后的值可以通过穷举算法,得到md5密码对应的明文。
    建议对md5进行散列时加salt(盐),进行加密相当于对原始密码+盐进行散列。
    
    正常使用时散列方法:
    在程序中对原始密码+盐进行散列,将散列值存储到数据库中,并且还要将盐也要存储在数据库中。
    
    如果进行密码对比时,使用相同 方法,将原始密码+盐进行散列,进行比对。
    1.1.1md5散列测试程序:
    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());
            //36f2dfa24d0a9fa97276abbe13e596fc
            //36f2dfa24d0a9fa97276abbe13e596fc
    1.1.2自定义realm支持散列算法 
    需求:实际开发时realm要进行md5值(明文散列后的值)的对比。
    1.1.2.1新建realm(CustomRealmMd5.java)
    package cn.itcast.shiro.realm;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    
    /**
     * 自定义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) {
            return null;
        }
    }
    
    
    
    1.1.2.2在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",
                    "222222");
            try {
                // 执行认证提交
                subject.login(token);
            } catch (AuthenticationException e) {
                e.printStackTrace();
            }
            // 是否认证通过
            boolean isAuthenticated = subject.isAuthenticated();
            System.out.println("是否认证通过:" + isAuthenticated);
        }
  • 相关阅读:
    jenkins更换国内源
    部署jdk和maven
    Prometheus监控Nginx
    Prometheus监控MySQL
    MySql里动态视图的实现
    MySql里split函数的实现
    HTML编码规范
    消弱反驳18招
    Pr2020
    记忆准则
  • 原文地址:https://www.cnblogs.com/yaowen/p/8735606.html
Copyright © 2020-2023  润新知