Spring Security 入门与实战指南(第二版)
https://github.com/WuSangui571/SpringSecurity 中的 README.md 文件浏览,此处发表的是经由 GPT 润色过的精简版。
第 4 章 验证码登录
在第 3 章自定义登录页的基础上,增加验证码校验,确保用户必须提供正确验证码才能登录。
4.1 前端页面调整
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>登录title>head>
<body>
<form th:action="@{/user/login}" method="post">
<label>账号:<input type="text" name="username"/>label><br/>
<label>密码:<input type="password" name="password"/>label><br/>
<label>验证码:<input type="text" name="captcha"/>label>
<img th:src="@{/common/captcha}" alt="验证码"/><br/>
<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
<button type="submit">登录button>
form>
body>
html>4.2 后端验证码生成
引入 Hutool Captcha:
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-captchaartifactId>
<version>5.8.38version>
dependency>编写 CaptchaController:
public class CaptchaController {
("/common/captcha")
public void generateCaptcha(HttpServletRequest req,
HttpServletResponse res) throws IOException {
res.setContentType("image/gif");
ICaptcha captcha = CaptchaUtil.createGifCaptcha(90, 30, 4, 50);
req.getSession().setAttribute("captcha", captcha.getCode());
captcha.write(res.getOutputStream());
}
}建议将验证码逻辑进一步抽象到 Service 层,Controller 仅负责路由。
4.3 验证过滤器实现
自定义 CaptchaFilter,在 UsernamePasswordAuthenticationFilter 之前进行校验:
public class CaptchaFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain)
throws ServletException, IOException {
if ("/user/login".equals(req.getRequestURI())) {
String code = (String) req.getSession().getAttribute("captcha");
String input = req.getParameter("captcha");
if (!StringUtils.hasText(input) || !input.equalsIgnoreCase(code)) {
res.sendRedirect("/toLogin");
return;
}
}
chain.doFilter(req, res);
}
}4.4 安全链配置
在 SecurityFilterChain 中放行验证码接口,并注册过滤器:
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.formLogin(form -> form
.loginPage("/toLogin")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/", true)
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/toLogin", "/common/captcha").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(captchaFilter,
UsernamePasswordAuthenticationFilter.class)
.csrf().and()
.build();
}4.5 验证码登录流程
访问
/hello,未登录跳转/toLogin。用户提交账号、密码、验证码。
CaptchaFilter校验验证码。通过则继续,失败重定向。UsernamePasswordAuthenticationFilter验证用户名和密码。登录成功,保存
Authentication到SecurityContext。
第 5 章 BCrypt 密码加密与匹配原理
5.1 密码安全存储
数据库仅存密文,不可逆。避免泄漏后被直接利用。
Spring Security 默认使用
BCrypt,具有内置盐值机制。
5.2 使用 BCryptPasswordEncoder
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}常用方法:
encode(raw):加密明文。matches(raw, encoded):匹配明文与密文。
String hash1 = encoder.encode("123");
String hash2 = encoder.encode("123");
assert !hash1.equals(hash2);
assert encoder.matches("123", hash1);5.3 算法结构
BCrypt 密文格式:$版本$强度$盐值+散列值
版本:前缀,如
$2a$10$。强度:工作因子。
盐值:22 字符随机值。
散列值:根据明文和盐值生成。
匹配时,提取密文中的盐值,重新加密明文并比对散列值。
第 6 章 获取当前登录用户信息
6.1 注入 Principal
("/userInfo")
public Principal userInfo(Principal principal) {
return principal;
}返回示例(简化):
{
"name": "admin",
"principal": { "username": "admin" }
}仅包含框架默认字段,不足以展示业务字段。
6.2 自定义 UserDetails
让实体 TUser 实现 UserDetails:
public class TUser implements UserDetails {
private Integer id;
private String loginAct;
private String loginPwd;
private String name;
// … 其他字段 …
public Collection extends GrantedAuthority> getAuthorities() { return List.of(); }
public String getPassword() { return loginPwd; }
public String getUsername() { return loginAct; }
// 其余 isXXX 方法同理
}在 loadUserByUsername 返回 TUser,并对不想输出的属性或方法加 @JsonIgnore。
6.3 注入 Authentication
("/userInfo2")
public Authentication userInfo2(Authentication auth) {
return auth;
}6.4 常用工具方法
public class LoginInfoUtil {
public static TUser getCurrentUser() {
Authentication auth = SecurityContextHolder
.getContext().getAuthentication();
return (TUser) auth.getPrincipal();
}
}- 微信
- 赶快加我聊天吧

- 赶快加我聊天吧

2025年06月23日 20:42:34 1楼
你真牛逼