Spring Security 教程(11)、验证

认证流程全景图

authentication 配图
authentication 配图
authentication 配图

当用户提交登录表单时,Spring Security 的认证流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
用户提交表单
↓
UsernamePasswordAuthenticationFilter 拦截
↓
提取 username 和 password
↓
创建 UsernamePasswordAuthenticationToken(未认证状态)
↓
AuthenticationManager.authenticate()
↓
DaoAuthenticationProvider(默认实现)
↓
UserDetailsService.loadUserByUsername() 加载用户
↓
PasswordEncoder 比对密码
↓
认证成功 → 创建完全认证的 Authentication 对象
↓
存入 SecurityContextHolder
↓
重定向到目标页面

UserDetailsService:用户信息的来源

UserDetailsService 是认证的核心接口,只有一个方法:

1
2
3
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

你可以实现这个接口,从数据库、LDAP、Redis 等任何地方加载用户信息。

Spring Security 提供了几个内置实现:

  • JdbcUserDetailsManager:从 JDBC 数据源加载用户
  • LdapUserDetailsManager:从 LDAP 目录加载用户
  • InMemoryUserDetailsManager:从内存中加载用户(适合测试)

自定义 UserDetailsService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Service
public class CustomUserDetailsService implements UserDetailsService {

@Autowired
private UserRepository userRepository;

@Autowired
private PasswordEncoder passwordEncoder;

@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在: " + username);
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.getEnabled(),
!user.getAccountExpired(),
!user.getCredentialsExpired(),
!user.getAccountLocked(),
user.getAuthorities()
);
}
}

PasswordEncoder:密码比对

UserDetailsService 返回的用户密码需要经过 PasswordEncoder 比对:

1
2
3
4
5
public interface PasswordEncoder {
String encode(CharSequence rawPassword); // 加密
boolean matches(CharSequence rawPassword, String encodedPassword); // 比对
default boolean upgradeEncoding(String encodedPassword) { return false; }
}

Spring Security 提供了多种实现:

  • BCryptPasswordEncoder:推荐使用,内置盐值,成本因子可调
  • Pbkdf2PasswordEncoder:基于 PBKDF2 的密码编码器
  • SCryptPasswordEncoder:基于 scrypt 的密码编码器
  • NoOpPasswordEncoder:明文密码(仅用于测试)

认证失败处理

认证失败时,Spring Security 会记录失败原因并跳转到错误页面。你可以自定义失败处理:

1
2
3
4
5
http.formLogin()
.failureHandler((request, response, exception) -> {
// 记录日志、限流、发送告警等
response.sendRedirect("/login?error=true");
});

这为安全监控提供了切入点:你可以记录失败次数、检测暴力破解、发送安全告警。