【Shiro】Apache Shiro架构之自定义realm

2024-06-22 05:32

本文主要是介绍【Shiro】Apache Shiro架构之自定义realm,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Shiro系列文章: 
【Shiro】Apache Shiro架构之身份认证(Authentication) 
【Shiro】Apache Shiro架构之权限认证(Authorization) 
【Shiro】Apache Shiro架构之集成web 
【Shiro】Apache Shiro架构之实际运用(整合到Spring中)

   
  之前写的博客里都是使用.ini文件来获取信息的,包括用户信息,角色信息,权限信息等。进入系统时,都是从.ini文件这读取进入的。实际中除非这个系统特别特别简单,否则一般都不是这样干的,这些信息都是需要在数据库中进行维护的,所以就需要用到自定义realm了。

写在前面:这篇博文是基于上一篇Shiro集成web基础之上修改的。

1. 数据库建表

  首先在数据库中新建三个表:t_user,t_role和t_permission,分别存储用户信息,角色信息和权限信息,建表语句如下:

CREATE TABLE `t_role` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`rolename` varchar(20) DEFAULT NULL COMMENT '角色名称',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8CREATE TABLE `t_user` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户主键',`username` varchar(20) NOT NULL COMMENT '用户名',`password` varchar(20) NOT NULL COMMENT '密码',`role_id` int(11) DEFAULT NULL COMMENT '外键关联role表',PRIMARY KEY (`id`),KEY `role_id` (`role_id`),CONSTRAINT `t_user_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8CREATE TABLE `t_permission` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`permissionname` varchar(50) NOT NULL COMMENT '权限名',`role_id` int(11) DEFAULT NULL COMMENT '外键关联role',PRIMARY KEY (`id`),KEY `role_id` (`role_id`),CONSTRAINT `t_permission_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

  每个表中我添加了一些测试数据,如下: 
user

role

permission

2. 自定义realm

  自定义realm中需要操作数据库,所有首先得先写一个dao,使用的是原始的jdbc,主要是下面的自定义realm。

public class UserDao {//根据用户名查找用户public User getByUsername(Connection conn, String username) throws Exception {User resultUser = null;String sql = "select * from t_user where username=?";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1, username);ResultSet rs = ps.executeQuery();if(rs.next()) {resultUser = new User();resultUser.setId(rs.getInt("id"));resultUser.setUsername(rs.getString("username"));resultUser.setPassword(rs.getString("password"));}return resultUser;}//根据用户名查找改用户所拥有的角色public Set<String> getRoles(Connection conn, String username) throws Exception {Set<String> roles = new HashSet<String>();String sql = "select * from t_user u, t_role r where u.role_id=r.id and u.username=?";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1, username);ResultSet rs = ps.executeQuery();while(rs.next()) {roles.add(rs.getString("rolename"));}return roles;}//根据用户名查找该用户角色所拥有的权限public Set<String> getPerms(Connection conn, String username) throws Exception {Set<String> perms = new HashSet<String>();String sql = "select * from t_user u, t_role r, t_permission p where u.role_id=r.id and p.role_id=r.id and u.username=?";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1, username);ResultSet rs = ps.executeQuery();while(rs.next()) {perms.add(rs.getString("permissionname"));}return perms;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

  有了dao了,接下来就可以写自定义的realm了,自定义realm需要继承AuthorizingRealm类,因为该类封装了很多方法,它也是一步步继承自Realm类的,继承了AuthorizingRealm类后,需要重写两个方法:

doGetAuthenticationInfo()方法:用来验证当前登录的用户,获取认证信息 
doGetAuthorizationInfo()方法:用来为当前登陆成功的用户授予权限和角色(已经登陆成功了)

  下面来看一下具体的实现:

public class MyRealm extends AuthorizingRealm {private UserDao userDao = new UserDao();// 为当前登陆成功的用户授予权限和角色,已经登陆成功了@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {String username = (String) principals.getPrimaryPrincipal(); //获取用户名SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();Connection conn = null;try {conn = DbUtil.getConnection();authorizationInfo.setRoles(userDao.getRoles(conn, username)); //设置角色authorizationInfo.setStringPermissions(userDao.getPerms(conn, username)); //设置权限            } catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {try {DbUtil.closeConnection(conn);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}return authorizationInfo;}// 验证当前登录的用户,获取认证信息@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String) token.getPrincipal(); // 获取用户名Connection conn = null;try {conn = DbUtil.getConnection();User user = userDao.getByUsername(conn, username); // 仅仅是根据用户名查出的用户信息,不涉及到密码if (user != null) {AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), "myrealm");return authcInfo;} else {return null;}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {try {DbUtil.closeConnection(conn);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}return null;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

  从上面两个方法中可以看出:验证身份的时候是根据用户输入的用户名先从数据库中查出该用户名对应的用户,这时候并没有涉及到密码,也就是说到这一步的时候,即使用户输入的密码不对,也是可以查出来该用户的,然后将该用户的正确信息封装到authcInfo 中返回给Shiro,接下来就是Shiro的事了,它会根据这里面的真实信息与用户前台输入的用户名和密码进行校验, 这个时候也要校验密码了,如果校验通过就让用户登录,否则跳转到指定页面。同理,权限验证的时候也是先根据用户名获取与该用户名有关的角色和权限,然后封装到authorizationInfo中返回给Shiro。

3. 修改ini文件

  在该配置文件中,[users]和[roles]的信息就可以删掉了,因为这些信息都是从数据库中维护的,另外还要在文件中指定我们自定义的realm的完全限定名,并且指定securityManager的realm使用我们自定义的realm,如下:

[main]
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized.jsp
perms.unauthorizedUrl=/unauthorized.jsp#定义自己的realm
myRealm=demo.shiro.realm.MyRealm
securityManager.realms=$myRealm#定义请求的地址需要做什么验证
[urls]
/login=anon 
/admin=authc 
/student=roles[teacher]
/teacher=perms["user:create"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

  这样我们自定义的realm就搞定了,根据配置文件,当我们请求…/admin的时候会进行身份认证,所以会进入LoginServlet中,当调用currentUser.login(token);的时候,就会进入我们自定义的realm中的doGetAuthenticationInfo方法进行身份初始化,然后交给Shiro去验证。当我们请求…./student的时候,也会先进行身份验证,就是上面的过程,然后验证通过,当我们再次请求…/student的时候,就会进入我们自定义的realm中的doGetAuthorizationInfo方法进行权限的初始化,然后交给Shiro去验证。 
  下一篇博文我将总结一下如何将Shiro运用到实际项目中,即如何将Shiro整合到spring中。

本文转自:csdn eson_15

这篇关于【Shiro】Apache Shiro架构之自定义realm的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1083419

相关文章

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java异常架构Exception(异常)详解

《Java异常架构Exception(异常)详解》:本文主要介绍Java异常架构Exception(异常),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. Exception 类的概述Exception的分类2. 受检异常(Checked Exception)

如何自定义Nginx JSON日志格式配置

《如何自定义NginxJSON日志格式配置》Nginx作为最流行的Web服务器之一,其灵活的日志配置能力允许我们根据需求定制日志格式,本文将详细介绍如何配置Nginx以JSON格式记录访问日志,这种... 目录前言为什么选择jsON格式日志?配置步骤详解1. 安装Nginx服务2. 自定义JSON日志格式各

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

基于Spring实现自定义错误信息返回详解

《基于Spring实现自定义错误信息返回详解》这篇文章主要为大家详细介绍了如何基于Spring实现自定义错误信息返回效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录背景目标实现产出背景Spring 提供了 @RestConChina编程trollerAdvice 用来实现 HTT

SpringSecurity 认证、注销、权限控制功能(注销、记住密码、自定义登入页)

《SpringSecurity认证、注销、权限控制功能(注销、记住密码、自定义登入页)》SpringSecurity是一个强大的Java框架,用于保护应用程序的安全性,它提供了一套全面的安全解决方案... 目录简介认识Spring Security“认证”(Authentication)“授权” (Auth

SpringBoot自定义注解如何解决公共字段填充问题

《SpringBoot自定义注解如何解决公共字段填充问题》本文介绍了在系统开发中,如何使用AOP切面编程实现公共字段自动填充的功能,从而简化代码,通过自定义注解和切面类,可以统一处理创建时间和修改时间... 目录1.1 问题分析1.2 实现思路1.3 代码开发1.3.1 步骤一1.3.2 步骤二1.3.3

dubbo3 filter(过滤器)如何自定义过滤器

《dubbo3filter(过滤器)如何自定义过滤器》dubbo3filter(过滤器)类似于javaweb中的filter和springmvc中的intercaptor,用于在请求发送前或到达前进... 目录dubbo3 filter(过滤器)简介dubbo 过滤器运行时机自定义 filter第一种 @A

使用Apache POI在Java中实现Excel单元格的合并

《使用ApachePOI在Java中实现Excel单元格的合并》在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用ApachePOI库在Java中实现Excel... 目录工具类介绍工具类代码调用示例依赖配置总结在日常工作中,Excel 是一个不可或缺的工http://