【SpringBoot】SpringBoot 中的 Shiro、Spring Security 学习过程及碰到的问题和解决方法
文章目录
- 网站安全
- 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
:自定义认证策略
环境搭建
- 创建项目
- 检查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 {@Overrideprotected 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();}//定义认证规则@Overrideprotected 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 {@Beanpublic 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 用户详情服务实例*/@Beanpublic 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
页面; - 注销:
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" style="text-align: center"><h3>Spring Security Study by 秦疆</h3></div><div><br><div class="ui three column stackable grid"><!--菜单 , 根据用户的角色, 动态的实现-->