Springboot基础知识(18)- spring-boot-starter-security(Spring Security 启动器)
Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。
Spring Security 对 Web 资源的保护是靠 Filter 实现的。当初始化 Spring Security 时,会创建一个名为 springSecurityFilterChain 的 Servlet 过滤器,类型为 org.springframework.security.web.FilterChainProxy,它实现了 javax.servlet.Filter,因此外部的请求会经过此类。
FilterChainProxy 是一个代理,真正起作用的是 FilterChainProxy 中 SecurityFilterChain 所包含的各个 Filter。这些 Filter 作为 Bean 被 Spring 管理,它们是 Spring Security 核心,他们不直接处理用户的认证,也不直接处理用户的授权,而是把它们交给了认证管理器(AuthenticationManager)和决策管理器 (AccessDecisionManager)进行处理。
Spring Security 功能的实现主要是由一系列过滤器链相互配合完成:
(1) SecurityContextPersistenceFilter:是整个拦截过程的入口和出口(也就是第一个和最后一个拦截 器),会在请求开始时从配置好的 SecurityContextRepository 中获取 SecurityContext,然后把它设置给 SecurityContextHolder。在请求完成后将 SecurityContextHolder 持有的 SecurityContext 再保存到配置好的 SecurityContextRepository,同时清除 securityContextHolder 所持有的 SecurityContext;
(2) UsernamePasswordAuthenticationFilter:用于处理来自表单提交的认证。该表单必须提供对应的用户名和密码,其内部还有登录成功或失败后进行处理的 AuthenticationSuccessHandler 和 AuthenticationFailureHandler,这些都可以根据需求做相关改变;
(3) FilterSecurityInterceptor: 是用于保护web资源的,使用AccessDecisionManager对当前用户进行授权访问;
这里重点讨论两个概念:身份认证和授权,它们也是 Spring Security 的主要职责。
在 Spring Boot 出现之前,Spring Security 已经发展了很多年,但那时相较于强大的 Shiro,它一直不温不火。因为和 Shiro 相比,在 SSH (Spring+Struts+Hibernate) / SSM (Spring+SpringMVC+MyBatis) 中整合 Spring Security 是一件很繁琐的事情。
Spring Boot 给 Spring Security 提供了自动化配置方案 (spring-boot-starter-security),可以零配置使用 Spring Security。
1. 创建 Spring Boot 项目
项目名称:SpringbootWeb02
Spring Boot 版本:2.6.6
具体操作参考 “ Springboot基础知识(08)- spring-boot-starter-web(Web启动器)” 里 SpringbootWeb 项目的创建过程。
2. 添加 Thymeleaf 模板
1) 修改 pom.xml
1 <project ... > 2 ... 3 <dependencies> 4 ... 5 6 <dependency> 7 <groupId>org.springframework.boot</groupId> 8 <artifactId>spring-boot-starter-thymeleaf</artifactId> 9 </dependency> 10 11 ... 12 </dependencies> 13 14 ... 15 </project>
在IDE中项目列表 -> SpringbootWeb02 -> 点击鼠标右键 -> Maven -> Reload Project
2) 创建模板文件
Thymeleaf 模板的默认位置在 resources/templates 目录下,默认的后缀是 html,即只要将 HTML 页面放在“classpath:/templates/”下,Thymeleaf 就能自动进行渲染。
(1) 创建 src/main/resources/templates/common.html 文件
1 <div th:fragment="fragment-header" id="fragment-header-id"> 2 <p>Header</p> 3 </div> 4 <div th:fragment="fragment-banner" id="fragment-banner-id"> 5 <p>Banner</p> 6 <hr /> 7 </div> 8 <div th:fragment="fragment-footer(var)" id="fragment-footer-id"> 9 <hr /> 10 <p th:text="${var}">Footer</p> 11 </div>
注:若 src/main/resources/templates 目录不存在,手动创建各级目录,下同。
(2) 创建 src/main/resources/templates/home.html 文件
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Home</title> 6 </head> 7 <body> 8 9 <div th:replace="common::fragment-header"></div> 10 11 <div th:replace="common::fragment-banner"></div> 12 13 <div id="content" th:style="'min-height: 480px;'"> 14 <h3>Home Page</h3> 15 16 <p>Message: <span th:text="${message}"></span></p> 17 </div> 18 19 <div th:replace="common::fragment-footer(var='Copyright 2020')"></div> 20 21 </body> 22 </html>
(3) 修改 src/main/java/com/example/controller/IndexController.java 文件
1 package com.example.controller; 2 3 import org.springframework.ui.Model; 4 import org.springframework.stereotype.Controller; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.ResponseBody; 7 8 @Controller 9 public class IndexController { 10 @ResponseBody 11 @RequestMapping("/test") 12 public String test() { 13 return "Test Page"; 14 } 15 16 @RequestMapping("/home") 17 public String home(Model model) { 18 model.addAttribute("message", "Spring Boot Thymeleaf Demo"); 19 return "home"; 20 } 21 }
3) 运行
Edit Configurations
Click "+" add new configuration -> Select "Maven"
Command line: clean spring-boot:run
Name: SpringbootWeb02 [clean,spring-boot:run]
-> Apply / OK
Click Run "SpringbootWeb02 [clean,spring-boot:run]"
...
Spring boot web project
访问 http://localhost:9090/home
3. 添加 Spring Security
1) 修改 pom.xml
1 <project ... > 2 ... 3 <dependencies> 4 ... 5 6 <dependency> 7 <groupId>org.springframework.boot</groupId> 8 <artifactId>spring-boot-starter-security</artifactId> 9 </dependency> 10 11 ... 12 </dependencies> 13 14 ... 15 </project>
在IDE中项目列表 -> SpringbootWeb02 -> 点击鼠标右键 -> Maven -> Reload Project
运行并访问 http://localhost:9090/test,自动跳转到 http://localhost:9090/login, 默认用户名是 user,密码在项目启动时输出在控制台,格式如下:
Using generated security password: 61c29bfd-0f2b-4a35-a27c-c569f9b6f02d
This generated password is for development use only. Your security configuration must be updated before running your application in production.
可以设置自定义用户名和密码,在 application.properties 中配置:
spring.security.user.name=admin
spring.security.user.password=123456
spring.security.user.roles=admin
注: application.properties 的 admin 配置后,默认用户名 user 将失效。
2) 默认安全保护
Spring Boot 项目(本文版本 2.6.6)导入 spring-boot-starter-security 包后,http://localhost:9090/test 就处于默认安全保护状态,要关闭或暂停这种默认的安全保护状态,需要修改 src/main/java/com/example/App.java 里的启动类,修改代码如下:
1 package com.example; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; 7 8 @SpringBootApplication 9 @EnableAutoConfiguration( exclude = { SecurityAutoConfiguration.class } ) 10 public class App { 11 public static void main(String[] args) { 12 SpringApplication.run(App.class, args); 13 System.out.println("Spring boot example04 project"); 14 } 15 }
以上代码中,新增了 @EnableAutoConfiguration 注解,所以要重新开启保护状态,只需注释掉这一行。
还有一种方法也可以达到这种效果,在 Spring Boot 的启动类中的注解 @SpringBootApplication 上加入排除 Spring Boot 安全组件的配置,即:
@SpringBootApplication( exclude = { SecurityAutoConfiguration.class } )
如果不熟悉 @SpringBootApplication 注解的原理,建议使用 @EnableAutoConfiguration 注解。
4. 自定义配置 Spring Security
自定义配置 Spring Security 可以通过配置 WebSecurityConfigurerAdapter 的扩展类来实现,比如创建 WebSecurityConfig 类:
1 @Configuration 2 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 3 4 @Override 5 protected void configure(AuthenticationManagerBuilder auth) throws Exception { 6 super.configure(auth); 7 } 8 9 @Override 10 public void configure(WebSecurity web) throws Exception { 11 super.configure(web); 12 } 13 14 @Override 15 protected void configure(HttpSecurity http) throws Exception { 16 super.configure(http); 17 } 18 19 }
1) 认证管理器配置方法
void configure(AuthenticationManagerBuilder auth) 用来配置认证管理器 AuthenticationManager。所有 UserDetails 相关的由它处理,包含 PasswordEncoder 密码编码处理。
2) 核心过滤器配置方法
void configure(WebSecurity web) 用来配置 WebSecurity。而 WebSecurity 是基于 Servlet Filter 用来配置 springSecurityFilterChain。
而 springSecurityFilterChain 又被委托给了 Spring Security 核心过滤器 Bean DelegatingFilterProxy。
相关逻辑可以在 WebSecurityConfiguration 中找到,一般不会过多来自定义 WebSecurity,使用较多的使其ignoring() 方法用来忽略 Spring Security 对静态资源的控制。
3) 安全过滤器链配置方法
void configure(HttpSecurity http) 这个是我们使用最多的,用来配置 HttpSecurity 。 HttpSecurity 用于构建一个安全过滤器链 SecurityFilterChain,SecurityFilterChain 最终被注入核心过滤器 。
(1) HttpSecurity 默认配置
protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests().anyRequest().authenticated() .and() .formLogin() .and() .httpBasic(); }
上面是 Spring Security 在 Spring Boot 中的默认配置。通过以上的配置,应用具备了以下的功能:
a) 所有的请求访问都需要被授权;
b) 使用 form 表单进行登陆(默认路径为/login);
c) 防止 CSRF 攻击、 XSS 攻击;
d) 启用 HTTP Basic 认证。
(2) HttpSecurity 常用配置
HttpSecurity 使用了 builder 的构建方式来灵活制定访问策略,最早基于 XML 标签对 HttpSecurity 进行配置,现在使用 javaConfig 方式。常用配置如下:
方法 | 描述 |
openidLogin() | 用于基于 OpenId 的验证 |
headers() | 将安全标头添加到响应,比如说简单的 XSS 保护 |
cors() | 配置跨域资源共享(CORS) |
sessionManagement() | 允许配置会话管理 |
portMapper() | 允许配置一个 PortMapper(HttpSecurity#(getSharedObject(class))),其他提供 SecurityConfigurer 的对象使用 PortMapper 从 HTTP 重定向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,Spring Security 使用一个 PortMapperImpl 映射 HTTP 端口 8080 到 HTTPS 端口8443,HTTP 端口 80 到 HTTPS 端口 443 |
jee() | 配置基于容器的预认证。 在这种情况下,认证由 Servlet 容器管理 |
x509() | 配置基于x509的认证 |
rememberMe | 允许配置“记住我”的验证 |
authorizeRequests() | 允许基于使用 HttpServletRequest 限制访问 |
requestCache() | 允许配置请求缓存 |
exceptionHandling() | 允许配置错误处理 |
securityContext() | 在 HttpServletRequests 之间的 SecurityContextHolder 上设置SecurityContext的管理。 当使用 WebSecurityConfigurerAdapter 时,这将自动应用 |
servletApi() | 将 HttpServletRequest 方法与在其上找到的值集成到 SecurityContext 中。 当使用 WebSecurityConfigurerAdapter 时,这将自动应用 |
csrf() | 添加 CSRF 支持,使用 WebSecurityConfigurerAdapter 时,默认启用 |
logout() | 添加退出登录支持。当使用 WebSecurityConfigurerAdapter 时,这将自动应用。默认情况是,访问URL “/logout”,使 HTTP Session 无效来清除用户,清除已配置的任何 #rememberMe()身份验证,清除 SecurityContextHolder,然后重定向到 “/login?success” |
anonymous() | 允许配置匿名用户的表示方法。 当与 WebSecurityConfigurerAdapter 结合使用时,这将自动应用。 默认情况下,匿名用户将使用 org.springframework.security.authentication.AnonymousAuthenticationToken 表示,并包含角色 “ROLE_ANONYMOUS” |
formLogin() | 指定支持基于表单的身份验证。如果未指定 FormLoginConfigurer#loginPage(String),则将生成默认登录页面 |
oauth2Login() | 根据外部 OAuth 2.0 或 OpenID Connect 1.0 提供程序配置身份验证 |
requiresChannel() | 配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射 |
httpBasic() | 配置 Http Basic 验证 |
addFilterBefore() | 在指定的 Filter 类之前添加过滤器 |
addFilterAt() | 在指定的 Filter 类的位置添加过滤器 |
addFilterAfter() | 在指定的 Filter 类的之后添加过滤器 |
and() | 连接以上策略的连接器,用来组合安全策略。实际上就是 "而且" 的意思 |
5. HttpSecurity 配置实例
1) 创建 src/main/java/com/example/config/WebSecurityConfig.java 文件
1 package com.example.config; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 6 import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 import org.springframework.security.core.userdetails.UserDetailsService; 8 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 9 10 @Configuration 11 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 12 13 @Autowired 14 private UserDetailsService userDetailsService; 15 16 @Override 17 protected void configure(AuthenticationManagerBuilder auth) throws Exception { 18 auth.userDetailsService(userDetailsService); 19 } 20 21 @Override 22 protected void configure(HttpSecurity http) throws Exception { 23 // 配置认证 24 http.authorizeRequests().anyRequest().authenticated() 25 26 .and() 27 .formLogin() 28 .loginPage("/login") // 自定义登录页面 29 .loginProcessingUrl("/login/post") // 登录访问路径 30 .defaultSuccessUrl("/home").permitAll() // 登陆成功之后跳转地址 31 32 .and() 33 .csrf().disable(); // 关闭 csrf 保护功能,默认是开启的 34 35 } 36 37 }
2) 创建 src/main/resources/templates/login.html 文件
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Login</title> 6 </head> 7 <body> 8 <h3>Login</h3> 9 <p> </p> 10 <form action="/login/post" method="post"> 11 <p><input type="text" placeholder="Username" name="username" value="admin"></p> 12 <p><input type="password" placeholder="Password" name="password" value="123456"></p> 13 <p><input type="submit" value="Submit"></p> 14 </form> 15 </body> 16 </html>
注:如果开启 csrf 功能,需要在 <form></form> 标记内添加如下 Thymeleaf 模板代码。
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
3) 修改 src/main/java/com/example/controller/IndexController.java 文件
1 package com.example.controller; 2 3 import org.springframework.ui.Model; 4 import org.springframework.stereotype.Controller; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.ResponseBody; 7 8 @Controller 9 public class IndexController { 10 @ResponseBody 11 @RequestMapping("/test") 12 public String test() { 13 return "Test Page"; 14 } 15 16 @RequestMapping("/home") 17 public String home(Model model) { 18 model.addAttribute("message", "Spring Boot Thymeleaf Demo"); 19 return "home"; 20 } 21 22 @RequestMapping("/login") 23 public String login() { 24 return "login"; 25 } 26 }
访问 http://localhost:9090/home,自动跳转到 http://localhost:9090/login
项目源码:https://gitee.com/slksm/public-codes/tree/master/demos/SpringbootWeb02
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)