引言
Realm 是安全数据源的意思,配置在 Realm 中的数据,我们可以等同于放置在数据库中的数据一样来看待,因为它们都是“绝对正确”的,我们通过“绝对正确”的数据,去完成对登录用户的数据的判定。
在第 1 节中,我们把“正确的”用户名和密码,放在了一个配置文件中,用于校验用户填写的用户名和密码是否正确。这个“校验”的操作是 Shiro 帮助我们完成的,校验不正确,Shiro 通过抛出异常的方式告诉我们。
这一节,我们来看看,我们把“正确的”数据放在数据库中,又怎样操作呢?
强调:本节内容,只用于测试,不适合在生产环境中使用
步骤:
1、引入依赖
// 上一节 Hello World 部分的依赖
testCompile group: 'junit', name: 'junit', version: '4.12'
compile group: 'org.apache.shiro', name: 'shiro-core', version: '1.3.0'
compile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.21'
// 这一节使用 JdbcRealm 添加的依赖
compile 'mysql:mysql-connector-java:5.1.38'
compile 'c3p0:c3p0:0.9.1.2'
compile 'commons-logging:commons-logging:1.2'
2、编写配置文件
[main]
# 表示实例化右边字符串表示的类,赋给左边字符串表示的变量。
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass=com.mysql.jdbc.Driver
dataSource.user=root
dataSource.password=123456
# 表示对 dataSource 这个变量设置属性值 jdbcUrl ,这个属性值是一个字符串。
dataSource.jdbcUrl=jdbc:mysql://localhost:3306/db_shiro?useUnicode=true&characterEncoding=UTF-8
# 表示对 jdbcRealm 这个变量设置属性值 dataSource , 这个 dataSource 属性是上面实例化的一个对象,所以表示这个对象要使用前缀 `$`。
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm
3、编写 SQL 初始化脚本
DROP DATABASE db_shiro;
# 创建数据库 db_shiro
CREATE DATABASE db_shiro DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
# 使用数据库 db_shiro
USE db_shiro;
drop table if exists users;
create table users(
id int(11) not null auto_increment comment '主键',
username varchar(20) default null comment '用户名',
password varchar(20) default null comment '密码',
PRIMARY KEY (id)
)engine=innodb auto_increment=1 default charset=utf8 comment '用户表';
insert into users(username,password) values('liwei','123456');
insert into users(username,password) values('zhouguang','666666');
下面,我们解释一下,为什么表名设置成 users。
答案很简单,是因为表名 users 是写死在代码中的。我们打开 JdbcRealm 的源代码就很容易发现这一点。
4、编写测试代码
package com.liwei.shiro;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.jvm.hotspot.HelloWorld;
/**
* Created by liwei on 16/8/11.
*/
public class JdbcRealmTest {
/**
* 模拟登录界面,把用户尝试登录的信息填写在下面的静态常量中,去体验 Shiro 的 Hello World
*/
private static String loginUserName = "liwei"; // 登录界面的用户名填写
private static String loginPassword = "123456"; // 登录界面的密码填写
/**
* 此时关于 Shiro 的配置,采用的是 classpath 的 jdbc_realm.ini 文件中
* 我们须要关注 [main] 节点的配置
* Shiro 通过依赖注入的方式,找到了存放用户名和密码的 user 表
*/
private static String iniResourcePath = "classpath:jdbc_realm.ini";
private static Logger LOGGER = LoggerFactory.getLogger(HelloWorld.class);
public static void main(String[] args) {
// 前 4 步和上一节是一样的,是模板代码
Factory<SecurityManager> factory = new IniSecurityManagerFactory(iniResourcePath);
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(loginUserName, loginPassword);
try {
currentUser.login(token);
LOGGER.info("登录验证通过,即用户名和密码与 user 表中的 userName 和 password 字段值完全匹配。");
} catch (AuthenticationException e) {
// UnknownAccountException 和 IncorrectCredentialsException
// 这两个异常都是 AuthenticationException 的子类,所以可以通过这个异常类同一捕获
LOGGER.error("登录验证失败!");
e.printStackTrace();
}
currentUser.logout();
}
}
第 1、2 节,是简单的 Shiro 的 Hello World。让我们对 Shiro 有了一个直观的感觉。Shiro 让我们不用写“如果用户名不存在,就提示用户名不存在”;“在用户名存在的情况下,去检验用户输入的密码是否和数据库中输入的密码相符”这样的逻辑。这是关于身份认证的操作,Shiro 替我们完成了。