一、Spring Security 认证流程原理
1. 认证与授权流程概览
Spring Security 的认证和授权基于**过滤器链(Filter Chain)**实现。请求到达应用后,会依次经过多个过滤器,最终由 AuthenticationManager
完成认证,AccessDecisionManager
完成授权。核心流程如下:
-
请求拦截:通过
FilterChainProxy
代理调用多个过滤器(如UsernamePasswordAuthenticationFilter
)。 -
认证处理:过滤器将请求中的认证信息(如用户名、密码)封装为
AuthenticationToken
,提交给AuthenticationManager
。 -
身份校验:
AuthenticationManager
委托具体的AuthenticationProvider
(如DaoAuthenticationProvider
)进行校验,后者通过UserDetailsService
从数据库加载用户信息并比对密码。 -
上下文存储:认证成功后,
SecurityContextHolder
存储用户的SecurityContext
(包含认证信息)。 -
授权决策:
FilterSecurityInterceptor
调用AccessDecisionManager
,根据用户权限决定是否允许访问资源。
2. 核心过滤器链
Spring Security 的过滤器链是认证与授权的核心载体,关键组件如下:
过滤器名称 | 作用 |
---|---|
SecurityContextPersistenceFilter | 从 SecurityContextRepository 加载/保存 SecurityContext ,确保请求间上下文共享。 |
UsernamePasswordAuthenticationFilter | 拦截 /login 请求,处理表单登录,封装 UsernamePasswordAuthenticationToken 。 |
BasicAuthenticationFilter | 处理 HTTP Basic 认证(浏览器弹窗登录)。 |
RememberMeAuthenticationFilter | 实现“记住我”功能,基于 Cookie 自动登录。 |
AnonymousAuthenticationFilter | 处理匿名请求,为未登录用户分配匿名身份。 |
ExceptionTranslationFilter | 捕获认证和授权异常,如 AuthenticationException 和 AccessDeniedException 。 |
FilterSecurityInterceptor | 最终授权决策,调用 AccessDecisionManager 判断用户是否有权限访问资源。 |
3. 关键概念解析
-
AuthenticationToken
认证请求的载体,例如UsernamePasswordAuthenticationToken
封装了用户名和密码。 -
AuthenticationManager
认证入口,代理多个AuthenticationProvider
,默认实现为ProviderManager
。 -
AuthenticationProvider
具体认证逻辑的实现者(如DaoAuthenticationProvider
用于数据库校验,CasAuthenticationProvider
用于单点登录)。 -
UserDetailsService
从数据源(数据库、内存等)加载用户信息,返回UserDetails
对象(包含用户名、密码、权限列表)。 -
SecurityContext
存储当前用户的认证信息,通过SecurityContextHolder
与线程绑定,全局可访问。
二、Spring Security 认证实现步骤
1. 自定义 UserDetailsService
从内存切换为数据库存储用户信息,需实现 UserDetailsService
接口:
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) {
User userFromDB = userMapper.selectByUsername(username);
if (userFromDB == null) {
throw new UsernameNotFoundException("用户不存在");
}
// 从数据库加载权限列表
List<GrantedAuthority> authorities = new ArrayList<>();
return new org.springframework.security.core.userdetails.User(
username,
userFromDB.getPassword(),
authorities
);
}
}
2. 配置密码编码器
明文密码不安全!使用 BCryptPasswordEncoder
加密:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
3. 集成 MyBatis
添加依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
配置数据源:
spring:
datasource:
url: jdbc:mysql:///auth-db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
4. 自定义登录页面
HTML 页面(login.html
):
<form method="post" action="/login">
<input type="text" name="username" placeholder="用户名">
<input type="password" name="password" placeholder="密码">
<button type="submit">登录</button>
</form>
Spring Security 配置:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login.html").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.successForwardUrl("/home");
}
三、注意事项与最佳实践
-
密码安全:务必使用
BCryptPasswordEncoder
加密,避免明文存储。 -
权限控制:通过
@PreAuthorize
或配置类中的.hasRole()
实现细粒度授权。 -
会话管理:启用
HttpSession
管理或结合 Redis 实现分布式会话。 -
异常处理:自定义
AuthenticationEntryPoint
和AccessDeniedHandler
优化异常响应。
四、总结
通过本文,您已掌握 Spring Security 的认证流程原理与实现方法。关键步骤包括:
-
理解过滤器链与核心组件(如
AuthenticationManager
、UserDetailsService
)。 -
实现数据库驱动的用户认证。
-
配置密码编码器与自定义登录页面。
进一步学习建议:
-
探索 OAuth2 和 JWT 集成,实现更安全的分布式认证。