Spring Boot 使用 JdbcRealm 进行认证授权
概述
上一篇博客我们主要讲了把用户安全信息(相应的角色/权限)配置在 .ini
文件,使用 IniRealm
去读取 .ini
文件获得用户的安全信息。也是有局限性的。因为我们得事先把所有用户信息配置在.ini
文件,这样显然是行不通的,我们的系统用户都是动态的不固定的,它的一些用户信息权限信息都是变化的,所以固定在.ini
配置文件显然是行不通的。这些数据通常我们都是把它存入到DB
中,那shiro
有没有提供直接从DB读取用户安全信息的域呢 ? (Realm)
shiro 作为一个优秀的开源框架,显然是可以的。
下面就该 JdbcRealm 出场了。
JdbcRealm 实践
JdbcRealm 实践
很明显看到 jdbc
就明白是跟数据有关的吧,所以我们要引入 mysql
数据库驱动
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
数据源
有了mysql数据库驱动是不是得加个数据源呀?这里我们用阿里的druid
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
数据库表结构
下面我们创建一个名为 shiro 的数据库、分别创建三张表 users、user_roles、roles_permissions
CREATE DATABASE IF NOT EXISTS shiro DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
- users
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(25) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- roles_permissions
CREATE TABLE `roles_permissions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`permission` varchar(255) DEFAULT NULL,
`role_name` varchar(25) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- user_roles
CREATE TABLE `user_roles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_name` varchar(25) DEFAULT NULL,
`username` varchar(25) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 分别插入如下几条数据
USE shiro;
INSERT INTO `shiro`.`users` (`id`, `username`, `password`) VALUES ('1', 'admin', '123456');
INSERT INTO `shiro`.`users` (`id`, `username`, `password`) VALUES ('2', 'test', '123456');
INSERT INTO `shiro`.`user_roles` (`id`, `role_name`, `username`) VALUES ('1', 'admin', 'admin');
INSERT INTO `shiro`.`user_roles` (`id`, `role_name`, `username`) VALUES ('2', 'test', 'test');
INSERT INTO `shiro`.`roles_permissions` (`id`, `permission`, `role_name`) VALUES ('1',
'user:deleted', 'test');
INSERT INTO `shiro`.`roles_permissions` (`id`, `permission`, `role_name`) VALUES ('2',
'user:list', 'test');
INSERT INTO `shiro`.`roles_permissions` (`id`, `permission`, `role_name`) VALUES ('3', '*',
'admin');
INSERT INTO `shiro`.`roles_permissions` (`id`, `permission`, `role_name`) VALUES ('4',
'user:edit', 'test');
创建 testJdbcRealm方法
@Test
public void testJdbcRealm(){
//配置数据源
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/shiro");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");
//配置文件中的用户权限信息,文件在类路径下
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
//使用JdbcRealm下面的值需要为true不然无法查询用户权限
jdbcRealm.setPermissionsLookupEnabled(true);
//1,构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//设置Realm
defaultSecurityManager.setRealm(jdbcRealm);
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取主体
Subject subject = SecurityUtils.getSubject();
//用户名和密码的token
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
try {
//2,主体提交认证请求
subject.login(token);
System.out.println("认证状态:isAuthenticated=" + subject.isAuthenticated());
//检查是否有角色
subject.checkRoles("admin");
System.out.println("有admin角色");
//检查是否有权限
subject.checkPermissions("user:delete");
System.out.println("有user:delete权限");
}catch (IncorrectCredentialsException exception){
System.out.println("用户名或密码错误");
}catch (LockedAccountException exception){
System.out.println("账号已被锁定");
}catch (DisabledAccountException exception){
System.out.println("账号已被禁用");
}catch (UnknownAccountException exception){
System.out.println("用户不存在");
}catch ( UnauthorizedException ae ) {
System.out.println("用户没有权限");
}
}
测试
当用户 admin 登录进来的时候 程序输出
认证状态:isAuthenticated=true
有admin角色
有user:delete权限
当用户 test 登录进来的时候 程序输出
UsernamePasswordToken token = new UsernamePasswordToken("test", "123456");
认证状态:isAuthenticated=true
用户没有权限
源码分析
看了这个例子,为什么我们没有写 sql 怎么查询用户信息和角色,权限信息的呢?我们来看JdbcRealm源码
从以上源码得知其实 JdbcRealm 已经帮我们写好查询语句了,所以我们就要在数据库创建与之对应的表结构,这样才能查出数据,但是有的同学可能就有疑问了,这里只能使用他默认的 sql,在实际的开发中,我们不可能就简单的使用 JdbcRealm 默认的 sql 语句,而是自己自定义的 sql 语句,更多时候我们的数据库以及数据表都是根据业务需要自己创建的。
- 首先把上面三张表分别修改一下表名
USE shiro;
ALTER TABLE users RENAME sys_users;
ALTER TABLE user_roles RENAME sys_user_roles;
ALTER TABLE roles_permissions RENAME sys_roles_permissions;
新建testNewJdbcRealm
@Test
public void testNewJdbcRealm(){
//配置数据源
DruidDataSource dataSource = new DruidDataSource();
//如果使用的是新版的驱动 配置driver的时候要注意
dataSource.setUrl("jdbc:mysql://localhost:3306/shiro?serverTimezone=UTC");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("root");
//配置文件中的用户权限信息,文件在类路径下
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
//使用JdbcRealm下面的值需要为true不然无法查询用户权限
jdbcRealm.setPermissionsLookupEnabled(true);
//使用自定义sql查询用户信息
String sql="select password from sys_users where username = ?";
jdbcRealm.setAuthenticationQuery(sql);
String roleSql = "select role_name from sys_user_roles where username = ?";
jdbcRealm.setUserRolesQuery(roleSql);
String permissionsSql = "select permission from sys_roles_permissions where role_name = ?";
jdbcRealm.setPermissionsQuery(permissionsSql);
//1,构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//设置Realm
defaultSecurityManager.setRealm(jdbcRealm);
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取主体
Subject subject = SecurityUtils.getSubject();
//用户名和密码的token
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
try {
//2,主体提交认证请求
subject.login(token);
System.out.println("认证状态:isAuthenticated=" + subject.isAuthenticated());
//检查是否有角色
subject.checkRoles("admin");
System.out.println("有admin角色");
//检查是否有权限
subject.checkPermissions("user:delete","user:edit","user:list","user:add");
System.out.println("有 user:add、user:list、user:edit、user:delete权限");
}catch (IncorrectCredentialsException exception){
System.out.println("用户名或密码错误");
}catch (LockedAccountException exception){
System.out.println("账号已被锁定");
}catch (DisabledAccountException exception){
System.out.println("账号已被禁用");
}catch (UnknownAccountException exception){
System.out.println("用户不存在");
}catch ( UnauthorizedException ae ) {
System.out.println("用户没有权限");
}
}
- 最后程序输出
认证状态:isAuthenticated=true
有admin角色
有 user:add、user:list、user:edit、user:delete权限