一、前言
在 Shiro【初识】中我们已经知道 Realm 主要是用来从数据库中获取用户、角色、资源等数据的。
但 Shiro 还给我们提供了很多 Realm,让我们可以操作其他的数据源,比如配置文件等等。
二、项目环境
本文中的项目使用环境为:JDK8 + Maven 3.6.3 + SpringBoot 2.4.1 + Shiro 1.4.0 + Druid 1.1.6
项目依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
三、Shiro内置 IniRealm 实操
类路径下有如下配置文件:
shiro.ini
# 格式 name=password,role1,role2,..roleN
[users]
jack = 456, user
zhangsan = 123, root, admin
# 格式 role=permission1,permission2...permissionN 也可以用通配符
# 下面配置user的权限为所有video:find,video:buy,如果需要配置video全部操作crud 则 user = video:*
[roles]
user = video:find,video:buy
visitor = good:find,good:add
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
测试类的代码如下:
/**
* 测试 IniRealm 的使用:从ini格式的文件中获取用户、角色、资源(权限)
*/
public class IniRealmTest {
@Test
public void test(){
// 创建SecurityManager工厂,通过配置文件ini创建
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
// 构造SecurityManager环境
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jack", "456");
subject.login(usernamePasswordToken);
// 判断是否认证成功
System.out.println(" 认证结果:"+subject.isAuthenticated());
// 判断是否有对应角色
System.out.println(" 是否有对应的user角色:"+subject.hasRole("user"));
// 获取账号
System.out.println(" getPrincipal=" + subject.getPrincipal());
// 检查是否有指定角色(无返回值)
subject.checkRole("user");
// 检查是否有指定权限(无返回值)
subject.checkPermission("video:find");
// 检查是否有指定权限(有返回值)
System.out.println( "是否有video:find 权限:"+ subject.isPermitted("video:find"));
subject.logout();
System.out.println("logout后认证结果:"+subject.isAuthenticated());
}
}
四、Shiro内置 JdbcRealm 实操
(一)方式一
类路径下有如下配置文件:
jdbcrealm.ini
#注意 文件格式必须为ini,编码为ANSI
#声明Realm,指定realm类型
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
#配置数据源
#dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource=com.alibaba.druid.pool.DruidDataSource
# mysql-connector-java 5 用的驱动url是com.mysql.jdbc.Driver,mysql-connector-java6以后用的是com.mysql.cj.jdbc.Driver
dataSource.driverClassName=com.mysql.cj.jdbc.Driver
#避免安全警告
dataSource.url=jdbc:mysql://localhost:3306/shiro_test?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
dataSource.username=root
dataSource.password=root
#指定数据源
jdbcRealm.dataSource=$dataSource
#开启查找权限
jdbcRealm.permissionsLookupEnabled=true
#指定SecurityManager的Realms实现,设置realms,可以有多个,用逗号隔开
securityManager.realms=$jdbcRealm
数据库中表的格式、字段名称、关联关系一定要按照 Shiro 的格式进行创建,否则会报错。
其SQL脚本如下:
DROP TABLE IF EXISTS `roles_permissions`;
CREATE TABLE `roles_permissions` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_name` varchar(100) DEFAULT NULL,
`permission` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_roles_permissions` (`role_name`,`permission`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `roles_permissions` WRITE;
/*!40000 ALTER TABLE `roles_permissions` DISABLE KEYS */;
INSERT INTO `roles_permissions` (`id`, `role_name`, `permission`)
VALUES
(4,'admin','video:*'),
(3,'role1','video:buy'),
(2,'role1','video:find'),
(5,'role2','*'),
(1,'root','*');
/*!40000 ALTER TABLE `roles_permissions` ENABLE KEYS */;
UNLOCK TABLES;
DROP TABLE IF EXISTS `user_roles`;
CREATE TABLE `user_roles` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`role_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_roles` (`username`,`role_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `user_roles` WRITE;
/*!40000 ALTER TABLE `user_roles` DISABLE KEYS */;
INSERT INTO `user_roles` (`id`, `username`, `role_name`)
VALUES
(1,'jack','role1'),
(2,'jack','role2'),
(4,'xdclass','admin'),
(3,'xdclass','root');
/*!40000 ALTER TABLE `user_roles` ENABLE KEYS */;
UNLOCK TABLES;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
`password_salt` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_users_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
INSERT INTO `users` (`id`, `username`, `password`, `password_salt`)
VALUES
(1,'jack','123',NULL),
(2,'xdclass','456',NULL);
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
UNLOCK TABLES;
测试类的代码如下:
/**
* 测试 JdbcRealm 的使用:从数据库中获取用户、角色、资源(权限)
* 其实都是将数据获取到 Realm 对象中,只不过该种方式是通过 ini 文件的方式获取数据库信息从而获取数据
*/
public class JdbcRealmTest01 {
@Test
public void test() {
//创建SecurityManager工厂,通过配置文件ini创建
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:jdbcrealm.ini");
// 构造SecurityManager环境
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
//用户输入的账号密码
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jack", "123");
subject.login(usernamePasswordToken);
System.out.println(" 认证结果:"+subject.isAuthenticated());
System.out.println(" 是否有对应的role1角色:"+subject.hasRole("role1"));
System.out.println(" 是否有video:find权限:"+ subject.isPermitted("video:find"));
}
}
(二)方式二
/**
* 测试 JdbcRealm 的使用:从数据库中获取用户、角色、资源(权限)
* 其实都是将数据获取到 Realm 对象中,只不过该种方式是通过连接池的方式获取数据库信息从而获取数据
*/
public class JdbcRealmTest02 {
@Test
public void test2(){
DefaultSecurityManager securityManager = new DefaultSecurityManager();
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/shiro_test?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false");
ds.setUsername("root");
ds.setPassword("root");
JdbcRealm jdbcRealm = new JdbcRealm();
// 开启查找权限
jdbcRealm.setPermissionsLookupEnabled(true);
jdbcRealm.setDataSource(ds);
securityManager.setRealm(jdbcRealm);
//将securityManager 设置到当前运行环境中
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
//用户输入的账号密码
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jack", "123");
subject.login(usernamePasswordToken);
System.out.println(" 认证结果:"+subject.isAuthenticated());
System.out.println(" 是否有对应的role1角色:"+subject.hasRole("role1"));
System.out.println(" 是否有video:find权限:"+ subject.isPermitted("video:find"));
System.out.println(" 是否有任意权限:"+ subject.isPermitted("aaaa:xxxxxxxxx"));
}
}
Java新手,若有错误,欢迎指正!