一. Shiro 的认证错误
在上一章节的例子中,我们可以看到, Shiro 是通用 Subject 对象的 login() 方法,进行认证判断的。 我们自己在开发中,常常有这么一个需求,在登录时,如果登录失败,会给出一些相应的提示,如用户名不存在,密码错误,账号不可用等信息。 Shiro 也提供了相应的功能。
public abstract void login(AuthenticationToken paramAuthenticationToken) throws AuthenticationException;
Shiro 会抛出一个异常, AuthenticationException。
这个异常有许多子类,
通常会捕获相应的子类异常信息,进行相应的错误提示。
常见的是 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 里面配置出来的。
一定要保证字段是一致的,不然无法通过认证。