文章目录
网站安全
Spring Security
概念
环境搭建
用户认证和授权
定义规则,编写基础配置类
内容解析
Security的授权和认证的内容
thymeleaf 的 SpringSecurity 的整合
登出注销流程
内容显示
测试项目
设置跳转自己的登录页面和记住账号密码功能
保存cookie账号
跳转到自己的登录页
Shiro
简介
快速开始
shiro-springBoot-web
新建一个shiro项目
Shiro导致项目出错
查错,寻找解决方案
手动注册Filter解决?,禁用自动配置,审慎使用AI
继续排查
前提
对比项目差别点,检查自身错误
检查错误信息
测试Shiro
编写认证授权的Relam
控制器
跳转的页面编写
测试
编写登录拦截
实现用户认证
根据`quickstart`,编写认证操作
关联数据库实现用户认证
设置加密
实现用户授权
准备工作
关联数据库实现用户授权
整合前端代码
准备工作
实现代码
网站安全
在Web开发过程中,安全是一个需要认真考虑的点。
一般在系统设计初期就要考虑进来,
如果应用的基本架构已经确定,要修复安全漏洞,可能需要对系统的架构做出比较重大的调整,因而需要更多的开发时间,影响应用的发布进程。
一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。
用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。
用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
Spring提供了一个功能强大的高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准,也就是Spring Security。
Spring Security
中文网站:https://springdoc.cn/spring-boot/web.html#web.security
概念
Spring Security是一个框架,侧重于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正强大之处在于它可以轻松地扩展以满足定制需求
Spring Security 框架对于Web安全都有很好的支持。
在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等
使用用户名、密码,有时与身份验证因素结合使用
在用户授权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。
在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。
需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理
重要的几个类:
WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略
AuthenticationManagerBuilder:自定义认证策略
环境搭建
创建项目![图片[1] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/5b1c3eace70f44ca829e6a327c53873b.png)
检查pom的配置,确保引入Spring Secutrity
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
导入静态资源

编写控制器跳转
package com.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class RouterController {
@RequestMapping(value = {
"/","/index"})
public String index() {
return "index";
}
@RequestMapping(value = "/toLogin")
public String toLogin() {
return "views/login";
}
@RequestMapping("/level1/{id}")
public String toLevel1(@PathVariable Integer id) {
return "views/level1/"+ id;
}
@RequestMapping("/level2/{id}")
public String toLevel2(@PathVariable Integer id) {
return "views/level2/"+ id;
}
@RequestMapping("/level3/{id}")
public String toLevel3(@PathVariable Integer id) {
return "views/level3/"+ id;
}
}
尝试启动,拦截器拦截到login页面,环境搭建完成


用户认证和授权
定义规则,编写基础配置类
Spring Security 6.0 开始(对应 Spring Boot 3.0+),
WebSecurityConfigurerAdapter这个类已经被彻底移除,不再存在。
旧版的功能写法
package com.kuang.config;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity // 开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 定制请求的授权规则
// 首页所有人可以访问
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
// 开启自动配置的登录功能
// /login 请求来到登录页
// /login?error 重定向到这里表示登录失败
http.formLogin();
}
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中定义,也可以在jdbc中去拿....
//Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
//要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
//spring security 官方推荐的是使用bcrypt加密方式。
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
}
}
新版功能写法
@EnableWebSecurity
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(request -> request
.requestMatchers("/").permitAll() // 允许所有人访问首页
// .requestMatchers("/login").permitAll() // 允许所有人访问首页
.requestMatchers("/level1/**").hasRole("vip1") // 允许vip1访问level1
.requestMatchers("/level2/**").hasRole("vip2") // 允许vip2访问level2
.requestMatchers("/level3/**").hasRole("vip3") // 允许vip3访问level3
.anyRequest().authenticated() // 其他请求需要登录
).formLogin(AbstractAuthenticationFilterConfigurer::permitAll
).logout(LogoutConfigurer::permitAll
);
return http.build();
}
/**
* 配置用户详情服务Bean
* 该方法创建并配置一个基于内存的用户详情管理器,包含三个预定义用户:
* 1. admin用户:拥有所有角色权限
* 2. manager用户:拥有vip2和vip3角色权限
* 3. employee用户:拥有vip3角色权限
* 所有用户的初始密码都为"123456",使用委托密码编码器进行加密存储
*
* @return UserDetailsService 用户详情服务实例
*/
@Bean
public UserDetailsService userDetailsService() {
// 创建委托密码编码器,支持多种密码编码方式
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
// 构建admin用户,拥有最高权限,包含所有角色
UserDetails admin = User.builder()
.username("admin")
.password(encoder.encode("123456"))
.roles("vip1","vip2","vip3")
.build();
// 构建manager用户,拥有中等级别权限
UserDetails manager = User.builder()
.username("manager")
.password(encoder.encode("123456"))
.roles("vip2","vip3")
.build();
// 构建普通employee用户,拥有基础权限
UserDetails employee = User.builder()
.username("user")
.password(encoder.encode("123456"))
.roles("vip3")
.build();
// 返回基于内存的用户详情管理器,包含所有预定义用户
return new InMemoryUserDetailsManager(admin, manager, employee);
}
}
内容解析:
登录:.formLogin(AbstractAuthenticationFilterConfigurer::permitAll等同于.formLogin(form -> form .permitAll() ) 表示 登录页面和处理URL允许所有人访问,默认跳转到Spring Security的Login页面;![图片[2] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/ff9fe6767e2a4f4ba13f143d6f72fdf3.png)
注销:LogoutConfigurer::permitAll同上所述,说的是logout的方法;
PasswordEncoder 作为密码编码器,将密码进行编码后存放到UserDetails的password里面;
InMemoryUserDetailsManager(admin, manager, employee)基于memory内存认证;
内容解析
Security的授权和认证的内容
Authentication(授权):Authentication :: Spring Security
Authentication(认证):
thymeleaf 的 SpringSecurity 的整合
导入整合依赖启动器
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
导入命名空间: xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
<!DOCTYPE html>
<html lang="en" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
xmlns:th="http://www.thymeleaf.org"> <!-- 主要是这行代码 官方建议使用 -->
<!--上述操作是加注释-->
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<title>首页</title>
<!--semantic-ui-->
<link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
<link rel="stylesheet" th:href="@{/qinjiang/css/qinstyle.css}">
</head>
<body>
<!--主容器-->
<div class="ui container">
<div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
<div class="ui secondary menu">
<a class="item" th:href="@{/index}">首页</a>
<!--登录注销-->
<div class="right menu">
<!--如果未登录-->
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/toLogin}">
<i class="address card icon"></i> 登录
</a>
</div>
<!--如果登录了 , 显示用户名与注销按钮-->
<div sec:authorize="isAuthenticated()">
<a class="item">
用户名: <span sec:authentication="name"></span>
角色: <span sec:authentication="principal.authorities"></span>
</a>
</div>
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a>
</div>
<!--已登录
<a th:href="@{/usr/toUserCenter}">
<i class="address card icon"></i> admin
</a>
-->
</div>
</div>
</div>
<div class="ui segment">
<h3>Spring Security Study by 秦疆</h3>
</div>
<div>
<br>
<div class="ui three column stackable grid">
<!--菜单 , 根据用户的角色, 动态的实现-->
<div class="column" sec:authorize="hasRole('vip1')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 1</h5>
<hr>
<div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
<div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
<div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip2')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 2</h5>
<hr>
<div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
<div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
<div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip3')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 3</h5>
<hr>
<div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
<div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
<div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/qinjiang/js/semantic.min.js}"></script>
</body>
</html>
登出注销流程
前端设置跳转的连接:
<a class="item" th:href="@{/logout}">
<i class="address card icon"></i> 注销
</a>
在Security里面开启自动注销的配置
Security5
//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
//....
//开启自动配置的注销的功能
// /logout 注销请求
http.logout();
}
// .logoutSuccessUrl("/"); 注销成功来到首页
http.logout().logoutSuccessUrl("/");
Security6
@EnableWebSecurity
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(request -> request
.logout(LogoutConfigurer::permitAll);
return http.build();
}
}
内容显示
根据角色判断:<div class="column" sec:authorize="hasRole('vip2')">
根据权限判断:登录:<div sec:authorize="isAuthenticated()">、未登录:<div sec:authorize="!isAuthenticated()">
如果注销404了,就是因为它默认防止csrf跨站请求伪造,因为会产生安全问题;
我们可以将请求改为post表单提交;
或者在spring security中关闭csrf功能;在 配置中增加 http.csrf().disable();。
测试项目
admin![图片[3] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/c680af0f47a7404c86064776d908e397.png)
manager ![图片[4] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/2d913372a1674758a824e112711eb3ec.png)
user 
设置跳转自己的登录页面和记住账号密码功能
保存cookie账号
//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
//记住我
http.rememberMe();
}
http.authorizeHttpRequests.rememberMe(config -> config
.rememberMeCookieName("remember-me") );

跳转到自己的登录页
// AbstractAuthenticationFilterConfigurer::permitAll // 登录页面和处理URL允许所有人
http.authorizeHttpRequests.formLogin(
login -> login.loginPage("/toLogin").permitAll()

退出注销后,跳转登录页面,写一个controller
@RequestMapping(value = "/logout")
public String toLogout() {
return "views/login";
}
默认到根目录请求

效果

Shiro
简介
学习文档:Shiro
官网:https://shiro.apache.org/
快速开始
shiro/samples/quickstart/pom.xml at main · apache/shiro
导入依赖
samples/quickstart/pom.xml
配置文件
samples/quickstart/src/main/resources/log4j2.xml
Helloword
samples/quickstart/src/main/java/Quickstart.java
启动成功![图片[5] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/9cd3193634dd4991bfdc3488f71ccfd3.png)
分析快速启动的项目,得到Subject的大部分内容
// 获取当前用户 Subject
Subject currentUser = SecurityUtils.getSubject();
// 通过用户获取session
Session session = currentUser.getSession();
// 令牌,判断当前用户是否已经认证
if (!currentUser.isAuthenticated()) {
}
try {
// 登录操作
currentUser.login(token);
} catch (UnknownAccountException uae) {
// 不存在的账户信息
} catch (IncorrectCredentialsException ice) {
// 账号密码错误
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
// 账户被锁
} catch (AuthenticationException ae) {
// 认证失败
}
if (currentUser.hasRole("schwartz")) {
}// 角色判断
// 非实例级别 粗粒度权限
if (currentUser.isPermitted("lightsaber:wield")) {
}
// 实例级权限 细粒度权限
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
}
currentUser.logout(); // 注销
System.exit(0); // 结束
以前的结构
shiro-springBoot-web
导入shiro的依赖
编写Shiro的核心配置
realm 用户认证授权
ShiroConfig
新建一个shiro项目
选择这些,之后我们再去导入依赖

检查pom.xml,确保无误;
在themleaf创建个首页,编写一个controller,测试springboot项目正常启动;
使用的Springboot3.x,所以看:https://github.com/apache/shiro/tree/main/samples/spring-boot-3-web
引入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>2.0.0-alpha-4</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>2.0.0-alpha-4</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>2.0.0-alpha-4</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>2.0.0-alpha-4</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>2.0.0-alpha-4</version>
<classifier>jakarta</classifier>
</dependency>
编写Configuration 参照samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/WebApp.java
package com.demo.config;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.TextConfigurationRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Apache Shiro 核心配置类
* 用于集成 Shiro 权限框架到 Spring Boot 3.x 应用
*/
@Configuration
public class ShiroConfig {
/**
* 创建并配置Realm Bean用于权限管理
*
* @return 配置好的TextConfigurationRealm实例
*/
@Bean
public Realm realm() {
TextConfigurationRealm realm = new TextConfigurationRealm();
// 配置用户及其对应的权限角色
// 配置用户:格式为 username = password, role1, role2, ...
realm.setUserDefinitions(
"admin=admin,admin,manager,user
"
+ "manager=manager,manager,user
"
+ "user=user,user");
// 配置角色及其关系 这里不是“角色继承”,而是“角色拥有的权限”
// 配置角色权限(可选):格式为 role = permission1, permission2, ...
realm.setRoleDefinitions(
"admin=*:*:*
"
+ "manager=read:write
"
+ "user=read"
);
return realm;
}
/**
* 配置 Shiro 过滤器链
* 定义哪些路径需要什么样的过滤器(权限控制)
*
* @return ShiroFilterChainDefinition 实例
*/
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
// /login 路径允许匿名访问(anon),即未登录用户也可以访问,用于登录页面或登录接口。
chainDefinition.addPathDefinition("/login", "anon");
// 访问 /logout 需要登出(logout),即用户可登出。
chainDefinition.addPathDefinition("/logout", "logout");
// 访问根路径 / 需要认证(authc),即用户必须已登录才可访问。
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
/**
* 【关键】配置 SecurityManager
* 这是 Shiro 的核心,必须显式声明为 Bean
* shiro-spring-boot-starter 会自动使用这个 Bean
*
* @param realm 从 Spring 容器注入的 Realm
* @return 配置好的 SecurityManager
*/
@Bean
public DefaultWebSecurityManager securityManager(Realm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager ();
securityManager.setRealm(realm);
return securityManager;
}
}
Shiro导致项目出错
查错,寻找解决方案
期间一直报错java.lang.IllegalStateException,ClassNotFoundException,多方查找资料后发现是因为 Shiro 和 Spring Boot 版本不兼容导致的。是一个典型的 Jakarta EE 9+ 迁移问题,其中 javax.servlet 包被重命名为 jakarta.servlet
网络上提出的解决方案有下面几种
1. 降级 Spring Boot 版本,降到`SpringBoot 2.x` 使用`javax.servlet`的那个版本,因为本身想根据视频中的思路,学习最新的知识。故而pass;
2. 升级 Shiro 版本,目前所使用的版本就是直接从github上查找所取下来的最新版的master版本,`2.0.0-alpha-4`,所以不存在不是最新的版的情况,先放一边;
3. 添加 Jakarta Servlet API 兼容性依赖,添加一个桥接的依赖包,使`Javax`和`Jakarta`整合,合理;
使用上述方式意义尝试后,依旧提示有问题,又去通义上让AI给写一个Shiro的整合方案,检查后发现少注入的一个SecurityManager,补充后重新测试,程序能正常加载
重新捋一下流程
确定使用的Shiro版本是1.13.0,运行SpringBoot启动类正常
运行时报错 把配置类删掉,
确定使用的Shiro版本是2.0.0,运行SpringBoot启动类提示 java.lang.ClassNotFoundException,提示的内容说 Shiro 仍在引用旧的 javax.servlet 包,导致 ClassNotFoundException。但是在官方说现在已经支持SpringBoot 3.x,所以我们要检查下哪个个地方引的javax.servlet。
根据错误堆栈,问题出在 ShiroWebFilterConfiguration.shiroFilterFactoryBean 的条件处理上,根本原因是 ClassNotFoundException: javax.servlet.Filter。,javax.servlet.Filter这个类没有找到;
检查javadoc,问题点依旧存在,底层包依旧使用的javax.servlet![图片[6] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/9e3b56feee4f4f20a618ee31f02045c0.png)
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.demo</groupId>
<artifactId>springboot-shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-shiro</name>
<description>springboot-shiro</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<shiro.version>2.0.0</shiro.version>
<!-- <spring-framework.version>6.2.9</spring-framework.version>-->
</properties>
<!-- 引入Shiro BOM统一版本管理(可选但推荐) -->
<dependencyManagement>
<dependencies>
<dependency>
<!-- <groupId>org.apache.shiro</groupId>-->
<!-- <artifactId>shiro-bom</artifactId>-->
<!-- <version>2.0.0</version> <!– 使用最新兼容版本 –>-->
<!-- <type>pom</type>-->
<!-- <scope>import</scope>-->
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework</groupId>-->
<!-- <artifactId>spring-framework-bom</artifactId>-->
<!-- <version>6.2.9</version>-->
<!-- <type>pom</type>-->
<!-- <scope>import</scope>-->
<!-- </dependency>-->
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Shiro Spring Boot Starter (Web环境) -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
<!-- 排除可能存在的旧版 shiro-web -->
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 显式引入 Shiro Web Jakarta 版本 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</exclusion>
<!-- <exclusion>-->
<!-- <groupId>org.apache.shiro</groupId>-->
<!-- <artifactId>shiro-core</artifactId>-->
<!-- </exclusion>-->
</exclusions>
</dependency>
<!-- 若需手动引入核心组件(如非Spring Boot场景) -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- below dependencies may not be necessary in "real" applications -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
报错的log
The called method's class hierarchy was loaded from the following locations:
org.springframework.boot.web.servlet.FilterRegistrationBean: file:/D:/work/apache-maven/apache-maven-3.9.3/org/springframework/boot/spring-boot/3.5.4/spring-boot-3.5.4.jar
org.springframework.boot.web.servlet.AbstractFilterRegistrationBean: file:/D:/work/apache-maven/apache-maven-3.9.3/org/springframework/boot/spring-boot/3.5.4/spring-boot-3.5.4.jar
org.springframework.boot.web.servlet.DynamicRegistrationBean: file:/D:/work/apache-maven/apache-maven-3.9.3/org/springframework/boot/spring-boot/3.5.4/spring-boot-3.5.4.jar
org.springframework.boot.web.servlet.RegistrationBean: file:/D:/work/apache-maven/apache-maven-3.9.3/org/springframework/boot/spring-boot/3.5.4/spring-boot-3.5.4.jar
真是服了


手动注册Filter解决?,禁用自动配置,审慎使用AI
/**
* Apache Shiro 核心配置类
* 用于集成 Shiro 权限框架到 Spring Boot 3.x 应用
*/
@Configuration
@SpringBootApplication(exclude = {
ShiroBeanAutoConfiguration.class,
ShiroAnnotationProcessorAutoConfiguration.class,
ShiroWebFilterConfiguration.class // 关键:禁用有问题的自动配置
})
public class ShiroConfigR {
public FilterRegistrationBean<DelegatingFilterProxy> shiroFilterRegistration() {
FilterRegistrationBean<DelegatingFilterProxy> registration = new FilterRegistrationBean<>();
DelegatingFilterProxy filter = new DelegatingFilterProxy("shiroFilter");
filter.setTargetFilterLifecycle(true);
registration.setFilter(filter);
registration.setEnabled(true);
registration.addUrlPatterns("/*");
registration.setOrder(1);
// 使用新 API,不要用 setDispatcherTypes(...)
// Spring Boot 3 中应使用 setDispatcherTypes 枚举集合
registration.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD));
return registration;
}
// 使用简单内存 Realm(生产环境替换为数据库查询)
@Bean
public Realm realm() {
SimpleAccountRealm realm = new SimpleAccountRealm();
// 添加一个测试账户
realm.addAccount("admin", "123456", "admin");
return realm;
}
@Bean
public DefaultSecurityManager securityManager() {
DefaultSecurityManager manager = new DefaultSecurityManager();
manager.setRealm(realm());
return manager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
filter.setSecurityManager(securityManager());
// 设置未登录跳转登录页
filter.setLoginUrl("/login");
// 登录成功后跳转首页
filter.setSuccessUrl("/index");
// 无权限跳转页
filter.setUnauthorizedUrl("/unauthorized");
// 配置拦截规则
Map<String, String> chain = new LinkedHashMap<>();
chain.put("/login", "anon");
chain.put("/doLogin", "anon");
chain.put("/css/**", "anon");
chain.put("/js/**", "anon");
chain.put("/favicon.ico", "anon");
chain.put("/**", "authc"); // 其他路径需要认证
filter.setFilterChainDefinitionMap(chain);
return filter;
}
SpringBoot2+Shiro
SpringBoot3 不要了 还是 +Spring Security吧
继续排查
根据成功者的项目学习排查错误寻找解决方案,Ruoyi是怎么实现的呢
前提
刷git的时候看到了ruoyi,ruoyi同时支持Spring boot2, spring boot3,而且他的权限模块也是使用的Shiro,去看一看他是怎么实现的。
参照学习: 插件集成 | RuoYi
对比项目差别点,检查自身错误
跟着若依吧他需要的包导进去,
没有使用shiro-spring-boot-starter;myproject:去掉,会不会使用依赖移动器找不到导致的导入失败;
重新定义了spring-web,spring-webmvc;myproject:引入并指定版本
引入使用了jakarta.servlet-api,没有javax.servlet-api;myproject:删掉
为了使用Shiro 单独引入使用了shiro-core,shiro-spring,shiro-web,在shiro-web排除了shiro-web,并且都指定使用了jakarta的classifier;
同一个pom.xml中引入的依赖,同一个包下所使用的版本都是同一个version;
检查错误信息
发现导入 的Maven仓库里有两个spring-web,是不是这里出现的问题

mvn clean install ,清除缓存重新再引入依赖
删除不起作用反而会产生干扰的包
<!-- 引入Shiro BOM统一版本管理(可选但推荐) -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-bom</artifactId>
<!-- 使用最新兼容版本 -->
<version>${shiro.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>6.2.9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
重新mvn clean install,检查依赖,没有Spring5.3.2的依赖了,可能是解决了 ![图片[7] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/3792a6a82eb24686b9863288f9d5f289.png)
验证,检查各个包中主要使用的类,是否都抛弃了javax换成了Jakarta ![图片[8] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/7849fc0c7a904e7c93a6b8063cb7b6f5.png)
![图片[8] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/c2e2d4632b5f4ddf81dba43120727579.png)
项目启动测试,没有问题 ![图片[9] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/757b4ab246e942948d50a789776e1354.png)
为避免同情况发送,决定统一依赖包 使用<poperites> </poperites>管理

测试Shiro
编写认证授权的Relam
package com.demo.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class UserRealm extends AuthorizingRealm {
/**
* 获取授权信息 授权
*
* @param principals 身份凭证集合,包含用户的身份信息
* @return AuthorizationInfo 授权信息对象,包含用户的权限信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 创建简单的授权信息对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 添加用户相关权限
info.addStringPermission("user:add");
info.addStringPermission("user:delete");
info.addStringPermission("user:update");
System.out.println("执行了授权-----》");
return info;
}
/**
* 执行身份认证信息获取操作 认证
*
* @param token 认证令牌,包含用户提交的认证信息
* @return AuthenticationInfo 认证信息对象,包含正确的用户名和密码
* @throws AuthenticationException 认证异常,当认证过程中出现错误时抛出
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 创建简单的认证信息对象,使用硬编码的用户名"admin"和密码"123456"
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo("admin", "123456", getName());
System.out.println("执行了认证----》");
return info;
}
}
控制器
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/index")
@RequiresAuthentication
public String index() {
return "index";
}
@GetMapping("/unauthorized")
public String unauthorized() {
return "unauthorized";
}
}
跳转的页面编写

测试


编写登录拦截
在AbstractShiroWebFilterConfiguration这个类中,注入的时候根据所需要的内容给加载进去;

在前面的Autowired没有了ShiroFilterFactoryBean,多出了shiroFilterChainDefinition;

往后看,在ShiroFilterFactoryBean的方法中,将shiroFilterChainDefinition的getFilterChainMapset到 ShiroFilterFactoryBean中;

我们可以不去注入ShiroFilterChainDefinition这个方法,直接将所有的内容直接set进去;![图片[10] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/05f9a805bb1c405581a85cf5a9cff5db.png)
但是最好还是再写一个ShiroFilterChainDefinition,毕竟ShiroFilterFactoryBean虽然仍然是作为整个 Shiro 权限的入口,但是使用shiroFilterChainDefinition 这个SpringBean可以更好的实现配置解耦;![图片[11] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/c458f341df6349ad9e73fde65b5ea04f.png)
![图片[11] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/6cb7b43617a0403b8d56bc972ea38137.png)
Shiro 初始化流程
Spring Boot 启动
↓
加载 Shiro 自动配置类 (AbstractShiroWebFilterConfiguration)
↓
创建 SecurityManager Bean
↓
创建 ShiroFilterChainDefinition Bean(定义 URL 权限规则)
↓
调用 shiroFilterFactoryBean() 方法
↓
设置 loginUrl / successUrl / unauthorizedUrl
↓
绑定 SecurityManager
↓
设置 filterChainDefinitionMap(核心权限规则)
↓
生成 ShiroFilterFactoryBean → 创建 ShiroFilter
↓
注册到 Spring Web Filter Chain
↓
拦截所有请求,执行认证授权逻辑
本着存疑的心态去社区看Sample,确实也是使用的ShiroFilterChainDefinition;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.shiro.samples;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.TextConfigurationRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.HashMap;
import java.util.Map;
@Configuration
@ControllerAdvice
@SpringBootApplication
public class WebApp {
private static Logger log = LoggerFactory.getLogger(WebApp.class);
public static void main(String[] args) {
SpringApplication.run(WebApp.class, args);
}
@ExceptionHandler(AuthorizationException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public String handleException(AuthorizationException e, Model model) {
// you could return a 404 here instead (this is how github handles 403, so the user does NOT know there is a
// resource at that location)
log.debug("AuthorizationException was thrown", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("status", HttpStatus.FORBIDDEN.value());
map.put("message", "No message available");
model.addAttribute("errors", map);
return "error";
}
@Bean
public Realm realm() {
TextConfigurationRealm realm = new TextConfigurationRealm();
realm.setUserDefinitions("joe.coder=password,user
" + "jill.coder=password,admin");
realm.setRoleDefinitions("admin=read,write
" + "user=read");
realm.setCachingEnabled(true);
return realm;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
// need to accept POSTs from the login form
chainDefinition.addPathDefinition("/login.html", "authc");
chainDefinition.addPathDefinition("/logout", "logout");
return chainDefinition;
}
@ModelAttribute(name = "subject")
public Subject subject() {
return SecurityUtils.getSubject();
}
}
测试,页面正常被拦截
实现用户认证
根据quickstart,编写认证操作
// 获取当前用户 Subject
Subject currentUser = SecurityUtils.getSubject();
// 通过用户获取session
Session session = currentUser.getSession();
// 令牌,判断当前用户是否已经认证
if (!currentUser.isAuthenticated()) {
}
try {
// 登录操作
currentUser.login(token);
} catch (IncorrectCredentialsException ice) {
// 账号密码错误
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
if (currentUser.hasRole("schwartz")) {
}// 角色判断
// 非实例级别 粗粒度权限
if (currentUser.isPermitted("lightsaber:wield")) {
}
// 实例级权限 细粒度权限
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
}
currentUser.logout(); // 注销
System.exit(0); // 结束
新建一个/login的Controller,使用一个post请求提交表单,做简单处理
@PostMapping("/login")
public String doLogin(@RequestParam String username, @RequestParam String password,Model model) {
System.out.println("username--------->"+username + " password--------->"+password);
Subject subject = SecurityUtils.getSubject();
// 创建token
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
System.out.println("token--------->"+token);
try {
// 登录
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
// 账号请求
model.addAttribute("error", "用户不存在");
return "login";
// return "redirect:/login?error";
} catch (AuthenticationException e) {
// 权限认证
model.addAttribute("error", "用户名或密码错误");
return "login";
// return "redirect:/login?error";
}
}
写一个login.html测试请求

新建一个/login的Controller,使用一个get请求跳转页面,测试 Shiro过滤器工厂Bean是否在正常加载

@GetMapping("/login")
public String login(@RequestParam(required = false) String error, Model model) {
// 获取当前用户
if (error != null) {
model.addAttribute("error", error+"Shiro login 页面");
}
model.addAttribute("error", "Shiro login 页面");
return "login";
}

在获取认证信息验证Shiro的拦截操作,写了一个令牌,通过log看流程

这里犯了错误,只用SimpleAuthenticationInfo只会进行权限认证,出现错误只会Authentication的Exception,而我们会用到的有多种场景;
try {
// 登录操作
currentUser.login(token);
} catch (UnknownAccountException uae) {
// 不存在的账户信息
} catch (IncorrectCredentialsException ice) {
// 账号密码错误
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
// 账户被锁
} catch (AuthenticationException ae) {
// 认证失败
}

对应修改内容
执行
正确的场合,页面正常加载,进入后面的页面 ![图片[12] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/fdb9f83867914a9eaaaf47cf32be866d.png)
![图片[12] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/0e40a9ada99d473c8fc04e3ece584f28.png)
错误的场合,返回login页面,响应post请求的login内容,返回在post请求下的结果

根据步骤5 所验证的思路,使用Shiro的操作流程如下:
页面初次加载,指定登录页面,通过get请求进入登录页;
在登录页填写信息提交表单,将表单数据通过Pos请求提交至控制器;
调用AuthenticationInfo,获取实际授权的用户加密的令牌信息info;
在控制器调用Subject,将在AuthenticationInfo 的info获取过去;
通过UsernamePasswordToken将我们的账号密码加密成令牌;
调用subject.login方法执行登录操作,期间对两个加密的token进行匹配;
监听这个subject的操作,正常则继续进行页面跳转,将token保存在cookie中;
捕获到异常则执行异常的处理逻辑
关联数据库实现用户认证
导入依赖
导入数据库驱动
导入数据源Druid
导入Mybatis
导入log4j,Druid和Mybatis使用
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.20</version> <!-- 请检查Maven仓库获取最新版本 -->
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!-- configure logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.24.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.1</version>
<scope>runtime</scope>
</dependency>
建包,mapper,service,pojo;
配置配置文件,数据库,映射包;
写接口,实现方法;
测试数据库连接,方法正常映射,数据正常抽出;![图片[13] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/2a950da9e61e46cf9fc485f13c8b4cd4.png)
修改Realm的令牌验证的方法
设置加密
虽然实现了这个认证功能,但是密码还是明文显示的
找方法体,在SimpleAuthenticationInfo方法实现SaltedAuthenticationInfo、MergableAuthenticationInfo,
用于对账户凭证进行加盐处理的盐值;若未使用盐值,则为 null![图片[14] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/1e2d8aeb5ead48aa9d281fb8a4a63871.png)
查看ByteSource,匹配实现方法

ByteSource.Util.bytes(user.getPassword())对Password进行加密;
测试,info的信息已经是加密后的了

实现用户授权
准备工作
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
// 添加Shiro的内置过滤器
/*
anno : 匿名用户可访问
authc : 认证用户可访问
logout : 登出
user : 用户认证通过或RememberMe登录的都可以访问
perms : 该资源必须得到资源权限才能访问
*/
// 配置过滤器链映射关系
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/**", "authc");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
// 添加其他路径=过滤器定义
chainDefinition.addPathDefinitions(filterChainDefinitionMap);
// 配置单独页面请求根据权限访问
chainDefinition.addPathDefinition("/user/add", "perms[user:add]");
chainDefinition.addPathDefinition("/user/update", "perms[user:update]");
return chainDefinition;
}
设置perms;

查看doGetAuthorizationInfo为默认状态,测试;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 创建简单的授权信息对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
System.out.println("执行了授权-----》");
// // 添加用户相关权限
// info.addStringPermission("user:add");
// info.addStringPermission("user:delete");
// info.addStringPermission("user:update");
System.out.println(info);
return info;
}

设置未授权页面的跳转,测试

@GetMapping("/unauthorized")
@ResponseBody
public String noAuth() {
return "未授权页面";
}

在doGetAuthorizationInfo增加一个权限user:add

结合上面的内容测试,预定现象:add页面能够跳转,update页面跳转未授权;测试完成 
关联数据库实现用户授权
在数据库中加一个字段perms,将前面我们所写入的user:add数据写入;![图片[15] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/a20ea02c0d534ff2ac607863957f82e2.png)
对应的实体类内容同步更新;

在用户认证的时候去取出用户信息,在info中利用principal字段,将 用户信息 传入Session中; ![图片[16] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/7a811c5ec7e94cc8b50c2fff1507f7e3.png)
在AuthorizationInfo中,读取Subject对象的内容,add到Perms中;![图片[17] - 【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法 - 宋马](https://pic.songma.com/blogimg/20250830/02b1e94c1b1c45179c90f67720ce45b3.png)
测试,root用户只能进add,不能进update,测试ok
整合前端代码
准备工作
导入依赖,若依用到这个
<!-- thymeleaf模板引擎和shiro框架的整合 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
</dependency>
学习期间还是审慎好些,去这里找,最新版是2.1.0


<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.1.0</version>
</dependency>
配置整合Shiro+thymeleaf;
// 配置ShiroDialect,用于thymeleaf中使用Shiro标签
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
实现代码
导入命名空间,xmlns:shiro="http://www.pollix.at/thymeleaf/shiro";
<html lang="zh"
xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
写入内容,根据权限显示内容;
<div shiro:hasPermission="user:add">
<a href="/user/add">add</a>
</div>
<div shiro:hasPermission="user:update">
<a href="/user/update">update</a>
</div>
测试,根据既定想象内容正常显示 ;






















暂无评论内容