- Spring Security 教程
- Spring Security - 首页
- Spring Security - 简介
- Spring Security - 架构
- Spring Security - 项目模块
- Spring Security - 环境设置
- Spring Security - 表单登录
- Spring Security - 自定义表单登录
- Spring Security - 注销
- Spring Security - 记住我
- Spring Security - 重定向
- Spring Security - 标签库
- Spring Security - XML 配置
- Spring Security - 认证提供程序
- Spring Security - 基本认证
- Spring Security - AuthenticationFailureHandler
- Spring Security - JWT
- Spring Security - 检索用户信息
- Spring Security - Maven
- Spring Security - 默认密码编码器
- Spring Security – 密码编码
- Spring Security - 方法级别
- Spring Security 有用资源
- Spring Security - 快速指南
- Spring Security - 有用资源
- Spring Security - 讨论
Spring Security - 默认密码编码器
无密码编码器
在 Spring Security 5 之前,开发人员可以使用内存密码作为纯文本,但随着 Spring Security 中密码相关增强功能的引入,现在 Spring Security 不支持纯文本密码。请考虑以下配置片段以更好地理解它。
纯文本密码
@Bean
protected UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password("user123") // password stored as plain text
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password("admin123")
.roles("USER", "ADMIN") // password stored as plain text
.build();
return new InMemoryUserDetailsManager(user, admin);
}
输出
以上 Spring Security 配置用于创建两个用户的内存用户库,这些用户由纯文本密码标识。如果我们尝试使用此 InMemoryUserDetailsManager 验证用户,则应用程序服务器将抛出异常。以下是在用户尝试登录时出现的堆栈跟踪,并且用户将再次显示登录页面
java.lang.IllegalArgumentException: You have entered a password with no PasswordEncoder. If that is your intent, it should be prefixed with `{noop}`.
at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:296) ~[spring-security-crypto-6.3.1.jar:6.3.1]
...
使用 {noop} 的纯文本密码
以上堆栈跟踪表明不支持纯文本内存密码。如果我们需要有意实现它,则需要使用 '{noop}' 作为前缀。让我们再次更新 Spring Security 配置并查看结果。
@Bean
protected UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password("{noop}user123") // password stored as plain text
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password("{noop}admin123")
.roles("USER", "ADMIN") // password stored as plain text
.build();
return new InMemoryUserDetailsManager(user, admin);
}
现在,如果您运行应用程序并登录,用户将能够登录到应用程序。在这里,Spring Security 使用 NoOpPasswordEncoder 作为默认密码编码器来验证配置的用户。
请注意,不建议在生产环境中使用此方法,因为 NoOpPasswordEncoder 已弃用且不安全。
使用密码编码器
始终建议使用良好的密码编码器来加密内存密码。BCryptPasswordEncoder 就是这样一个内置的密码编码器,它将以 BCrypt 编码格式将密码存储在内存中。
@Bean
protected UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("user123")) // encode the password
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin123")) // encode the password
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
// Create a new Password Encoder
@Bean
protected PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
让我们开始使用 Spring Security 进行实际编程。在开始使用 Spring 框架编写示例之前,您必须确保已正确设置 Spring 环境,如Spring Security - 环境设置章节中所述。我们还假设您对 Spring Tool Suite IDE 有基本的了解。
现在让我们继续编写一个由 Maven 管理的基于 Spring MVC 的应用程序,该应用程序将要求用户登录、验证用户,然后提供使用 Spring Security 表单登录功能注销的选项。
使用 Spring Initializr 创建项目
Spring Initializr 是开始 Spring Boot 项目的好方法。它提供了一个易于使用的用户界面来创建项目、添加依赖项、选择 Java 运行时等。它生成一个骨架项目结构,一旦下载,就可以导入到 Spring Tool Suite 中,然后我们可以继续使用我们的现成项目结构。
我们选择一个 Maven 项目,将项目命名为 formlogin,Java 版本为 21。添加以下依赖项:
Spring Web
Spring Security
Spring Boot DevTools
Thymeleaf 是 Java 的模板引擎。它允许我们快速开发静态或动态网页以在浏览器中呈现。它具有极强的扩展性,允许我们详细定义和自定义模板的处理过程。此外,我们可以通过点击此链接了解更多关于 Thymeleaf 的信息。
让我们继续生成我们的项目并下载它。然后,我们将它解压缩到我们选择的文件夹中,并使用任何 IDE 打开它。我将使用Spring Tools Suite 4。它可以从https://springframework.org.cn/tools网站免费下载,并且针对 Spring 应用程序进行了优化。
包含所有相关依赖项的 pom.xml
让我们看一下我们的 pom.xml 文件。它应该看起来类似于以下内容:
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>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tutorialspoint.security</groupId>
<artifactId>formlogin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>formlogin</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-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>
Spring Security 配置类
在我们的 config 包内,我们创建了 WebSecurityConfig 类。我们将使用此类进行安全配置,因此让我们使用 @Configuration 注解和 @EnableWebSecurity 对其进行注释。因此,Spring Security 知道将此类视为配置类。如我们所见,Spring 使配置应用程序变得非常容易。
WebSecurityConfig
package com.tutorialspoint.security.formlogin.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
protected UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("user123"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin123"))
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
protected PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(
request -> request.requestMatchers("/login").permitAll()
.requestMatchers("/**").authenticated()
)
.formLogin(form -> form.loginPage("/login")
.defaultSuccessUrl("/")
.failureUrl("/login?error=true")
.permitAll())
.rememberMe(config -> config.key("123456")
.tokenValiditySeconds(3600))
.logout(config -> config
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID"))
.build();
}
}
控制器类
在此类中,我们为 "/" 端点和 "/login" 端点创建了映射,分别用于此应用程序的索引页面和登录页面。
AuthController.java
package com.tutorialspoint.security.formlogin.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AuthController {
@GetMapping("/")
public String home() {
return "index";
}
@GetMapping("/login")
public String login() {
return "login";
}
}
视图
让我们在/src/main/resources/templates文件夹中创建 index.html,其内容如下,作为主页并显示登录用户的姓名。
index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>
Hello World!
</title>
</head>
<body>
<h1 th:inline="text">Hello <span sec:authentication="name"></span>!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</body>
<html>
login.html
让我们在/src/main/resources/templates文件夹中创建 login.html,其内容如下,作为登录页面。我们使用默认名称username、password和remember-me作为文本字段。如果使用其他名称,则也需要在 Spring Security 配置类中设置相同的名称。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example</title>
</head>
<body>
<div th:if="${param.error}">
<p>Bad Credentials</p>
</div>
<div th:if="${param.logout}">You have been logged out.</div>
<form th:action="@{/login}" method="post">
<h1>Please sign in</h1>
<table>
<tr>
<td><label for="username"><b>Username</b></label></td>
<td><input type="text" placeholder="Enter Username" name="username" id="username" required></td>
</tr>
<tr>
<td><label for="password"><b>Password</b></label></td>
<td><input type="password" placeholder="Enter Password" name="password" id="password" required></td>
</tr>
<tr>
<td><label for="remember-me"><b>Remember Me</b></label> </td>
<td><input type="checkbox" name="remember-me" /></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="Sign In" /></td>
</tr>
</table>
</form>
</body>
</html>
在登录表单中,我们使用 POST 方法登录,同时使用名称和 id 为username、password和remember-me复选框的输入字段。如果选中复选框,则用户登录应用程序时将创建记住我的 Cookie。
运行应用程序
由于我们已准备好所有组件,因此让我们运行应用程序。右键单击项目,选择以...方式运行,然后选择Spring Boot 应用。
它将启动应用程序,并且应用程序启动后,我们可以运行 localhost:8080 以检查更改。
输出
现在打开 localhost:8080,您可以看到我们的登录页面,其中包含“记住我”复选框。
登录页面
主页
输入有效凭据。