Shiro【内置Realm实操】

一、前言

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新手,若有错误,欢迎指正!

posted @ 2021-03-04 15:43  跑调大叔!  阅读(115)  评论(0编辑  收藏  举报