Spring Security 6.x 系列(12)—— Form表单认证登录自定义配置

一、前言

在本系列文章中介绍了 Form 表单认证和注销流程,对部分源码也进行详细分析。

本章主要学习 Spring Security 中表单认证的相关自定义配置。

二、自定义登录页面

Spring Security 表单认证默认规则中对未认证的请求会重定向到默认登录页面,也支持自定义设置登录页面。

2.1 整合 Thymeleaf 模版引擎

2.1.1 pom 依赖

完整 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"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><artifactId>spring-security</artifactId><groupId>com.gm</groupId><version>0.0.1-SNAPSHOT</version></parent><artifactId>form-security-custom</artifactId><packaging>jar</packaging><description>form表单登录示例(自定义)</description><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity6</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies>
</project>

2.1.2 Thymeleaf 配置

src/main/resources/application.yaml

server:port: 9000logging:level:org.springframework.security: TRACEspring:thymeleaf:#配置模板路径,默认是templatesprefix: classpath:/templates/#文件后缀suffix: .html#编码encoding: UTF-8#内容类别content-type: text/html#模板的模式,支持 HTML, XML TEXT JAVASCRIPTmode: HTML#开发时建议设置为false,否则会有缓存,导致页面没法及时看到更新后的效果。cache: false

2.2 创建登录页

src/main/resources/templates 目录下创建一个简单的登录页 login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>登录</title><!-- 引入 Bootstrap 样式文件 --><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"><style>body {background-color: #f8f9fa;}.login-container {max-width: 400px;margin: 0 auto;margin-top: 100px;padding: 20px;border-radius: 5px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);background-color: #fff;}.custom-alert {background-color: #FFD2D2;padding: 10px;border-radius: 5px;display: flex;align-items: center;justify-content: center;text-align: center;min-height: 50px; /* 设置最小高度 */}</style>
</head>
<body>
<div class="container"><div class="row justify-content-center"><div class="col-md-6 login-container"><h2 class="text-center mb-4">Login</h2><form th:action="@{/login}" method="post"><div th:if="${exception}" class="custom-alert"><p th:text="${exception}"></p></div><div th:if="${logout}" class="custom-alert"><p th:text="${logout}"></p></div><div class="mb-3"><input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/><label for="username" class="form-label">用户名</label><input type="text" class="form-control" id="username" name="username" required></div><div class="mb-3"><label for="password" class="form-label">密码</label><input type="password" class="form-control" id="password" name="password" required></div><button type="submit" class="btn btn-primary w-100">Login</button></form></div></div>
</div><!-- 引入 Bootstrap JS 文件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
  • <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/> 是处理开启 CSRF 保护所需参数
  • <div th:if="${exception}" class="custom-alert"><p th:text="${exception}"></p></div> 是显示认证相关异常,稍后详细介绍
  • <div th:if="${logout}" class="custom-alert"><p th:text="${logout}"></p></div> 是显示注册相关信息,稍后详细介绍

2.3 创建 Controller 访问控制

新建 LoginController.java

@Controller
@Slf4j
class LoginController {/*** 登录页面** @return*/@GetMapping("/login")String login() {return "login";}
}

2.4 Spring Security 配置

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {/*** 构建SecurityFilterChain** @param http* @return* @throws Exception*/@BeanSecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {// 配置所有http请求必须经过认证http.authorizeHttpRequests(authorizeRequests ->authorizeRequests.requestMatchers(new String[]{"/favicon.ico", "/custom/error", "/error", "/logout/success"}).permitAll().anyRequest().authenticated());// 开启表单认证(默认配置)// http.formLogin(Customizer.withDefaults());// 表单认证自定义配置http.formLogin(form ->form.loginPage("/login").permitAll() // /login,需permitAll放开访问控制并配置对应的controller请求地址.loginProcessingUrl("/login") // form表单登录处理请求URL(POST).usernameParameter("username") // form表单用户名参数名称.passwordParameter("password") // form表单密码参数名称);// 开启 CSRF 保护http.csrf(Customizer.withDefaults());// 禁止 CSRF 保护// http.csrf(csrf -> csrf.disable());// 构造器构建SecurityFilterChain对象return http.build();}/*** 配置登录名密码** @return*/@Beanpublic UserDetailsService userDetailsService() {UserDetails user = User.withUsername("admin").password("{noop}123").roles("USER").build();return new InMemoryUserDetailsManager(new UserDetails[]{user});}
}
  • loginPage("/login").permitAll():配置 URL 生成登录页面,常与 Controller 配合使用,必须是 POST
  • loginProcessingUrl("/login"):配置登录请求处理 URL ,用于修改UsernamePasswordAuthenticationFilter中拦截登录处理请求 URL 地址。
  • usernameParameter("username"):配置用户名参数名称 ,用于修改UsernamePasswordAuthenticationFilter中拦截登录处理请求用户名对应的参数名称。
  • passwordParameter("password"):配置密码参数名称 ,用于修改UsernamePasswordAuthenticationFilter中拦截登录处理请求密码对应的参数名称。

2.5 效果

在这里插入图片描述

三、自定义登录成功请求转发/重定向地址

3.1 创建登录成功页

src/main/resources/templates 目录下创建一个简单的登录成功页面 success.html

3.2 创建 Controller 访问控制

LoginController.java 新增:

/*** 登录成功页面** @return*/
@RequestMapping("/login/success")
String success() {return "success";
}

3.3 Spring Security 配置

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {/*** 构建SecurityFilterChain** @param http* @return* @throws Exception*/@BeanSecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {// 配置所有http请求必须经过认证http.authorizeHttpRequests(authorizeRequests ->authorizeRequests.requestMatchers(new String[]{"/favicon.ico", "/custom/error", "/error", "/logout/success"}).permitAll().anyRequest().authenticated());// 开启表单认证(默认配置)// http.formLogin(Customizer.withDefaults());// 表单认证自定义配置http.formLogin(form ->form.loginPage("/login").permitAll() // /login,需permitAll放开访问控制并配置对应的controller请求地址.loginProcessingUrl("/login") // form表单登录处理请求URL(POST).usernameParameter("username") // form表单用户名参数名称.passwordParameter("password") // form表单密码参数名称//.successForwardUrl("/login/success") // 登录成功请求转发URL(请求转发地址栏不变).defaultSuccessUrl("/login/success") // 登录成功请求重定向URL(重定向地址栏变));// 开启 CSRF 保护http.csrf(Customizer.withDefaults());// 禁止 CSRF 保护// http.csrf(csrf -> csrf.disable());// 构造器构建SecurityFilterChain对象return http.build();}/*** 配置登录名密码** @return*/@Beanpublic UserDetailsService userDetailsService() {UserDetails user = User.withUsername("admin").password("{noop}123").roles("USER").build();return new InMemoryUserDetailsManager(new UserDetails[]{user});}
}
  • successForwardUrl("/login/success"):配置登录成功请求转发 URL (请求转发地址栏不变),常与 Controller 配合使用,详情请见:ForwardAuthenticationSuccessHandler

  • defaultSuccessUrl("/login/success"):配置登录成功请求重定向URL(重定向地址栏变),常与 Controller 配合使用,详情请见:SavedRequestAwareAuthenticationSuccessHandler

3.4 效果

3.4.1 请求转发效果

在这里插入图片描述

3.4.2 重定向效果

在这里插入图片描述

四、自定义登录失败请求转发/重定向地址

4.1 创建登录失败页

登录失败页采用原登录页,显示各类登录异常。

4.2 创建 Controller 访问控制

/*** 登录失败页面** @return*/
@RequestMapping("/custom/error")
String failure(HttpServletRequest request, Model model) {// 以下是配置failureForwardUrl方式获取登录异常Object exception = request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);if (exception != null) {if (exception instanceof BadCredentialsException) {BadCredentialsException badCredentialsException = (BadCredentialsException) exception;model.addAttribute("exception", badCredentialsException.getMessage());return "login";}}exception = request.getAttribute(WebAttributes.ACCESS_DENIED_403);if (exception instanceof AccessDeniedException) {AccessDeniedException accessDeniedException = (AccessDeniedException) exception;model.addAttribute("exception", accessDeniedException.getMessage());return "login";}// 以下是配置failureUrl方式获取登录异常HttpSession session = request.getSession(false);if (session != null) {exception = request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);model.addAttribute("exception", ((AuthenticationException) exception).getMessage());return "login";}return "login";
}

注意
根据 Spring Security 中失败请求处理的配置不同获取异常的方式也是多样的。

4.3 Spring Security 配置

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {/*** 构建SecurityFilterChain** @param http* @return* @throws Exception*/@BeanSecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {// 配置所有http请求必须经过认证http.authorizeHttpRequests(authorizeRequests ->authorizeRequests.requestMatchers(new String[]{"/favicon.ico", "/custom/error", "/error", "/logout/success"}).permitAll().anyRequest().authenticated());// 开启表单认证(默认配置)// http.formLogin(Customizer.withDefaults());// 表单认证自定义配置http.formLogin(form ->form.loginPage("/login").permitAll() // /login,需permitAll放开访问控制并配置对应的controller请求地址.loginProcessingUrl("/login") // form表单登录处理请求URL(POST).usernameParameter("username") // form表单用户名参数名称.passwordParameter("password") // form表单密码参数名称//.successForwardUrl("/login/success") // 登录成功请求转发URL(请求转发地址栏不变).defaultSuccessUrl("/login/success") // 登录成功请求重定向URL(重定向地址栏变).failureForwardUrl("/custom/error") // 登录失败请求转发URL(请求转发地址栏不变)//.failureUrl("/custom/error") // 登录失败请求重定向URL(重定向地址栏变)(POST));// 配置AccessDeniedException异常处理请求URL(POST),主要是是处理401 BadCredentialsException 和 403 AccessDeniedException 异常http.exceptionHandling(exception -> exception.accessDeniedPage("/custom/error"));// 开启 CSRF 保护http.csrf(Customizer.withDefaults());// 禁止 CSRF 保护// http.csrf(csrf -> csrf.disable());// 构造器构建SecurityFilterChain对象return http.build();}/*** 配置登录名密码** @return*/@Beanpublic UserDetailsService userDetailsService() {UserDetails user = User.withUsername("admin").password("{noop}123").roles("USER").build();return new InMemoryUserDetailsManager(new UserDetails[]{user});}
}
  • failureForwardUrl("/custom/error"):配置登录失败跳转请求 URL ,常与 Controller 配合使用,详情请见:ForwardAuthenticationFailureHandler

  • failureUrl("/custom/error"):配置登录失败请求重定向URL(重定向地址栏变),常与 Controller 配合使用,详情请见:SimpleUrlAuthenticationFailureHandler

  • http.exceptionHandling(exception -> exception.accessDeniedPage("/custom/error")):配置 AccessDeniedException 异常处理调整URL,主要是是处理401 BadCredentialsException403 AccessDeniedException 异常,详情请见:AccessDeniedHandlerImpl

4.4 效果

在这里插入图片描述
在这里插入图片描述

五、自定义登录成功/失败处理器

目前基本都是前后端分离,基于 JSON 实现交与,后端中并没有任何页面,也不需要跳转地址,只需要告知前端登录成功返回用户信息即可,然后由前端进行页面跳转。

5.1 自定义登录成功处理器

在上篇录流程分析过,登录成功后会调用登录成功处理器(默认SavedRequestAwareAuthenticationSuccessHandler)进行页面跳转,那么只需要自定义登录成功处理器,就可以直接实现 JSON 返回,AuthenticationsuccessHandler 接口用于处理用户身份验证成功后的处理策略,实现可以随心所欲:

public class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandler {/*** 登录成功后直接返回 JSON** @param request        请求* @param response       响应* @param authentication 成功认证的用户信息*/@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8"); // 返回JSONresponse.setStatus(HttpStatus.OK.value());  // 状态码 200Map<String, Object> result = new HashMap<>(); // 返回结果result.put("msg", "登录成功");result.put("code", 200);result.put("data", authentication);response.getWriter().write(JSONUtil.toJsonStr(result));}
}

5.2 自定义登录失败处理器

在上篇录流程分析过,登录失败后会调用登录失败处理器(默认SimpleUrlAuthenticationFailureHandler)进行页面跳转,那么只需要自定义登录失败处理器,就可以直接实现 JSON 返回,AuthenticationFailureHandler 接口用于处理用户身份验证成功后的处理策略,实现可以随心所欲:

public class JsonAuthenticationFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8"); // 返回JSONresponse.setStatus(HttpStatus.BAD_REQUEST.value());  // 状态码 400Map<String, Object> result = new HashMap<>(); // 返回结果result.put("msg", "登录失败");result.put("code", 400);result.put("data", exception.getMessage());response.getWriter().write(JSONUtil.toJsonStr(result));}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/295049.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AtomHub 开源容器镜像中心开放公测,国内服务稳定下载

由开放原子开源基金会主导&#xff0c;华为、浪潮、DaoCloud、谐云、青云、飓风引擎以及 OpenSDV 开源联盟、openEuler 社区、OpenCloudOS 社区等成员单位共同发起建设的 AtomHub 可信镜像中心正式开放公测。AtomHub 秉承共建、共治、共享的理念&#xff0c;旨在为开源组织和开…

【操作系统】补充:你看到的所有地址都不是真的

补充&#xff1a;你看到的所有地址都不是真的 写过打印出指针的 C 程序吗&#xff1f;你看到的值&#xff08;一些大数字&#xff0c;通常以十六进制打印&#xff09;是虚拟地址&#xff08;virtual address&#xff09;。有没有想过你的程序代码在哪里找到&#xff1f;你也可以…

Kubernetes pod ip 暴露

1. k8s pod 和 service 网络暴露 借助 iptables 的路由转发功能&#xff0c;打通k8s集群内的pod和service网络&#xff0c;与外部网络联通 # 查看集群的 pod 网段和 service 网段 kubectl -n kube-system describe cm kubeadm-config networking:dnsDomain: cluster.localpod…

【水文专业词汇】气象水文、水利工程等

水文专业词汇&#xff1a;气象水文、水利工程等 气象水文类水循环过程地区分类 水利工程类跨流域调水工程 参考 气象水文类 水循环过程 中文英文降水/降雨precipitation/rainfall径流runoff/streamflow产汇流runoff generation 地区分类 中文英文雨养作物区rain-fed agricu…

机场信息集成系统系列介绍(8):基于视频分析的航班保障核心数据自动采集系统

目录 一、背景 二、相关功能规划 1、功能设计 2、其他设计要求 三、具体保障数据采集的覆盖点 四、相关性能指标要求 1、性能指标要求 2、算法指标要求 一、背景 基于视频分析的航班保障核心数据自动化采集系统&#xff0c;是ACDM系统建设的延伸&#xff0c;此类系统并…

【Linux基础开发工具】gcc/g++使用make/Makefile

目录 前言 gcc/g的使用 1. 语言的发展 1.1 语言和编译器自举的过程 1.2 程序翻译的过程&#xff1a; 2. 动静态库的理解 Linux项目自动化构建工具-make/makefile 1. 快速上手使用 2. makefile/make执行顺序的理解 前言 了解完vim编辑器的使用&#xff0c;接下来就可以尝…

springboot使用Validated实现参数校验

做为后端开发人员&#xff0c;一定有前端传的数据是可能会出错的警惕性&#xff0c;否则程序就可能会出错&#xff0c;比如常遇到的空指针异常&#xff0c;所以为了程序运行的健壮性&#xff0c;我们必须对每一个参数进行合法校验&#xff0c;就能避免很多不必要的错误&#xf…

力扣单调栈算法专题训练

目录 1 专题说明2 训练 1 专题说明 本博客用来计算力扣上的单调栈题目、解题思路和代码。 单调栈题目记录&#xff1a; 2232866美丽塔II 2 训练 题目1&#xff1a;2866美丽塔II。 解题思路&#xff1a;先计算出prefix[i]&#xff0c;表示0~i满足递增情况下&#xff0c;0~i…

html旋转相册

一、实验题目 做一个旋转的3d相册&#xff0c;当鼠标停留在相册时&#xff0c;相册向四面散开 二、实验代码 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" con…

【数据结构之单链表】

数据结构学习笔记---003 数据结构之单链表1、什么是单链表?1.1、概念及结构 2、单链表接口的实现2.1、单链表的SList.h2.1.1、定义单链表的结点存储结构2.1.2、声明单链表各个接口的函数 2.2、单链表的SList.c2.2.1、遍历打印链表2.2.2、销毁单链表2.2.3、打印单链表元素2.2.4…

三级安全教育二维码的应用

三级安全教育是指公司、项目经理部、施工班组三个层次的安全教育&#xff0c;是工人进场上岗前必备的过程&#xff0c;属于施工现场实名制管理的重要一环&#xff0c;也是工地管理中的核心部分之一&#xff0c;目前很多公司已经逐步开始使用系统来进行管理&#xff0c;下面我们…

Python数据科学视频讲解:特征归一化、特征标准化、样本归一化

5.1 特征归一化、特征标准化、样本归一化 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解5.1节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。内容涵盖数据科学应用的全流程&#…