springboot集成shiro
Yu9

0x01 shiro环境搭建

以下是在Spring Boot中整合Shiro的基本应用示例,帮助我们掌握Shiro的基本使用

  • springboot:2.2.1.RELEASE
  • java:1.8.0_144
  • mysql:5.7.21

01、创建springboot项目

1)创建旧版本的 Spring Boot 项目。

关于创建任意 Spring Boot 和 Java 版本的 Spring Boot 项目,以 Maven 项目为例,只需要在创建好的springboot项目修改 pom.xml 中的 2 个配置元素即可:

  • <version>2.2.1.RELEASE</version> 指定 Spring Boot 的版本号
  • <java.version>1.8</java.version> 指定 Java 的版本号

image-20240206034528767

2)创建springboot项目这个比较简单、就不多说了。然后导入thymeleaf模版依赖。

1
2
3
4
5
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2)新建一个首页, 在src\main\resources\templates路径下

我们要使用thymeleaf,需要在html文件中导入命名空间的约束,方便提示。

xmlns:th="http://www.thymeleaf.org"

image-20240206040244771

3)为首页编写一个Controller

image-20240206040125938

4)启动,测试。访问到如下页面就是成功了!

image-20240206040429833

02、导入基本的依赖

1
2
3
4
5
6
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>

03、编写shiro配置文件骨架

先创建好基本骨架、再添加内容!

shiro三大核心对象,咱们从后往前依次创建

  • ShiroFilterFactoryBean
  • DefaultWebSecurityManager
  • 自定义Realm对象

image-20240206053013955

1)Realm 可以理解为 Shiro 与后端数据源进行交互的桥梁

  • 负责从数据源(例如数据库、LDAP、文件系统等)中获取用户身份信息和权限信息,然后将这些信息提供给 Shiro 进行认证和授权操作。
  • 自定义 Realm 类需要继承 AuthorizingRealm 类,并实现其中的 doGetAuthenticationInfodoGetAuthorizationInfo 方法来完成认证和授权逻辑。
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
package com.example.shiro.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;

@Component
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了 -> 授权 doGetAuthorizationInfo");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了 -> 认证 doGetAuthenticationInfo");
return null;
}
}

2)DefaultWebSecurityManager是 Apache Shiro 框架中的一个关键组件,它实现了 SecurityManager 接口,并且特别适用于 Web 应用程序的安全管理。

DefaultWebSecurityManager 负责协调 Shiro 的各个组件,包括 Realm、SessionManager、CacheManager 等,以提供全面的安全功能。

1
2
3
4
5
6
7
8
9
10
11
12
/**
*安全管理器
* @return
*/
@Bean("securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(){
//创建了一个 DefaultWebSecurityManager 对象 securityManager
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联userRealm
securityManager.setRealm(userRealm);
return securityManager;
}

3)ShiroFilterFactoryBean

整合 Shiro 到 Spring 应用程序中时,通常会使用 ShiroFilterFactoryBean 来配置 Shiro 的过滤器链,以定义 URL 拦截的规则和权限控制策略。

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 自定义角色过滤器
* @return
*/
@Bean("shirFilter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
//创建了一个 ShiroFilterFactoryBean 对象
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}

完整配置文件:

image-20240206062410520

04、实现登录拦截

1)需求:login页都可访问、index也需登录才能访问

首先新建登录页,并编写接口

controller

1
2
3
4
@GetMapping("/login")
public String toLogin(){
return "login";
}

login.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>Login Page</title>
</head>
<body class="text-center">
<form th:action="@{/login}" method="post">
Username: <input type="text" placeholder="Username" th:name="username" required="required"><br>
Password: <input type="password" placeholder="Password" th:name="password" required="required"><br>
<button type="submit" name="submit" value="Login">Sign in</button>
</form>
</body>
</html>

2)实现

通过在shiro配置文件中配置ShiroFilterFactoryBean实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Bean("shirFilter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
//创建了一个 ShiroFilterFactoryBean 对象
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//未经身份验证时,将重定向到 /login 这个URL,如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
//添加shiro内置过滤器
/*
anon 无需认证可访问
authc 必须认证才能访问
user 需有rememberMe功能才可使用
permes 有对应权限可访问
role 有对应角色才可访问
*/
LinkedHashMap<String,String> fc = new LinkedHashMap<>();
fc.put("/**", "authc");
fc.put("/login", "anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(fc);
return shiroFilterFactoryBean;
}

3)测试:访问首页,直接跳转到登录页!

05、实现用户认证

用户认证是通过实现 Shiro 的 Realm 接口来完成的。在 Realm 实现类中,需要重写 doGetAuthenticationInfo 方法,该方法用于验证用户的身份凭证(例如用户名和密码)是否正确。在验证成功后,可以返回一个 AuthenticationInfo 对象,表示用户经过身份验证。

1)首先第一步还是先写一个处理登录的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@PostMapping("/login")
public String login(String username,String password,Model model){
//获取当前用户的 Subject 对象,该对象用于执行身份验证和授权操作。
Subject subject = SecurityUtils.getSubject();
//封装用户登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//执行登录,如果没有异常就说明ok
try {
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
model.addAttribute("msg","用户名错误");
return "login";
} catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "login";
}
}

2)登录页添加返回信息

1
<p th:text="${msg}" style="color:red"></p>

image-20240206075803549

3)处理登录信息调用的是controller,但实际的核心是在自定义的Realm

给证明一下,这里先把之前咱们自定义的Realm拿来回忆一下,貌似也啥都没写!

image-20240206080342566

4)随便登录一个用户,看看控制台

image-20240206080511944

image-20240206080940111

由此证具体的用户认证是通过实现 Shiro 的 Realm 接口来完成的

5)具体如何实现入户认证

一般都是从数据库查询然后与在controller接口处封装的数据对比完成!

现在为了方便演示,我们先用写死的用户信息!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了 -> 认证 doGetAuthenticationInfo");
//一般从数据库中获取
String username="admin";
String password="123456";

//获取到的用户数据与controller层封装的用户数据进行比较
UsernamePasswordToken userToken= (UsernamePasswordToken) token;
if(!userToken.getUsername().equals(username)){
return null;
}

//密码认证,shiro完成
return new SimpleAuthenticationInfo("",password,"");
}

6)测试,密码正确就可以成功完成认证

image-20240206223142678

06、整合mybaits

1)导入依赖

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
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>

<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>

<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>

2)mybatis配置文件

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
spring:
datasource:
url: jdbc:mysql://localhost:3306/shiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
username: shiro
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
#初始化大小
initialSize: 5
#最小值
minIdle: 5
#最大值
maxActive: 20
#最大等待时间,配置获取连接等待超时,时间单位都是毫秒ms
maxWait: 60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接
timeBetweenEvictionRunsMillis: 60000
#配置一个连接在池中最小生存的时间
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,
#'wall'用于防火墙,SpringBoot中没有log4j,我改成了log4j2
filters: stat,wall,log4j
#最大PSCache连接
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

# Mybatis配置
mybatis:
# 配置XML映射文件中指定的实体类别名路径
type-aliases-package: com.example.shiro.pojo
# 配置MyBatis的xml配置文件路径
mapper-locations: classpath:mapper/*.xml

3)idea连接数据库

image-20240206233123211

导入sql文件

1
2
3
4
5
6
7
8
9
10
11
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`rid` int(11) NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `users` VALUES (1, 'admin', '123456', 1);
INSERT INTO `users` VALUES (2, 'test', '123456', 0);

4)实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.shiro.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private Integer rid;
}

5)编写Mapper接口

image-20240207002015389

image-20240207002030453

6)测试一下

image-20240207002147632

7)规范一下,我们把Service层也写上

image-20240207002820092

image-20240207002835031

8)用户认证功能,完善从数据库获取用户信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了 -> 认证 doGetAuthenticationInfo");
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//数据库获取用户信息
User user = userService.getByUserName(userToken.getUsername());

if (null == user) {
return null;
} else {
String password = user.getPassword();
//密码认证,shiro完成
return new SimpleAuthenticationInfo(token.getPrincipal(), password, getName());
}
}

现在就实现了用户信息从数据库获取

07、实现rememberMe功能

Apache Shiro 提供了 “Remember Me”(记住我)功能,可以使用户在关闭浏览器后再次访问应用程序时无需重新登录。

当用户选择 “Remember Me” 选项并成功登录时,Shiro 将生成一个记住登录状态的令牌,并将其存储在用户浏览器的 Cookie 中。下次用户再次访问应用程序时,Shiro 将从 Cookie 中读取令牌,并使用该令牌自动进行身份验证,而无需用户再次提供用户名和密码。

1)修改配置类,添加一下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//cookie 属性设置
public SimpleCookie rememberMeCookie() {
SimpleCookie cookie = new SimpleCookie("ShiroTest_RememberMe");
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setMaxAge(30 * 24 * 60 * 60);
return cookie;
}

//创建 Shiro 的 cookie 管理对象
public CookieRememberMeManager rememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new
CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
return cookieRememberMeManager;
}

2)在安全管理器中设置rememberMe

1
securityManager.setRememberMeManager(rememberMeManager());

image-20240207022803747

3)配置shiro内置过滤器

1
fc.put("/**", "user");

image-20240207024200622

4)修改controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@PostMapping("/login")
public String login(String username,String password,Model model,@RequestParam(defaultValue =
"false") boolean rememberMe, HttpSession session){
//获取当前用户的 Subject 对象,该对象用于执行身份验证和授权操作。
Subject subject = SecurityUtils.getSubject();
//封装用户登录数据
AuthenticationToken token = new UsernamePasswordToken(username, password, rememberMe);
//执行登录,如果没有异常就说明ok
try {
subject.login(token);
return "redirect:index";
} catch (UnknownAccountException e) {
model.addAttribute("msg","用户名错误");
return "login";
} catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "login";
}
}

5)修改登录页面

1
2
3
4
5
6
<form th:action="@{/login}" method="post">
Username: <input type="text" placeholder="Username" th:name="username" required="required"><br>
Password: <input type="password" placeholder="Password" th:name="password" required="required"><br>
记住我:<input type="checkbox" name="rememberMe" value="true"></div><br>
<button type="submit" name="submit" value="Login">Sign in</button>
</form>

6)退出浏览器再次访问,状态还在!

08、退出功能

1)添加退出按钮

1
2
3
4
5
6
7
8
9
<body>

<h1>首页</h1>

<p th:text="${msg}"></p>

<a href="/logout">登出</a>

</body>

2)配置类中添加logout过滤器

1
fc.put("/logout","logout");

image-20240207035231353

09、授权之角色认证

在 Apache Shiro 中,授权是在 Realm 中完成的。通过在自定义的 Realm 类中实现 doGetAuthorizationInfo 方法来完成授权操作。该方法会在授权过程中被 Shiro 调用,并返回一个 AuthorizationInfo 对象,其中包含了用户的角色、权限等相关信息。

1)编写一个接口用于测试是否拥有角色

1
2
3
4
5
6
@GetMapping("/testAdmin")
@ResponseBody
public String testAdmin(){
return "您是管理员";
}

2)在shiro过滤器中设置,这个接口下的页面之后admin角色才可以访问

1
fc.put("/testAdmin", "roles[admin]");

image-20240208010242903

3)现在登录后我们访问这个页面,可以看到报401未授权、并且控制台可以看到条用了我们自定义的Realm中的doGetAuthorizationInfo方法

image-20240208010331576

image-20240208010401011

4)我们在自定义的Realm中的doGetAuthorizationInfo方法中给当前用户添加角色

1
2
3
4
5
6
7
8
9
10
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了 -> 授权 doGetAuthorizationInfo");
//1 创建对象,存储当前登录的用户的权限和角色
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//2 存储角色
info.addRole("admin");
return info;
}

5)再次访问就可以访问到了

image-20240208011115697

6)自定义401错误页面

1
shiroFilterFactoryBean.setUnauthorizedUrl("/401");

image-20240208011638613

1
2
3
4
5
@GetMapping("/401")
@ResponseBody
public String Unauthorized(){
return "你没有权限访问!";
}

10、授权之权限认证

权限认证和角色认证实现方法类似,就不过多赘述!接下来就直接整合规范一下授权这块

1)创建角色表、权限表、以及角色-权限表

image-20240208013637299

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
Navicat Premium Data Transfer

Source Server : shiro
Source Server Type : MySQL
Source Server Version : 50726
Source Host : localhost:3306
Source Schema : shiro

Target Server Type : MySQL
Target Server Version : 50726
File Encoding : 65001

Date: 08/02/2024 02:07:24
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for perm
-- ----------------------------
DROP TABLE IF EXISTS `perm`;
CREATE TABLE `perm` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`permission` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`ps-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of perm
-- ----------------------------
INSERT INTO `perm` VALUES (1, 'user:add', '增', 1);
INSERT INTO `perm` VALUES (2, 'user:update', '改', 2);
INSERT INTO `perm` VALUES (3, 'user:select', '查', 3);
INSERT INTO `perm` VALUES (4, 'user:delete', '删', 4);

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`roleName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`rid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'admin', '管理员', 1);
INSERT INTO `role` VALUES (2, 'user', '普通用户', 0);

-- ----------------------------
-- Table structure for role_perm
-- ----------------------------
DROP TABLE IF EXISTS `role_perm`;
CREATE TABLE `role_perm` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rid` int(11) NULL DEFAULT NULL,
`pid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Fixed;

-- ----------------------------
-- Records of role_perm
-- ----------------------------
INSERT INTO `role_perm` VALUES (1, 0, 1);
INSERT INTO `role_perm` VALUES (2, 0, 2);
INSERT INTO `role_perm` VALUES (3, 0, 3);
INSERT INTO `role_perm` VALUES (4, 1, 1);
INSERT INTO `role_perm` VALUES (5, 1, 2);
INSERT INTO `role_perm` VALUES (6, 1, 3);
INSERT INTO `role_perm` VALUES (7, 1, 4);

-- ----------------------------
-- Table structure for role_user
-- ----------------------------
DROP TABLE IF EXISTS `role_user`;
CREATE TABLE `role_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) NULL DEFAULT NULL,
`rid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Fixed;

-- ----------------------------
-- Records of role_user
-- ----------------------------
INSERT INTO `role_user` VALUES (1, 1, 1);
INSERT INTO `role_user` VALUES (2, 2, 0);

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`rid` int(11) NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES (1, 'admin', '123456', 1);
INSERT INTO `users` VALUES (2, 'test', '123456', 0);

SET FOREIGN_KEY_CHECKS = 1;

2)编写查询角色和权限的mapper

mapper接口

1
2
3
public String getUserRole(int id);

public List<String> getRolePerms(int id);

实现

1
2
3
4
5
6
7
8
9
10
11
<select id="getUserRole" resultType="java.lang.String">
select role from role where rid = (select rid from role_user where uid = #{id})
</select>
<select id="getRolePerms" resultType="java.lang.String">
SELECT DISTINCT p.permission
FROM users u
JOIN role_user ru ON u.id = ru.uid
JOIN role_perm rp ON ru.rid = rp.rid
JOIN perm p ON rp.pid = p.pid
WHERE u.id = #{id};
</select>

3)编写其对应的service

接口

1
2
3
public String getUserRole(int id);
public List<String> getRolePerms(int id);

实现

1
2
3
4
5
6
7
8
9
10
11
@Override
public String getUserRole(int id) {
String role=userMapper.getUserRole(id);
return role;
}

@Override
public List<String> getRolePerms(int id) {
List<String> perms = userMapper.getRolePerms(id);
return perms;
}

4)修改自定义的Realm中doGetAuthorizationInfo方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了 -> 授权 doGetAuthorizationInfo");
//1 创建对象,存储当前登录的用户的权限和角色
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//2 获取用户id
User p = (User) principals.getPrimaryPrincipal();
Integer uid = p.getId();
//3 查询角色
String userRole = userService.getUserRole(uid);
//4 查询权限
List<String> rolePerms = userService.getRolePerms(uid);
//5 存储角色
info.addRole(userRole);
//6 存储权限
info.addStringPermissions(rolePerms);
return info;
}

5)添加几个页面用来测试权限认证

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
@GetMapping("/admin")
@ResponseBody
public String testAdmin(){
return "您是管理员";
}

@GetMapping("/add")
@ResponseBody
public String add(){
return "add";
}

@GetMapping("/update")
@ResponseBody
public String update(){
return "update";
}

@GetMapping("/select")
@ResponseBody
public String select(){
return "select";
}

@GetMapping("/delete")
@ResponseBody
public String delete(){
return "delete";
}

6)shiro过滤器对这几个页面添加拦截

image-20240208025635561

7)测试

0x02 参考

start.spring.io 创建旧版本的 Spring Boot 项目 - spring 中文网 (springdoc.cn)

https://www.w3cschool.cn/shiro/co4m1if2.html

SpringBoot整合Shiro环境搭建_哔哩哔哩_bilibili

https://blog.csdn.net/weixin_60223449/article/details/127143064

https://github.com/phith0n/JavaThings/tree/master/shirodemo

由 Hexo 驱动 & 主题 Keep
总字数 50.7k 访客数 访问量