读前扫盲
HTTP 基本认证
1. 身份验证方式
HTTP 基本认证是一种基于 用户名和密码 的客户端-服务器身份验证机制。它的工作原理如下:
- 客户端(如浏览器)在访问受保护的资源时,需要提供用户名和密码。
- 服务器验证客户端提供的凭证,如果验证通过,则允许访问资源;否则返回错误。
2. Authorization 头
- 客户端在发送请求时,会将用户名和密码以
username:password
的形式拼接,然后进行 Base64 编码。 - 编码后的字符串通过 HTTP 请求头的
Authorization
字段发送给服务器。- 示例:
Authorization: Basic dXNlcjpwYXNzd29yZA==
- 其中
dXNlcjpwYXNzd29yZA==
是user:password
的 Base64 编码结果。
- 示例:
3. 401 Unauthorized
- 如果客户端未提供凭证,或者提供的凭证无效,服务器会返回 401 Unauthorized 状态码。
- 该状态码表示客户端未通过身份验证,无法访问请求的资源。
4. WWW-Authenticate: Basic
- 当服务器返回
401 Unauthorized
时,会在响应头中包含WWW-Authenticate: Basic
字段。 - 该字段提示客户端需要使用 HTTP 基本认证,并提供用户名和密码。
- 示例响应头:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Basic realm="My App"
- 示例响应头:
5. Base64
- Base64 是一种将二进制数据编码为 ASCII 字符串的编码方式。
- 在 HTTP 基本认证中,用户名和密码以
username:password
的形式拼接,然后进行 Base64 编码。- 示例:
- 原始字符串:
user:password
- Base64 编码后:
dXNlcjpwYXNzd29yZA==
- 原始字符串:
- 示例:
- 注意:Base64 不是加密,只是一种编码方式,容易被解码。
6. 安全性
- HTTP 基本认证的凭证(Base64 编码的用户名和密码)在传输过程中是 明文,容易被拦截和解码。
- 为了提高安全性,必须与 HTTPS 结合使用,通过 SSL/TLS 加密传输数据,防止凭证被窃取。
7. HTTP 安全策略
- 使用 HTTPS:
- 通过 SSL/TLS 加密通信,防止凭证在传输过程中被窃取。
- 避免在 URL 中传递敏感信息:
- URL 中的参数可能会被记录在日志或浏览器历史中,导致信息泄露。
- 强密码策略:
- 要求用户使用强密码,并定期更新密码。
- 结合其他安全机制:
- 使用更安全的身份验证方式(如 OAuth2、JWT)来增强安全性。
- 限制访问频率:
- 防止暴力破解,可以通过限制登录尝试次数来增强安全性。
总结
HTTP 基本认证是一种简单但安全性较低的身份验证方式,适用于内部系统或低安全性场景。它的核心流程如下:
- 客户端发送请求时,通过
Authorization
头传递 Base64 编码的用户名和密码。 - 服务器验证凭证,验证通过则允许访问,否则返回
401 Unauthorized
。 - 为了提高安全性,必须结合 HTTPS 使用,并遵循其他安全策略。
在实际应用中,建议在低安全性场景中使用 HTTP 基本认证,而在高安全性场景中结合 OAuth2、JWT 等更安全的机制。
正文开始:
1. HTTP 基本认证
HTTP 基本认证(Basic Authentication)是一种简单的身份验证方式,客户端在请求时通过 Authorization
头传递用户名和密码(Base64 编码)。
知识点
-
核心组件:
httpBasic()
:启用 HTTP 基本认证。UserDetailsService
:提供用户信息。PasswordEncoder
:加密和验证密码。
-
流程:
- 客户端发送请求时,服务器返回
401 Unauthorized
,并在响应头中包含WWW-Authenticate: Basic
。 - 客户端将用户名和密码以
username:password
的形式进行 Base64 编码,并通过Authorization
头发送。 - 服务器解码并验证用户名和密码。
- 客户端发送请求时,服务器返回
-
安全性:
- HTTP 基本认证的凭证是 Base64 编码的,容易被解码,因此必须与 HTTPS 结合使用。
示例代码
以下是一个实现 HTTP 基本认证的 Spring Boot 项目示例:
1. 依赖配置
在 pom.xml
中添加 Spring Security 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. 配置 SecurityFilterChain
在 SecurityConfig
类中配置 HTTP 基本认证:
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.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 SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated() // 所有请求都需要认证
)
.httpBasic(httpBasic -> httpBasic.realmName("My App")); // 启用 HTTP 基本认证
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
3. 控制器
创建一个简单的控制器:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@GetMapping("/home")
public String home() {
return "Welcome to the home page!";
}
}
4. 运行项目
启动项目后,访问 http://localhost:8080/home
,浏览器会弹出登录框,输入用户名 user
和密码 password
即可访问。
2. Remember-Me 功能
Remember-Me 功能允许用户在关闭浏览器后仍然保持登录状态。Spring Security 通过生成一个 Remember-Me Cookie 来实现这一功能。
知识点
-
核心组件:
rememberMe()
:启用 Remember-Me 功能。tokenRepository
:用于管理 Remember-Me Token。key()
:设置 Remember-Me 的密钥,用于加密 Token。
-
流程:
- 用户登录时,选择“记住我”。
- 服务器生成一个 Remember-Me Token 并存储在 Cookie 中。
- 用户再次访问时,服务器通过 Cookie 中的 Token 自动登录。
-
安全性:
- Remember-Me Token 默认有效期为 2 周。
- 建议使用持久化 Token 存储(如数据库)以增强安全性。
示例代码
以下是一个实现 Remember-Me 功能的 Spring Boot 项目示例:
1. 依赖配置
在 pom.xml
中添加 Spring Security 和数据库依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</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-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
2. 配置 SecurityFilterChain
在 SecurityConfig
类中配置 Remember-Me 功能:
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.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;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final DataSource dataSource;
public SecurityConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated() // 所有请求都需要认证
)
.formLogin(form -> form
.loginPage("/login") // 自定义登录页面
.permitAll()
)
.rememberMe(remember -> remember
.tokenRepository(persistentTokenRepository()) // 使用数据库存储 Token
.key("my-secure-key") // 设置 Remember-Me 密钥
.tokenValiditySeconds(1209600) // Token 有效期为 2 周
);
return http.build();
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
3. 控制器
创建一个控制器来处理登录页面:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LoginController {
@GetMapping("/login")
public String login() {
return "login"; // 返回 login.html
}
}
4. 登录页面
在 src/main/resources/templates
下创建 login.html
:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form th:action="@{/login}" method="post">
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
</div>
<div>
<input type="checkbox" id="remember-me" name="remember-me">
<label for="remember-me">Remember Me</label>
</div>
<div>
<button type="submit">Login</button>
</div>
</form>
</body>
</html>
5. 运行项目
启动项目后,访问 http://localhost:8080/login
,输入用户名 user
和密码 password
,勾选“Remember Me”即可启用 Remember-Me 功能。