前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Shiro Realm

Shiro Realm

作者头像
一份执着✘
发布于 2018-08-27 08:24:34
发布于 2018-08-27 08:24:34
84000
代码可运行
举报
文章被收录于专栏:赵俊的Java专栏赵俊的Java专栏
运行总次数:0
代码可运行

Realm 是什么

Realm: 域,Shiro 从 Realm 中获取用户,角色,权限信息。可以把 Relam 看成 DataSource,即安全数据源。

在前两章的认证和授权中,我们也使用到了 SimpleAccountRealm,并通过其 addAccount(username, password, roles) 来预设用户和角色信息。

IniRealm

IniRealm 顾名思义,即通过读取 .ini 文件来获取用户,角色,权限信息。

配置用户名/密码及其角色, 格式: “用户名=密码,角色1,角色2”,如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[users]
zhao = 123456, admin, user
wang = 123456, user

配置角色及权限之间的关系, 格式: “角色=权限1, 权限2”, 如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[roles]
admin = user:delete
user = user:select

结合起来,即 :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[users]
zhao = 123456, admin, user
wang = 123456, user

[roles]
admin = user:delete
user = user:select

然后进行测试 :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

import java.util.Arrays;

public class IniRealmTest {

    @Test
    public void testIniRealm() {
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        Realm iniRealm = new IniRealm("classpath:shiro.ini");
        defaultSecurityManager.setRealm(iniRealm);
        SecurityUtils.setSecurityManager(defaultSecurityManager);

        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("zhao", "123456");

        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            System.out.println("登陆失败");
        }

        System.out.println("--------------------认证--------------------");
        System.out.println("是否具备 admin 权限: " + subject.hasRole("admin"));
        System.out.println("是否具备 user 权限: " + subject.hasRole("user"));
        System.out.println("是否同时具备 admin 和 user 权限: " + subject.hasAllRoles(Arrays.asList("admin", "user")));

        System.out.println("--------------------授权--------------------");
        System.out.println("是否具备 user:delete 权限" + subject.isPermitted("user:delete"));
        System.out.println("是否具备 user:select 权限" + subject.isPermitted("user:select"));
        System.out.println("是否同时具备 user:delete 和 user:select 权限" + subject.isPermittedAll("user:delete", "user:select"));
    }
}

跟前两章的代码没有什么不同,只是将 SimpleAccountRealm 换成了 IniRealm。

JdbcRelam

JdbcRelam 顾名思义,即通过通过访问数据库来获取用户,角色,权限信息。

首先需要导入 mysql 的驱动包和 druid 数据库连接池的包:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.32</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

创建数据库表和初始化数据 :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for roles_permissions
-- ----------------------------
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 AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of roles_permissions
-- ----------------------------
INSERT INTO `roles_permissions` VALUES ('1', 'admin', 'user:delete');
INSERT INTO `roles_permissions` VALUES ('2', 'user', 'user:select');

-- ----------------------------
-- Table structure for users
-- ----------------------------
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 AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('1', 'zhao', '123456', null);

-- ----------------------------
-- Table structure for user_roles
-- ----------------------------
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 AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user_roles
-- ----------------------------
INSERT INTO `user_roles` VALUES ('1', 'zhao', 'admin');
INSERT INTO `user_roles` VALUES ('2', 'zhao', 'user');

测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;

/**
 * Shiro JdbcRealm 测试
 */
public class JdbcRealmTest {
    private DruidDataSource dataSource = new DruidDataSource();

    /**
     * 初始化 DataSource
     */
    @Before
    public void before() {
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/shiro");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
    }

    @Test
    public void testJdbcRealm() {
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        // 构建 JdbcRelam
        JdbcRealm jdbcRealm = new JdbcRealm();
        // 为 JdbcRelam 设置数据源
        jdbcRealm.setDataSource(dataSource);
        // 设置启用权限查询, 默认为 false
        jdbcRealm.setPermissionsLookupEnabled(true);

        defaultSecurityManager.setRealm(jdbcRealm);
        SecurityUtils.setSecurityManager(defaultSecurityManager);

        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("zhao", "123456");

        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            System.out.println("登陆失败");
        }

        System.out.println("--------------------认证--------------------");
        System.out.println("是否具备 admin 权限: " + subject.hasRole("admin"));
        System.out.println("是否具备 user 权限: " + subject.hasRole("user"));
        System.out.println("是否同时具备 admin 和 user 权限: " + subject.hasAllRoles(Arrays.asList("admin", "user")));
        System.out.println("--------------------授权--------------------");
        System.out.println("是否具备 user:delete 权限" + subject.isPermitted("user:delete"));
        System.out.println("是否具备 user:select 权限" + subject.isPermitted("user:select"));
        System.out.println("是否同时具备 user:delete 和 user:select 权限" + subject.isPermittedAll("user:delete", "user:select"));
    }
}

这里也只是将 Relam 修改了一下,其他代码是一样的。

细心的朋友可能会有疑问,我们这里没有写一行查询语句,那么Shiro 怎么知道你的数据库结构的,它如何来查询角色和权限信息。

其实我们点开 JdbcRelam 的源码看看就知道了,它内置了默认的查询角色和权限的 SQL 语句:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 = ?";

而上面我们建立的表结构是符合这些查询语句的。

但在实际项目开发中,我们不可能完全按照 Shiro 提供的这种方式来建表,我们可以通过修改 JdbcRealm 的默认查询语句来实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
jdbcRealm.setAuthenticationQuery(String authenticationQuery);
jdbcRealm.setPermissionsQuery(String permissionsQuery);
jdbcRealm.setUserRolesQuery(String userRolesQuery);

自定义 Relam

在真实项目开发中,我们往往会使用自定义 Realm 来实现一些自定义的功能,如判断账号锁定,账号登陆次数限制等。

我们需要创建一个类来继承 AuthorizingRealm ,并实现其抽象方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import im.zhaojun.pojo.User;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.HashSet;
import java.util.Set;

/**
 * 自定义 Realm
 */
public class MyCustomRealm extends AuthorizingRealm {


    /**
     * 根据用户凭证查询所用拥有的角色和权限
     * @param principalCollection 用户凭证
     * @return 返回授权信息,包含所拥有的角色和权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String)principalCollection.getPrimaryPrincipal();

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        // 根据用户名其所拥有的角色和权限
        Set<String> roles = selectRolesByUserName(username);
        Set<String> permissions = selectPermissionsByUserName(username);

        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }


    /**
     * 根据用户提交的凭证查询是否具有这个用户 (这里不判断密码是否正确)
     * @param authenticationToken 用户凭证 (账户密码)
     * @return 相应的用户信息
     * @throws AuthenticationException 当用户不存在或具备其他状态, 如被锁定, 等状态会抛出相应的异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        // 这个方法也可以使用 DAO 层的方法来查询数据库,返回 user 对象。
        User user = selectUserByUserName((String) authenticationToken.getPrincipal());

        if (user == null) {
            throw new UnknownAccountException("账号不存在");
        }

        return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), super.getName());
    }


    private Set<String> selectPermissionsByUserName(String username) {
        HashSet<String> permissions = new HashSet<>();
        // 假设只有 zhao 这个用户具备 select 权限
        if ("zhao".equals(username)) {
            permissions.add("select");
        }
        return permissions;
    }

    private Set<String> selectRolesByUserName(String username) {
        HashSet<String> roles = new HashSet<>();

        // 假设只有 zhao 这个用户具备 user 角色
        if ("zhao".equals(username)) {
            roles.add("user");
        }
        return roles;
    }

    private User selectUserByUserName(String username) {
        User user = null;

        // 假设当前只有 zhao - 123465 这个账户.
        if ("zhao".equals(username)) {
            user = new User("zhao", "123456");
        }
        return user;
    }
}

这里的代码也很简单,主要是实现了父类的抽象方法 :

  • doGetAuthenticationInfo : 获取用户认证信息,在这里我们不需要校验密码是否正确,因为有专门的密码校验器来做这件事,我们只需要返回认证信息即可。 (认证信息在这个示例中为 SimpleAuthenticationInfo, 即账号密码) 当然你也可以在返回认证信息前根据用户的状态,如冻结,锁定,或登陆次数来抛出相应的异常,以直接返回登陆失败,而不再进行密码校验。
  • doGetAuthorizationInfo : 获取用户授权信息,授权信息包括所拥有的角色和权限信息,这里的逻辑很简单,只需要根据用户信息查询出角色和权限,配置到 AuthorizationInfo 中返回即可 。

测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import im.zhaojun.realm.MyCustomRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

/**
 * 自定义 Realm 测试
 */
public class MyCustomRealmTest {

    @Test
    public void testCustomRealm() {
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        MyCustomRealm realm = new MyCustomRealm();
        securityManager.setRealm(realm);

        SecurityUtils.setSecurityManager(securityManager);

        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("zhao", "123456");

        subject.login(token);

        System.out.println("是否具备 user 角色: " + subject.hasRole("user"));
        System.out.println("是否具备 select 权限: " + subject.isPermitted("select"));
    }
}

本章代码地址 : https://github.com/zhaojun1998/Premission-Study/tree/master/Permission-Shiro-03/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-08-102,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Shiro框架学习
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
全栈程序员站长
2022/10/05
4100
Shiro框架学习
Shiro安全框架入门学习
  1)、Apache的强大灵活的开源安全框架。   2)、认证、授权、企业会话管理、安全加密。
别先生
2020/09/01
4780
Shiro安全框架入门学习
JAVAEE安全框架之shiro第一课
new UsernamePasswordToken(username, password);
张哥编程
2024/12/13
1180
JAVAEE安全框架之shiro第一课
Shiro | 实现权限验证完整版
提及权限,就会想到安全,是一个十分棘手的话题。这里只是作为学校Shiro的一个记录,而不是,权限就应该这样设计之类的。
全栈程序员站长
2022/06/29
5070
Shiro | 实现权限验证完整版
Shiro安全框架:认证
Shiro 安全框架 1. 认证 1. 采用简单的对象登陆认证(SimpleAccountRealm) public class AuthenticationTest { // 创建一个简单的认证 realm 也就是认证信息存放在对象中的 SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm(); @Before public void addUser(){ simpleAccountR
lwen
2018/06/13
5720
shiro框架是什么_中国历史知识框架
2.Shiro权限框架 2.1 概念 2.2 Apache Shiro 与Spring Security区别
全栈程序员站长
2022/10/05
5450
shiro框架是什么_中国历史知识框架
不解释,全网最全Shiro认证与授权原理分析
本篇为《Shiro从入门到精通》系列第二篇,在上篇《还在手写filter进行权限校验?尝试一下Shiro吧》中,我们学习了Shiro的基本功能、架构以及各个组件的概念。本篇文章继续深入,以官方示例为基础,讲解使用Shiro的流程以及认证和授权的原理分析。下面开始正文:
程序新视界
2021/01/29
8110
shiro
  Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Mister24
2018/12/27
7380
深入理解Shiro
​ Apache Shiro(发音为“shee-roh”,日语中“城堡”的意思)是一个功能强大且易于使用的 Java 安全框架,可执行身份验证、授权、加密和会话管理,可用于保护任何应用程序 -从命令行应用程序、移动应用程序到最大的 Web 和企业应用程序。
用户11097514
2024/05/30
2330
深入理解Shiro
Shiro 集成 Spring
本章我们来学习 Shiro 集成 Spring,即在 Web 环境下如何使用 Shiro 来进行权限控制。
一份执着✘
2018/08/27
7270
Shiro系列 | 《Shiro开发详细教程》第四章:Shiro中Ini配置
之前章节我们已经接触过一些 INI 配置规则了,如果大家使用过如 Spring 之类的 IOC/DI 容器的话,Shiro 提供的 INI 配置也是非常类似的,即可以理解为是一个 IOC/DI 容器,但是区别在于它从一个根对象SecurityManager 开始。
码神联盟
2018/12/11
8150
权限管理与Shiro入门(十一)
Realm域:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源
用户1289394
2023/10/07
2080
权限管理与Shiro入门(十一)
其实我不仅会 Spring Security,Shiro 也略懂一二!
和大家分享一个松哥原创的 Shiro 教程吧,还没写完,先整一部分,剩下的敬请期待。
江南一点雨
2021/04/22
1K0
其实我不仅会 Spring Security,Shiro 也略懂一二!
shiro:整合springboot快速上手(附带代码示例)
Apache Shiro是一个java的安全管理框架,可以用在javaEE环境下,也可以用在javaSE环境下。
冷环渊
2021/10/19
5750
Redis在Window服务下的安装
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pyycsd/article/details/80969693
用户1212940
2019/06/20
1.1K0
Redis在Window服务下的安装
Shiro集成Spring
1、Shiro集成Spring,使用maven进行jar包的依赖与管理,pom.xml的配置文件,如下所示:
别先生
2020/09/01
1.3K0
Shiro集成Spring
细说shiro之五:在spring框架中集成shiro
特别地!Shiro使用了日志框架slf4j,因此需要对应配置指定的日志实现组件,如:log4j,logback等。 在此,以使用log4j为日志实现为例:
编程随笔
2019/09/11
6790
细说shiro之五:在spring框架中集成shiro
shiro教程1(HelloWorld)
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Java帮帮
2019/12/13
3750
shiro教程1(HelloWorld)
shiro 第一个授权
1 第一个shiro授权程序 package com.shi.authorization; import java.util.Arrays; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.Secu
用户5927264
2019/07/31
3510
Shiro入门使用
首先创建 DefaultSecurityManager 实例,其中可以传入两个参数,Realm 和 Collection<Realm>,前面我们提到 Realm 领域,指的是用户账号权限信息。我们可以看一下它的实现类。
乐心湖
2021/02/24
5360
相关推荐
Shiro框架学习
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验