Spring Boot 整合 Shiro 实现认证和授权

Posted by 暮夏有五 on 2021-01-23
Estimated Reading Time 17 Minutes
Words 3.6k In Total
Viewed Times

在 Spring Boot 中做权限管理,一般来说,主流的方案是 Spring Security ,但是,仅仅从技术角度来说,也可以使用 Shiro。

一般来说,Spring SecurityShiro 的比较如下:

  1. Spring Security 是一个重量级的安全管理框架;Shiro 则是一个轻量级的安全管理框架
  2. Spring Security 概念复杂,配置繁琐;Shiro 概念简单、配置简单
  3. Spring Security 功能强大;Shiro 功能简单
  4. ……

虽然 Shiro 功能简单,但是也能满足大部分的业务场景。所以在传统的 SSM 项目中,一般来说,可以整合 Shiro

在 Spring Boot 中,由于 Spring Boot 官方提供了大量的非常方便的开箱即用的 Starter ,当然也提供了 Spring Security 的 Starter ,使得在 Spring Boot 中使用 Spring Security 变得更加容易,甚至只需要添加一个依赖就可以保护所有的接口,所以,如果是 Spring Boot 项目,一般选择 Spring Security 。这只是一个建议的组合,单纯从技术上来说,无论怎么组合,都是没有问题的。

在 Spring Boot 中整合 Shiro,有两种不同的方案:

  1. 第一种就是原封不动的,将 SSM 整合 Shiro 的配置用 Java 重写一遍。
  2. 第二种就是使用 Shiro 官方提供的一个 Starter 来配置,但是,这个 Starter 并没有简化多少配置。

准备工作


创建数据库

所需表如下:

  • user:用户表
  • role:角色表
  • perm:权限菜单表
  • user_role:用户与角色关联的中间表
  • role_prem:角色与权限菜单关联的中间表

执行数据库脚本

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
/*
Navicat Premium Data Transfer

Source Server : 127.0.0.1
Source Server Type : MySQL
Source Server Version : 50718
Source Host : 127.0.0.1:3306
Source Schema : shiro

Target Server Type : MySQL
Target Server Version : 50718
File Encoding : 65001
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for perm
-- ----------------------------
DROP TABLE IF EXISTS `perm`;
CREATE TABLE `perm` (
`perm_id` int(32) NOT NULL COMMENT '权限主键',
`perm_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '权限url',
`perm_description` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '权限描述',
PRIMARY KEY (`perm_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of perm
-- ----------------------------
INSERT INTO `perm` VALUES (1, '/user/*', '拥有对用户的所有操作权限');

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`role_id` int(32) NOT NULL COMMENT '角色主键',
`role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '角色名',
`role_description` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '角色描述',
PRIMARY KEY (`role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '超级管理员', '超级管理员');

-- ----------------------------
-- Table structure for role_perm
-- ----------------------------
DROP TABLE IF EXISTS `role_perm`;
CREATE TABLE `role_perm` (
`role_id` int(32) NOT NULL COMMENT '角色主键',
`perm_id` int(32) DEFAULT NULL COMMENT '权限主键'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role_perm
-- ----------------------------
INSERT INTO `role_perm` VALUES (1, 1);

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`user_id` int(32) NOT NULL COMMENT '用户主键',
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '用户名',
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '密码(存储加密后的密码)',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'root', 'e10adc3949ba59abbe56e057f20f883e');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`user_id` int(32) NOT NULL COMMENT '用户主键',
`role_id` int(32) NOT NULL COMMENT '角色主键'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1);

SET FOREIGN_KEY_CHECKS = 1;

数据库创建完成以后,用逆向工程生成对应的实体类和 mapper.xml 文件并加入到项目当中。

业务代码

这里我们需要定义一个业务接口查询用户的相关信息(包括用户关联的角色与权限)

UserService

1
2
3
4
5
6
7
8
9
public interface UserService {

/**
* 根据用户名查询用户信息(包含角色及权限信息)
* @param username 用户名
* @return User
*/
User selectByUsername(String username);
}

UserServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserMapper userMapper;

@Override
public User selectByUsername(String username) {

return userMapper.selectByUsername(username);
}
}

Web 页面

创建 login.html

引入 jquery.js

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
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<div>
用户名:<input id="username" name="username" type="text" /><br/>
密码:<input id="password" name="password" type="password"><br/>
<span id="tip" class="tip"></span><br/>
<button onclick="login()">点击登录</button>
</div>
</body>

<script type="text/javascript" src="/js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
function login() {
var username = $('#username').val()
var password = $('#password').val()
$.ajax({
url: '/login.do'
, data: {
username: username
, password: password
}
, type: 'post'
, dataType: 'json'
, success: function(res) {
if (res.code == 200) {
// 登录成功,跳转到 index.html
window.location.href = '/index.html'
} else {
// 登录失败,提示登录错误信息
$("#tip").text(res.msg)
}
}
, error: function() {
$("#tip").text('服务器响应失败')
}
})
}
</script>
</html>

创建 index.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
Hello Shiro
<a href="/logout.do">退出</a>
</body>
</html>

创建 unauthorized.html

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>无权访问</title>
</head>
<body>
权限不足
</body>
</html>

原生整合


引入依赖

加入 Shiro 相关的依赖,完整的 pom.xml 文件中的依赖如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>

创建 Realm

接下来我们来自定义核心组件 Realm

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
public class MyRealm extends AuthorizingRealm {

@Autowired
private UserService userService;

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
// 如果当前用户已完成认证则授权,注意:user 对象中包含了用户拥有的权限信息
if (user != null) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
List<String> roles = new LinkedList<>();
List<String> perms = new LinkedList<>();
// 添加角色
for (Role role : user.getRoleList()) {
roles.add(role.getRoleName());
}
// 添加菜单
for (Perm perm : user.getPermList()) {
perms.add(perm.getPermUrl());
}
simpleAuthorizationInfo.addRoles(roles);
simpleAuthorizationInfo.addStringPermissions(perms);
return simpleAuthorizationInfo;
}
// 否则不授权
return null;
}

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.selectByUsername(token.getUsername());
if (user == null) {
throw new UnknownAccountException();
}
return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
}
}

配置 Shiro

接下来进行 Shiro 的配置:

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class ShiroConfig {

/**
* 配置密码加密
*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {

HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
// 散列算法(加密)
credentialsMatcher.setHashAlgorithmName("MD5");
// 散列次数(加密次数)
credentialsMatcher.setHashIterations(1);
// storedCredentialsHexEncoded 默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}

/**
* 注入自定义的 Realm
*/
@Bean("MyRealm")
public MyRealm MyRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {

MyRealm MyRealm = new MyRealm();
MyRealm.setCredentialsMatcher(matcher);
return MyRealm;
}

/**
* 配置自定义权限过滤规则
*/
@Bean
public ShiroFilterFactoryBean shirFilter(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {

ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
bean.setSuccessUrl("/index.html");
bean.setLoginUrl("/login.html");
bean.setUnauthorizedUrl("/unauthorized.html");

/**
* anon:匿名用户可访问
* authc:认证用户可访问
* user:使用rememberMe可访问
* perms:对应权限可访问
* role:对应角色权限可访问
**/
Map<String, String> filterMap = new LinkedHashMap<>();
/**
* 允许匿名访问静态资源
*/
filterMap.put("/image/**", "anon");
filterMap.put("/css/**", "anon");
filterMap.put("/js/**", "anon");
filterMap.put("/plugin/**", "anon");
/**
* 允许匿名访问登录页面和登录操作
*/
filterMap.put("/login.html", "anon");
filterMap.put("/login.do", "anon");
/**
* 其它所有请求需要登录认证后才能访问
*/
filterMap.put("/**", "authc");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}

/**
* 注入 securityManager
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(HashedCredentialsMatcher hashedCredentialsMatcher, @Qualifier("sessionManager") DefaultWebSessionManager defaultWebSessionManager) {

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(MyRealm(hashedCredentialsMatcher));
securityManager.setSessionManager(defaultWebSessionManager);
return securityManager;
}

/**
* 开启权限注解
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {

AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {

DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}

/**
* 配置异常跳转页面
*/
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {

SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
// 未认证跳转页面(跳转路径为项目里的页面相对路径,并非 URL)
properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "login");
// 权限不足跳转页面
properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "unauthorized");
resolver.setExceptionMappings(properties);
return resolver;
}

/**
* 会话管理器
*/
@Bean("sessionManager")
public DefaultWebSessionManager defaultWebSessionManager() {

DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
// 设置用户登录信息失效时间为一天(单位:ms)
defaultWebSessionManager.setGlobalSessionTimeout(1000L * 60L * 60L * 24L);
return defaultWebSessionManager;
}

/**
* 重置 ShiroDialect,省略此步将不能在 Thymeleaf 页面使用 Shiro 标签
*/
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}

在这里进行 Shiro 的配置主要配置 3 个 Bean :

  1. 首先需要提供一个 Realm 的实例。
  2. 需要配置一个 SecurityManager,在 SecurityManager 中配置 Realm。
  3. 配置一个 ShiroFilterFactoryBean,在 ShiroFilterFactoryBean 中指定路径拦截规则等。
  4. 配置登录和测试接口。

其中,ShiroFilterFactoryBean 的配置稍微多一些,配置含义如下:

  • setSecurityManager 表示指定 SecurityManager
  • setLoginUrl 表示指定登录页面。
  • setSuccessUrl 表示指定登录成功页面。
  • 接下来的 Map 中配置了路径拦截规则,注意,要有序。

这些东西都配置完成后,接下来配置登录 Controller

创建 Controller

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
@Controller
public class IndexController {

@Autowired
private UserService userService;

@RequestMapping(value = "login.html")
public String loginView() {

// 判断当前用户是否通过认证
if (SecurityUtils.getSubject().isAuthenticated()) {
// 认证通过,重定向到首页
return "redirect:index.html";
} else {
// 未认证或认证失败,转发到登录页
return "login";
}
}

@RequestMapping(value = "login.do")
@ResponseBody
public AppReturn loginDo(@RequestParam String username, @RequestParam String password) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
try {
// 执行认证
subject.login(usernamePasswordToken);
} catch (UnknownAccountException e) {
return AppReturn.defeated("账号不存在");
} catch (IncorrectCredentialsException e) {
return AppReturn.defeated("密码错误");
}
return AppReturn.succeed("登录成功");
}

@RequestMapping(value = "index.html")
public String indexView() {
return "index";
}

@RequestMapping(value = "logout.do")
public String logoutDo() {

if (SecurityUtils.getSubject().isAuthenticated()) {
// 退出
SecurityUtils.getSubject().logout();
}
return "redirect:login.html";
}

@RequestMapping(value = "unauthorized.html")
public String unauthorizedView() {

return "unauthorized";
}
}
@Controller
public class IndexController {

@Autowired
private UserService userService;

@RequestMapping(value = "login.html")
public String loginView() {

// 判断当前用户是否通过认证
if (SecurityUtils.getSubject().isAuthenticated()) {
// 认证通过,重定向到首页
return "redirect:index.html";
} else {
// 未认证或认证失败,转发到登录页
return "login";
}
}

@RequestMapping(value = "login.do")
@ResponseBody
public AppReturn loginDo(@RequestParam String username, @RequestParam String password) {
return userService.loginDo(username, password);
}

@RequestMapping(value = "index.html")
public String indexView() {
return "index";
}

@RequestMapping(value = "logout.do")
public String logoutDo() {

if (SecurityUtils.getSubject().isAuthenticated()) {
// 退出
SecurityUtils.getSubject().logout();
}
return "redirect:login.html";
}

@RequestMapping(value = "unauthorized.html")
public String unauthorizedView() {

return "unauthorized";
}
}

最后访问 http://localhost:8080/login.html 进行登录即可。账号:root,密码:123456

整合 Shiro Starter


上面这种配置方式实际上相当于把 SSM 中的 XML 配置拿到 Spring Boot 中用 Java 代码重新写了一遍,除了这种方式之外,我们也可以直接使用 Shiro 官方提供的 Starter 。

引入依赖

添加 shiro-spring-boot-web-starter 依赖,这个依赖可以代替之前的 shiro-webshiro-spring 两个依赖,如下:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>

创建 Realm

这里的 Realm 和前面的一样,我就不再赘述。

配置 Shiro 基本信息

接下来在 application.properties 中配置 Shiro 的基本信息:

1
2
3
4
5
6
shiro.sessionManager.sessionIdCookieEnabled=true
shiro.sessionManager.sessionIdUrlRewritingEnabled=true
shiro.unauthorizedUrl=/unauthorizedurl
shiro.web.enabled=true
shiro.successUrl=/index
shiro.loginUrl=/login

配置解释:

  1. 第一行表示是否允许将sessionId 放到 cookie 中
  2. 第二行表示是否允许将 sessionId 放到 Url 地址拦中
  3. 第三行表示访问未获授权的页面时,默认的跳转路径
  4. 第四行表示开启 shiro
  5. 第五行表示登录成功的跳转页面
  6. 第六行表示登录页面

配置 ShiroConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
public class ShiroConfig {

@Bean
MyRealm myRealm() {
return new MyRealm();
}

@Bean
DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm());
return manager;
}

@Bean
ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
definition.addPathDefinition("/doLogin", "anon");
definition.addPathDefinition("/**", "authc");
return definition;
}
}

这里的配置和前面的比较像,但是不再需要 ShiroFilterFactoryBean 实例了,替代它的是 ShiroFilterChainDefinition,在这里定义 Shiro 的路径匹配规则即可。这里定义完之后,接下来的登录接口定义以及测试方法都和前面的一致,我就不再赘述了,大家可以参考上文。

Java 中使用 Shiro 权限注解


除了在 ShiroConfig 配置类中自定义权限过滤规则,还可以使用 Shiro 提供的注解实现权限过滤,在 Controller 中的每个请求方法上可以添加以下注解实现权限控制:

  • @RequiresAuthentication: 只有认证通过的用户才能访问

  • @RequiresRoles(value = {“root”}, logical = Logical.OR):

    • value:指定拥有 root 角色才能访问,角色可以是多个,以逗号隔开
    • logical:该属性有两个值,Logical.OR(只要拥有其中一个角色就能访问),Logical.AND(需要拥有指定的全部角色才能访问,否则会抛出权限不足异常)
  • @RequiresPermissions(value = {“/user/delete”}, logical = Logical.OR)

    • value:指定拥有 /user/delete 权限才能访问,权限可以是多个,以逗号隔开
    • logical:有两个值,Logical.OR(只要拥有其中一个权限就访问),Logical.AND(需要拥有指定的全部权限才能访问,否则会抛出权限不足异常)

Thymeleaf 模板中使用 Shiro 权限标签


修改 thymeleaf 模板的 html 标签,加入 xmlns:shiro=”http://www.pollix.at/thymeleaf/shiro 命名空间:

1
2
3
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

常用的 Shiro 标签有以下:

  • <shiro:hasRole=”root”>:需要拥有 root 角色
  • <shiro:hasAnyRoles=”root,guest”>:需要拥有 root 和 guest 中的任意一个角色
  • <shiro:hasAllRoles=”root,guest”>:需要同时拥有 root 和 guest 角色
  • <shiro:hasPerm="userAdd>":需要拥有 userAdd 权限
  • <shiro:hasAnyPerms="userAdd,userDelete>":需要拥有 userAdd 和 userDelete 中的任意一个权限
  • <shiro:hasAllPerms="userAdd,userDelete>":需要同时拥有 userAdd 和 userDelete 权限

更多干货请移步:https://antoniopeng.com


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !