• shiro入门与认证原理


    一、shiro介绍

    1、什么是shiro
     shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。
    2、shiro的优点
     (1)shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。
     (2)shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。
     (3)java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro。
    3、shiro的架构

     (1)Subject
      Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

     (2)SecurityManager
      SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
       SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

     (3)Authenticator
      Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
     (4)Authorizer
      Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

     (5)realm
      Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
    注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。

     (6)sessionManager
      sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
     (7)SessionDAO
      SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
     (8)CacheManager
      CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
     (9)Cryptography
      Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
    4.依赖jar包
    (1)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-all包括shiro所有的包:
    	<dependency>
    			<groupId>org.apache.shiro</groupId>
    			<artifactId>shiro-all</artifactId>
    			<version>1.2.3</version>
    		</dependency>
    

    (2)导入方式的jar包

    二、shiro认证

    1、认证流程

    2、入门程序(用户登陆和退出)
    创建一个Java工程,其目录结构如下

    (1)创建shiro_first.ini文件,通过此配置文件创建securityManager工厂。

    #对用户信息进行配置
    [users]
    #用户名和密码
    zhangsan=111111
    lisi=111111
    

    (2)测试代码

     public void test1(){
            //创建安全管理器 SecurityManager工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-first.ini");
            //创建安全管理器SecurityManager
            SecurityManager securityManager = factory.getInstance();
            //将SecurityManager设置到当前运行环境中
            SecurityUtils.setSecurityManager(securityManager);
            //从SecurityUtils里面创建一个主体
            Subject subject =  SecurityUtils.getSubject();
            //创建认证使用的token
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","1111111");
            //subject提交认证
            try{
                subject.login(token);
            }catch (Exception e){
                e.printStackTrace();
            }
            //是否认证通过
            boolean isAuthenticate =  subject.isAuthenticated();
            System.out.println("通过认证:"+isAuthenticate);
            System.out.println("是否认证通过:" + isAuthenticated);	
            //退出操作
            subject.logout();	
            //是否认证通过
            isAuthenticated =  subject.isAuthenticated();	
            System.out.println("是否认证通过:" + isAuthenticated);	
        }
    

    (3)执行流程分析
      1、通过ini配置文件创建securityManager
      2、调用subject.login方法主体提交认证,提交的token
      3、securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。
      4、ModularRealmAuthenticator调用IniRealm(给realm传入token) 去ini配置文件中查询用户信息
      5、IniRealm根据输入的token(UsernamePasswordToken)从 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)
    (4)总结
      ModularRealmAuthenticator作用进行认证,需要调用realm查询用户信息(在数据库中存在用户信息)
    ModularRealmAuthenticator进行密码对比(认证过程)。

      realm:需要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),如果查到用户返回认证信息,如果查询不到返回null
    3、自定义realm
    (1)定义realm要实现AuthorizingRealm接口

    public class CustomerRealm extends AuthorizingRealm {
        @Override
        public void setName(String name) {
            super.setName(name);
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
           //从token中取出身份信息
            String userCode = (String) authenticationToken.getPrincipal();
            //从数据库中去查询该用户是否存在
            //如果查询不到就返回 null
            //如果查询到了,就获取该用户的密码
            String password = "111111";
            //返回认证信息
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userCode,password,this.getName());
            return simpleAuthenticationInfo;
        }
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            return null;
        }
    }
    

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

    [main]
    #自定义realm
    customRealm=com.jack.realm.CustomerRealm
    #将realm设置到securityManager
    securityManager.realms=$customRealm
    

    (3)测试代码

     public void test2(){
            //创建安全管理器 SecurityManager工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-realm.ini");
            //创建安全管理器SecurityManager
            SecurityManager securityManager = factory.getInstance();
            //将SecurityManager设置到当前运行环境中
            SecurityUtils.setSecurityManager(securityManager);
            //从SecurityUtils里面创建一个主体
            Subject subject =  SecurityUtils.getSubject();
            //创建认证使用的token
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","111111");
            //subject提交认证
            try{
                subject.login(token);
            }catch (Exception e){
                e.printStackTrace();
            }
            //是否认证通过
            boolean isAuthenticate =  subject.isAuthenticated();
            System.out.println("通过认证:"+isAuthenticate);
        }
    

    4、散列算法
    (1)介绍
     通常需要对密码 进行散列,常用的有md5、sha,

     对md5密码,如果知道散列后的值可以通过穷举算法,得到md5密码对应的明文。
    建议对md5进行散列时加salt(盐),进行加密相当 于对原始密码+盐进行散列。
    正常使用时散列方法:
    在程序中对原始密码+盐进行散列,将散列值存储到数据库中,并且还要将盐也要存储在数据库中。

     如果进行密码对比时,使用相同 方法,将原始密码+盐进行散列,进行比对。
    (2)md5散列测试程序

    //md5加密,不加盐
    		String password_md5 = new Md5Hash("111111").toString();
    		System.out.println("md5加密,不加盐="+password_md5);
    		
    		//md5加密,加盐,一次散列
    		String password_md5_sale_1 = new Md5Hash("111111", "eteokues", 1).toString();
    		System.out.println("password_md5_sale_1="+password_md5_sale_1);
    		String password_md5_sale_2 = new Md5Hash("111111", "uiwueylm", 1).toString();
    		System.out.println("password_md5_sale_2="+password_md5_sale_2);
    		//两次散列相当于md5(md5())
    
    		//使用SimpleHash
    		String simpleHash = new SimpleHash("MD5", "111111", "eteokues",1).toString();
    		System.out.println(simpleHash);
    

    (3)自定义realm支持散列算法

    //继承AuthorizingRealm类,实现自定义realm
    public class CustomerRealmMd5 extends AuthorizingRealm {
        @Override
        public void setName(String name) {
            super.setName(name);
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
           //从token中取出身份信息
            String userCode = (String) authenticationToken.getPrincipal();
            //从数据库中去查询该用户是否存在
            //如果查询不到就返回 null
            //如果查询到了,就获取该用户的密码
            String password = "745c144d7721368a3257dcd0f728e4f1";
            //从数据库获取salt信息
            String salt = "adfgx";
            //返回认证信息
            SimpleAuthenticationInfo simpleAuthenticationInfo = new
                    SimpleAuthenticationInfo(userCode,password, ByteSource.Util.bytes(salt),this.getName());
            return simpleAuthenticationInfo;
        }
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            return null;
        }
    }
    

    (4)定义散列的凭证匹配器

    [main]
    #定义凭证匹配器
    credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
    #散列算法
    credentialsMatcher.hashAlgorithmName=md5
    #散列次数
    credentialsMatcher.hashIterations=1
    
    #将凭证匹配器设置到realm
    customRealm=com.jack.realm.CustomerRealmMd5
    customRealm.credentialsMatcher=$credentialsMatcher
    securityManager.realms=$customRealm
  • 相关阅读:
    python 元类
    python中__init__()、__new__()、__call__()、__del__()用法
    python内置数据结构方法的时间复杂度
    时间复杂度和空间复杂度
    数据结构及算法(1)
    sys模块python
    python中的文本操作
    python 中的os模块
    python 几种不同的格式化输出
    【js】null 和 undefined的区别?
  • 原文地址:https://www.cnblogs.com/jack1995/p/7445974.html
Copyright © 2020-2023  润新知