zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Java Web 网络留言板2 JDBC数据源 (连接池技术)
    Java Web 网络留言板3 CommonsDbUtils
    Java Web ConnectionPool (连接池技术)
    Java Web 网络留言板
    Java Web JDBC数据源
    Java Web CommonsUtils (数据库连接方法)
    Servlet 起源
    Hibernate EntityManager
    Hibernate Annotation (Hibernate 注解)
    wpf控件设计时支持(1)
  • 原文地址:https://www.cnblogs.com/jack1995/p/7445974.html
Copyright © 2011-2022 走看看