zoukankan      html  css  js  c++  java
  • Shiro的Jdbc配置和验证策略(二)

    戒色诗: 二八佳人体似酥,腰间仗剑斩凡夫。虽然不见人头落,暗里教君骨髓枯。

    一. Shiro 的认证错误

    在上一章节的例子中,我们可以看到, Shiro 是通用 Subject 对象的 login() 方法,进行认证判断的。 我们自己在开发中,常常有这么一个需求,在登录时,如果登录失败,会给出一些相应的提示,如用户名不存在,密码错误,账号不可用等信息。 Shiro 也提供了相应的功能。

    public abstract void login(AuthenticationToken paramAuthenticationToken)
      throws AuthenticationException;
    

    Shiro 会抛出一个异常, AuthenticationException。

    这个异常有许多子类,

    有图片

    通常会捕获相应的子类异常信息,进行相应的错误提示。

    异常类名 异常解释
    DisabledAccountException 禁用账户
    LockedAccountException 账户被锁定
    UnknownAccountException 错误的账户,即账户名不存在
    ExcessiveAttemptsException 登录失败次数过多,如超过3次
    IncorrectCredentialsException 错误凭证,即密码不正确
    ExpiredCredentialsException 过期凭证

    常见的是 IncorrectCredentialsException 密码不正确和 UnknownAccountException 和未知账户。

    友情提示: 在错误信息提示时,不要提示,用户名不存在,密码错误 这样的信息,而应该是 用户名/密码 错误,避免有心人尝试密码破解或者获取网站的用户名。

    登录成功之后,也可以进行相应的退出, 调用 subject对象的 logout() 方法进行退出。

    写一个 用户名密码和 退出的小例子。(配置文件,仍然使用 shiro.ini)

    package com.yjl.demo;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.DisabledAccountException;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    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;
    
    /**
     * 
     * @author 两个蝴蝶飞
     * Shiro 的第二个演示文件, 凭证错误和退出
     */
    public class ShiroDemo2 {
    	public static void main(String[] args) {
    		//1. 创建工厂 
    		Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
    		//2. 从工厂里面获取 SecurityManager
    		SecurityManager securityManager=factory.getInstance();
    		//3. 通过工具类设置 securityManager
    		SecurityUtils.setSecurityManager(securityManager);
    		
    		//4. 获取当前登录的用户
    		
    		Subject subject=SecurityUtils.getSubject();
    		
    		//5. 拼装用户的身份和密码Token
    		
    		UsernamePasswordToken token=new UsernamePasswordToken("yuezl","1234"); //1.正确密码
    		//UsernamePasswordToken token=new UsernamePasswordToken("yuezl","123456"); //2.错误密码
    		//UsernamePasswordToken token=new UsernamePasswordToken("yuezlAbc","1234"); //3.未知账户
    		
    		System.out.println("用户是否登录成功A:"+subject.isAuthenticated());
    		
    		//6. 调用 subject 里面的login 方法,进行登录
    		try{			
    			subject.login(token);
    			//7.判断用户是否登录成功
    			
    			if(subject.isAuthenticated()){
    				System.out.println("用户:"+token.getUsername()+", 登录成功");
    			}
    			
    		}catch (UnknownAccountException var6) {
                var6.printStackTrace();
                System.out.println("没有此账号");
            } catch (IncorrectCredentialsException var7) {
                var7.printStackTrace();
                System.out.println("密码不正确");
            } catch (DisabledAccountException var8) {
                var8.printStackTrace();
                System.out.println("账户不可用");
            }
    		System.out.println("用户是否登录成功B:"+subject.isAuthenticated());
    		
    		//退出程序
    		subject.logout();
    		
    		System.out.println("用户是否登录成功C:"+subject.isAuthenticated());
    		
    	}
    }
    
    

    当运行1时, 用户名和密码均正确时:

    有图片

    当运行2时,用户名正确,但密码不正确

    在这里插入图片描述
    当运行3时,用户名不存在

    在这里插入图片描述

    用户名和密码是我们自己在配置文件 shiro.ini 里面配置的,能不能在数据库里面查询呢?

    是可以的,我们可以实现 JdbcRealm

    二. 配置JdbcRealm

    二.一 JdbcRealm 内置 sql语句

    public class JdbcRealm
       extends AuthorizingRealm
     {
       protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password 
    from users where username = ?";
      protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt 
    from users where username = ?";
       protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name 
    from user_roles where username = ?";
      protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission 
    from roles_permissions where role_name = ?";
      private static final Logger log = LoggerFactory.getLogger(JdbcRealm.class);
     ...
    };
    

    JdbcRealm 继承了 AuthorizingRealm 抽象类。

    有图片

    由于 JdbcRealm 提供了内置的 sql 语句,所以如果我们想使用 JdbcRealm 那么就必须保证 用户表的表名必须为 users, 用户名必须为 username, 密码必须为 password 字段。 users 表不一定只有这两个字段,但必须要保证有这两个字段,如果想加盐加密,还需要有password_salt 字段。

    二.二 创建数据库 shiro,用户表为 users

    users 表里面必须有 username 和password 两个字段。

    有图片

    二.三 添加数据库和c3p0的相应依赖

    pom.xml 中追加依赖

    <dependency>
      		<groupId>com.mchange</groupId>
      		<artifactId>c3p0</artifactId>
      		<version>0.9.5.4</version>
      	</dependency>
      	<dependency>
      		<groupId>org.slf4j</groupId>
      		<artifactId>slf4j-log4j12</artifactId>
      		<version>1.7.25</version>
      	</dependency>
      	<dependency>
      		<groupId>commons-logging</groupId>
      		<artifactId>commons-logging</artifactId>
      		<version>1.2</version>
      	</dependency>
      	<dependency>
      		<groupId>mysql</groupId>
      		<artifactId>mysql-connector-java</artifactId>
      		<version>5.1.45</version>
      	</dependency>
    

    二.四 创建jdbcRealm.ini, 里面配置数据库的相应信息

    不用自定义用户信息了,直接通过 JdbcRealm 从数据库里面查询。 添加一个c3p0的数据库连接池。

    [main]
    #配置数据源
    dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
    #配置数据库的信息
    dataSource.driverClass=com.mysql.jdbc.Driver
    dataSource.jdbcUrl=jdbc:mysql://localhost:3306/shiro?characterEncoding=utf8
    dataSource.user=root
    dataSource.password=abc123
    #配置 realm,用JdbcRealm
    jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
    #配置数据源
    jdbcRealm.dataSource=$dataSource
    #注入到securityManager里面
    securityManager.realm=$jdbcRealm
    #不用自定义配置users 的信息
    

    需要在 [main] 里面进行配置, 类似于 Spring 的 注入。

    二.五 测试验证

    package com.yjl.demo;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.DisabledAccountException;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    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;
    
    /**
     * 
     * @author 两个蝴蝶飞
     * Shiro 的第三个演示文件, JdbcRealm的使用
     */
    public class ShiroDemo3 {
    	public static void main(String[] args) {
    		//1. 创建工厂 
    		Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:jdbcRealm.ini");
    		//2. 从工厂里面获取 SecurityManager
    		SecurityManager securityManager=factory.getInstance();
    		//3. 通过工具类设置 securityManager
    		SecurityUtils.setSecurityManager(securityManager);
    		
    		//4. 获取当前登录的用户
    		
    		Subject subject=SecurityUtils.getSubject();
    		
    		//5. 拼装用户的身份和密码Token
    		
    		UsernamePasswordToken token=new UsernamePasswordToken("yuezl","1234"); //1.正确密码
    		//UsernamePasswordToken token=new UsernamePasswordToken("yuezl","123456"); //2.错误密码
    		//UsernamePasswordToken token=new UsernamePasswordToken("yuezlAbc","1234"); //3.未知账户
    		
    		System.out.println("用户是否登录成功A:"+subject.isAuthenticated());
    		
    		//6. 调用 subject 里面的login 方法,进行登录
    		try{			
    			subject.login(token);
    			//7.判断用户是否登录成功
    			
    			if(subject.isAuthenticated()){
    				System.out.println("用户:"+token.getUsername()+", 登录成功");
    			}
    			
    		}catch (UnknownAccountException var6) {
                var6.printStackTrace();
                System.out.println("没有此账号");
            } catch (IncorrectCredentialsException var7) {
                var7.printStackTrace();
                System.out.println("密码不正确");
            } catch (DisabledAccountException var8) {
                var8.printStackTrace();
                System.out.println("账户不可用");
            }
    		System.out.println("用户是否登录成功B:"+subject.isAuthenticated());
    		
    		//退出程序
    		subject.logout();
    		
    		System.out.println("用户是否登录成功C:"+subject.isAuthenticated());
    		
    		
    	}
    }
    

    当运行1时, 用户名和密码均正确时:

    有图片

    当运行2时,用户名正确,但密码不正确

    在这里插入图片描述

    当运行3时,用户名不存在

    在这里插入图片描述

    注意,数据是从数据库里面查询出来的,并不是从 jdbcRealm.ini 里面配置出来的。

    一定要保证字段是一致的,不然无法通过认证。

    三. 认证策略

    securityManager 可以配置多个 Realm, 当配置不同Realm, 甚至Realm 是不同的数据库时,那么在验证的时候,以谁为准呢?

    这就是验证的策略。

    验证策略 AuthenticationStrategy 有三种:

    有图片

    策略名 作用
    AllSuccessfulStrategy 所有的Realm验证成功才算成功,且返回所有的Realm 认证信息,如果有一个没有通过,就是失败
    AtLeastOneSuccessfulStrategy 至少有一个成功,且返回所有通过认证的信息
    FirstSuccessfulStrategy 至少有一个成功,且只返回第一个通过认证的信息 (默认)

    AtLeastOneSuccessfulStrategy 与 FirstSuccessfulStrategy 的区别是, FirstSuccessfulStrategy 只返回第一条认证通过的信息,

    AtLeastOneSuccessfulStrategy则会返回所有认证通过的信息。

    下面写一个小例子,进行验证一下。

    三.一 创建数据库 shiro1, 里面有一个 users 数据表

    有图片

    注意,用户的密码。

    三.二 创建配置文件 jdbcStrategy.ini

    [main]
    #配置数据源
    dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
    #配置数据库的信息
    dataSource.driverClass=com.mysql.jdbc.Driver
    dataSource.jdbcUrl=jdbc:mysql://localhost:3306/shiro?characterEncoding=utf8
    dataSource.user=root
    dataSource.password=abc123
    #配置 realm
    jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
    #配置数据源
    jdbcRealm.dataSource=$dataSource
    
    #配置数据源1
    dataSource1=com.mchange.v2.c3p0.ComboPooledDataSource
    #配置数据库的信息
    dataSource1.driverClass=com.mysql.jdbc.Driver
    dataSource1.jdbcUrl=jdbc:mysql://localhost:3306/shiro1?characterEncoding=utf8
    dataSource1.user=root
    dataSource1.password=abc123
    #配置 realm
    jdbcRealm1=org.apache.shiro.realm.jdbc.JdbcRealm
    #配置数据源
    jdbcRealm1.dataSource=$dataSource1
    #注入多个realm 到securityManager里面
    securityManager.realms=$jdbcRealm,$jdbcRealm1
    #配置验证器
    authenticationStrategy=org.apache.shiro.authc.pam.FirstSuccessfulStrategy
    #authenticationStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
    #authenticationStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy
    securityManager.authenticator.authenticationStrategy=$authenticationStrategy
    
    

    三.三 测试文件,进行测试

    注意,用户名和密码

    Shiro 数据库里面, yuejl 的密码是1234, yuezl 的密码是1234

    Shiro1 数据库里面, yuejl的密码是12345, yuezl的密码是 1234.

    ShiroDemo5.java

    package com.yjl.demo;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.DisabledAccountException;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    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;
    
    /**
     * 
     * @author 两个蝴蝶飞
     * Shiro 的第五个演示文件, 策略
     */
    public class ShiroDemo5 {
    	public static void main(String[] args) {
    		//1. 创建工厂 
    		Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:jdbcStrategy.ini");
    		//2. 从工厂里面获取 SecurityManager
    		SecurityManager securityManager=factory.getInstance();
    		//3. 通过工具类设置 securityManager
    		SecurityUtils.setSecurityManager(securityManager);
    		
    		//4. 获取当前登录的用户
    		
    		Subject subject=SecurityUtils.getSubject();
    		
    		//5. 拼装用户的身份和密码Token
    		
    		//怕乱,不注释写了,后面的程序,只改变这一个 token 代码
    		UsernamePasswordToken token=new UsernamePasswordToken("yuejl","1234"); 
    		//6. 调用 subject 里面的login 方法,进行登录
    		try{			
    			subject.login(token);
    			//7.判断用户是否登录成功
    			
    			if(subject.isAuthenticated()){
    				System.out.println("用户:"+token.getUsername()+", 登录成功");
    			}
    			
    		}catch (UnknownAccountException var6) {
                var6.printStackTrace();
                System.out.println("没有此账号");
            } catch (IncorrectCredentialsException var7) {
                var7.printStackTrace();
                System.out.println("密码不正确");
            } catch (DisabledAccountException var8) {
                var8.printStackTrace();
                System.out.println("账户不可用");
            }
    	}
    }
    
    

    三.三.一 FirstSuccessfulStrategy 默认策略

    1. 如果用户是 yuejl, 1234 的话, shiro 符合,shiro1 不符合, 符合至少一个成功

    UsernamePasswordToken token=new UsernamePasswordToken("yuejl","1234"); 
    

    运行程序:

    有图片

    2. 如果用户是 yuejl, 12345 的话, shiro 不符合,shiro1 符合,符合至少一个成功

    UsernamePasswordToken token=new UsernamePasswordToken("yuejl","12345"); 
    

    运行程序:

    有图片

    3. 如果用户是 yuezl, 1234 的话, shiro 符合,shiro1 符合,符合至少一个成功

    UsernamePasswordToken token=new UsernamePasswordToken("yuezl","1234"); 
    

    运行程序:

    有图片

    三.三.二 AtLeastOneSuccessfulStrategy

    在 jdbcStrategy.ini 里面,将策略改成 AtLeastOneSuccessfulStrategy

    1. 如果用户是 yuejl, 1234 的话, shiro 符合,shiro1 不符合,符合至少一个成功

    UsernamePasswordToken token=new UsernamePasswordToken("yuejl","1234"); 
    

    运行程序:

    有图片

    2. 如果用户是 yuejl, 12345 的话, shiro 不符合,shiro1 符合,符合至少一个成功

    UsernamePasswordToken token=new UsernamePasswordToken("yuejl","12345"); 
    

    运行程序:

    有图片

    3. 如果用户是 yuezl, 1234 的话, shiro 符合,shiro1 符合,符合至少一个成功

    UsernamePasswordToken token=new UsernamePasswordToken("yuezl","1234"); 
    

    运行程序:

    有图片

    三.三.三 AllSuccessfulStrategy

    在 jdbcStrategy.ini 里面,将策略改成 AllSuccessfulStrategy

    1. 如果用户是 yuejl, 1234 的话, shiro 符合,shiro1 不符合,不符合全部成功

    UsernamePasswordToken token=new UsernamePasswordToken("yuejl","1234"); 
    

    运行程序:

    有图片

    2. 如果用户是 yuejl, 12345 的话, shiro 不符合,shiro1 符合,不符合全部成功

    UsernamePasswordToken token=new UsernamePasswordToken("yuejl","12345"); 
    

    运行程序:

    有图片

    3. 如果用户是 yuezl, 1234 的话, shiro 符合,shiro1 符合,符合全部成功

    UsernamePasswordToken token=new UsernamePasswordToken("yuezl","1234"); 
    

    运行程序:

    有图片
    策略演示成功 。

    本章节代码链接为:

    链接:https://pan.baidu.com/s/1u8i2KHx0FuWbAbf4Fuejzg 
    提取码:okrl
    

    谢谢您的观看,我是两个蝴蝶飞, 如果喜欢,请关注我,再次感谢 !!!

  • 相关阅读:
    CSP-S2-2019游记
    【BZOJ2301】【HAOI2011】Problem B
    【NOIp2017】宝藏
    【NOIp2016】天天爱跑步
    【NOIp2018】保卫王国
    【BZOJ2159】Crash的文明世界
    Van爆零赛总结
    【ZJOI2016】小星星
    【CF1025D】Recovering BST
    【HAOI2016】字符合并
  • 原文地址:https://www.cnblogs.com/yjltx/p/14343146.html
Copyright © 2011-2022 走看看