• shiro关于认证的学习


    一、shiro认证入门程序工程

    shiro分为认证和授权,这里我们先学习shiro的认证。

    1、shiro认证流程

    2、shiro认证入门工程的搭建

    (1)首先创建一个java工程

    在eclipse中显示的工程结构

    (2)添加shiro的jar包

    最核心的jar包就是:shiro-core.jar(下边其他的是需要依赖的jar包)

    在工程中创建一个lib文件夹(Folder),把jar包拷贝进去,然后build path。

    在eclipse中显示的工程结构

    这里我也贴一下,用maven时候,pom文件中的坐标

    
    	org.apache.shiro
    	shiro-core
    	1.2.3
    
    
    	org.apache.shiro
    	shiro-web
    	1.2.3
    
    
    	org.apache.shiro
    	shiro-spring
    	1.2.3
    
    
    	org.apache.shiro
    	shiro-ehcache
    	1.2.3
    
    
    	org.apache.shiro
    	shiro-quartz
    	1.2.3
    
    
    也可以通过引入shiro-all包括shiro所有的包:
    
    	org.apache.shiro
    	shiro-all
    	1.2.3
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    (3)创建一个log4j的日志文件

    创建一个config文件夹(Source Folder),把log4j的日志文件贴进去。

    这里可以简短的贴一下log4j日志文件最基本的内容

    log4j.rootLogger=debug,stdout
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
    
    • 1
    • 2
    • 3
    • 4
    • 5

    到时候创建一个log4j.properties文件,把上边的贴入进去就可以了。

    在eclipse中显示的工程结构

    这样我们这个最入门的工程就搭建好了。

    3、开始写shiro测试Demo

    首先我们创建一个包com.shiro.authentication,创建一个测试类AnthenticationTest.java。

    我们在测试这个环境中,并没有连接数据库,而是创建一个ini文件,在测试运行的时候加载ini文件里边配置的内容,就相当于从数据库中比对了。(ini文件相对于properties文件,就是可以分组,而properties文件只是简单的key,value形式)

    ini文件配置的内容

    (ini配置文件放在config文件夹中)

    接下来是测试类的内容

    package com.shiro.authentication;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.junit.Test;
    
    /**
     * 
     * 

    Title: AnthenticationTest

    *

    Description: 认证测试

    *

    Company: www.zonsim.com

    * @author Liuyl * @date 2016年10月5日上午12:14:55 * @version 1.0 */ public class AnthenticationTest { // 用户登陆和退出 @Test public void testLoginAndLogout(){ /** * (1)构建securityManager环境 */ // 创建securityManager工厂,通过ini配置文件创建securityManager工厂(ini是window中的一种配置文件) Factory factory = new IniSecurityManagerFactory( "classpath:shiro-first.ini"); //在ini里进行的配置,SecurityManager创建的时候就把ini里边的信息加载进来了,就相当于查询数据库了 //在说明一些SecurityManager类要导入org.apache.shiro.mgt.SecurityManager;包 // 创建SecurityManager SecurityManager securityManager = factory.getInstance(); //通过创建SecurityManager工厂和创建SecurityManager对象,完成了第一步构建securityManager环境 /** * 这个算是中间衔接的过程 */ // 将securityManager设置当前的运行环境中(这里是单例里的,但是不用我们管理,SecurityUtils完成这件事) SecurityUtils.setSecurityManager(securityManager); /** * 现在是一个测试环境,需要模拟一个subject,这里需要我们构造出来。 * 正常情况下,这个subject不需要我们来管,到时候就是我的一个http请求就是subject */ // 从SecurityUtils里边创建一个subject Subject subject = SecurityUtils.getSubject(); /** * (2)执行认证提交 */ // 在认证提交前准备token(令牌) // 这里的账号和密码 将来是由用户输入进去 UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "111111"); //我们在认证的时候可以抛出一个异常 try { //认证提交 subject.login(token); } catch (AuthenticationException e) {//AuthenticationException:shiro自己的认证异常 // TODO Auto-generated catch block e.printStackTrace(); } /** * (3)得到一个验证的结果 */ // 是否认证通过 boolean isAuthenticated = subject.isAuthenticated(); System.out.println("是否认证通过:" + isAuthenticated); // 退出操作 subject.logout();//一退出,身份信息就没有了 // 是否认证通过 isAuthenticated = subject.isAuthenticated(); System.out.println("是否认证通过:" + isAuthenticated);//退出了就因该是false了 /** * 整体的流程就是token中的用户名密码要和ini配置文件里边的用户名密码进行比对, * 成功了就是true,没有就是false */ } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    4、测试结果说明

    (1)上边测试的就过是true,false。

    在执行程序的时候,会拿着UsernamePasswordToken产生的token,去对面ini配置文件里边定义的帐号密码。有就返回true。

    当退出之后,身份信息就没有了,在进行身份认证,就是返回false了。

    (2)如果用户名不对

    会报帐号不存在异常:org.apache.shiro.authc.UnknownAccountException

    返回结果则是false,false

    (3)如果密码不正确

    会报凭证错误异常:org.apache.shiro.authc.IncorrectCredentialsException

    返回结果则是false,false

    5、总结shrio认证执行流程

    @1.通过ini配置文件创建securityManager。

    @2.调用subject.login方法主体提交认证,提交的token。

    @3.securityManager进行认证,securityManager通过Authenticator接口,最终由(调用)ModularRealmAuthenticator进行认证。

    @4.ModularRealmAuthenticator调用IniRealm(给realm传入的token)去ini配置文件中查询用户信息。

    @5.IniRealm根据输入的token(UsernamePasswordToken)从shiro-first.ini(自己定义的ini文件)查询用户信息,根据账号查询用户信息(账号和密码)

    如果查询到用户信息,就给ModularRealmAuthenticator返回用户信息(账号和密码),

    如果查询不到,就给ModularRealmAuthenticator返回null(说明账号不存在)。

    @6.ModularRealmAuthenticator接收IniRealm返回Authentication认证信息,
    如果返回的认证信息是null,ModularRealmAuthenticator抛出异常(org.apache.shiro.authc.UnknownAccountException)。

    如果返回的认证信息不是null(说明inirealm找到了用户),ModularRealmAuthenticator对IniRealm返回用户密码(在ini文件中存在)和token中的密码进行对比,如果不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException)

    二、自定义reaml认证

    将来实际开发需要realm从数据库中查询用户信息。

    1、在项目中创建一个realm包com.shiro.realm

    2、开始创建自定义realm类

    这个自定义realm类一般继承AuthorizingRealm这个类。

    下面就是自定义realm类

    package com.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;
    
    /**
     * 
     * 

    Title: CustomRealm

    *

    Description: 自定义realm

    *

    Company: www.zonsim.com

    * @author Liuyl * @date 2016年10月5日下午3:47:58 * @version 1.0 */ public class CustomRealm extends AuthorizingRealm{ // 设置realm的名称(这里边就读取ini配置文件中的数据) @Override public void setName(String name) { super.setName("customRealm"); } /** * 需要实现AuthorizingRealm这个接口的两个方法 */ //用户认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //因为我们现在还不连接数据库,我们在这里模拟一下 //第一步从token中取出身份信息( token是用户输入的)(这里边userCode取到的值是前边测试类中UsernamePasswordToken设置的值) String userCode = (String) token.getPrincipal(); // 第二步:根据用户输入的userCode从数据库查询 // .... //下边这步是模拟没有找到用户时候的情况 // 如果查询不到返回null //数据库中用户账号是zhangsansan if(!userCode.equals("zhangsansan")){// return null; } // 模拟从数据库查询到密码 String password = "111111"; // 如果查询到返回认证信息AuthenticationInfo //参数1.身份信息 2.凭证3.realm的名称 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( userCode, password, this.getName()); return simpleAuthenticationInfo; } //用户授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { // TODO Auto-generated method stub return null; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    3、配置自定义realm

    自定义reaml定义好之后,要对realm进行配置,如果不配置realm,securityManager就不知道调用哪个realm。
    所以需要在shiro-realm.ini(也可以配置到shiro-first.ini)配置realm注入到securityManager中。

    ini配置文件的内容为:

    4、写测试类进行测试

    就是上边测试类,改变一下加载的ini配置文件名称,不加载shiro-first.ini,改为加载shiro-realm.ini

    这样完成了自定义realm的认证方式。

    以后的开发中都会用自定义realm的方式来写。

    最后测试的结果,同上边的入门工程测试结果一样。

    三、自定义realm支持散列算法的认证

    1、首先了解一下加密的散列算法

    通常需要对密码进行散列,常用的有md5、sha,(md5散列后就不能逆向了)。

    对md5密码,如果知道散列后的值可以通过穷举算法,得到md5密码对应的明文。
    建议对md5进行散列时加salt(盐),进行加密相当于对原始密码+盐进行散列。

    正常使用时散列方法:
    在程序中对原始密码+盐进行散列,将散列值存储到数据库中,并且还要将盐也要存储在数据库中。
    如果进行密码对比时,使用相同方法,将原始密码+盐进行散列,进行比对。

    2、先来一个加密的测试程序,继续了解一下

    package com.shiro.authentication;
    
    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.crypto.hash.SimpleHash;
    import org.junit.Test;
    
    /**
     * 
     * 

    Title: MD5Test

    *

    Description:

    *

    Company: www.zonsim.com

    * @author Liuyl * @date 2016年10月5日下午4:36:30 * @version 1.0 */ public class MD5Test { @Test public void MD5_Test(){ //原始 密码 String source = "111111"; //盐 String salt = "qwerty"; //散列次数 int hashIterations = 2; //上边散列1次:f3694f162729b7d0254c6e40260bf15c //上边散列2次:36f2dfa24d0a9fa97276abbe13e596fc //构造方法中: //第一个参数:明文,原始密码 //第二个参数:盐,通过使用随机数 //第三个参数:散列的次数,比如散列两次,相当 于md5(md5('')) Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations); //得到md5散列后的密码 String password_md5 = md5Hash.toString(); System.out.println(password_md5); //第一个参数:散列算法 (这个和上边的作用是一样的) SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations); System.out.println(simpleHash.toString()); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    3、接下来,写最重要的支持散列算法的自定义realm

    package com.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;
    
    /**
     * 
     * 

    Title: CustomRealm

    *

    Description: 自定义realm

    *

    Company: www.zonsim.com

    * @author Liuyl * @date 2016年10月5日下午3:47:58 * @version 1.0 */ public class CustomRealmMD5 extends AuthorizingRealm{ // 设置realm的名称 @Override public void setName(String name) { super.setName("customRealmMD5"); } /** * 需要实现AuthorizingRealm这个接口的两个方法 */ //用户认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //因为我们现在还不连接数据库,我们在这里模拟一下 //第一步从token中取出身份信息( token是用户输入的)(这里边userCode取到的值是前边测试类中UsernamePasswordToken设置的值) String userCode = (String) token.getPrincipal(); // 第二步:根据用户输入的userCode从数据库查询 // .... /*//下边这步是模拟没有找到用户时候的情况 // 如果查询不到返回null //数据库中用户账号是zhangsansan if(!userCode.equals("zhangsansan")){// return null; }*/ // 模拟从数据库查询到密码,进行散列后的值(原来的明文密码是111111) String password = "f3694f162729b7d0254c6e40260bf15c"; // 从数据库获取salt String salt = "qwerty"; // 如果查询到返回认证信息AuthenticationInfo //参数1.身份信息 2.凭证3.盐4.realm的名称 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( userCode, password, ByteSource.Util.bytes(salt), this.getName()); return simpleAuthenticationInfo; } //用户授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { // TODO Auto-generated method stub return null; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    4、同上,需要配置自定义realm

    这里需要把凭证匹配器和自定义的realm都定义到realm中。

    ini配置文件的内容为:

    5、写测试类进行认证测试

    就是上边测试类,改变一下加载的ini配置文件名称,不加载shiro-realm.ini,改为加载shiro-realm-md5.ini

    这种方式基本上都用这种。

    最后测试的结果,同上边的入门工程测试结果一样。

    这样关于shiro认证方面的使用方法和知识点都介绍完成了。

  • 相关阅读:
    【经典问题】mysql和redis数据一致性问题
    红队专题-从零开始VC++C/S远程控制软件RAT-MFC-远控介绍及界面编写
    支付系统-名词解释
    计算机网络 -- 多人聊天室
    网页设计学习记录-常用圆角按钮css
    leetcode 138. 复制带随机指针的链表
    架构师的 36 项修炼第09讲:系统的安全架构设计
    QComboBox
    系统移植Makefile&README文件分析
    [附源码]Python计算机毕业设计Django酒店物联网平台系统
  • 原文地址:https://blog.csdn.net/m0_67401660/article/details/126496255