security状态切换
在 Spring Security 中,"未认证" 到 "已认证" 的转换是通过 Authentication 对象的状态变化 来实现的。以下是具体的实现原理和步骤:
1. 核心原理
- "未认证":最初,
Authentication对象中没有认证成功的标志,例如用户凭据(用户名和密码)仅供验证使用。 - "已认证":一旦验证成功,会创建一个新的
Authentication对象,标记为已认证(isAuthenticated = true),并且这个对象中包含用户的详细信息、权限信息等。
Spring Security 通过 AuthenticationManager 和 Provider 的协作来完成这个状态切换,核心在于构造和返回一个已认证的 Authentication 对象。
2. 实现步骤
以 UsernamePasswordAuthenticationToken 为例,整个流程如下:
(1) 未认证对象的创建
过滤器(如 UsernamePasswordAuthenticationFilter)会将用户的输入(用户名和密码)封装成一个 "未认证" 的 Authentication 对象:
java
// 用户输入的用户名和密码 String username = ...; String password = ...; // 构建一个未认证的 Authentication 对象 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password); 此时:
principal= 用户名(如字符串 "user")。credentials= 密码(如字符串 "password")。authorities=null,因为还未认证。
(2) 传递给 AuthenticationManager
这个未认证的 Authentication 对象会被传递给 AuthenticationManager,由其负责认证。
java
Authentication result = authenticationManager.authenticate(authentication); (3) 由 Provider 验证用户信息
AuthenticationManager会将Authentication对象传递给合适的AuthenticationProvider。- 具体的
Provider(如DaoAuthenticationProvider)会通过UserDetailsService加载用户信息,并对密码进行校验。
关键代码示例:
java
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // 1. 从 Authentication 中获取用户凭据 String username = authentication.getName(); String password = authentication.getCredentials().toString(); // 2. 从数据库加载用户 UserDetails userDetails = userDetailsService.loadUserByUsername(username); // 3. 校验密码 if (!passwordEncoder.matches(password, userDetails.getPassword())) { throw new BadCredentialsException("Invalid username or password"); } // 4. 返回已认证的 Authentication 对象 return new UsernamePasswordAuthenticationToken( userDetails, null, // 密码可以不再存储 userDetails.getAuthorities() // 用户权限 ); } (4) 创建一个新的、已认证的 Authentication 对象
- 校验成功后,
Provider会返回一个新的UsernamePasswordAuthenticationToken对象。 - 这个对象中:
principal=UserDetails对象,包含用户的详细信息。credentials= 通常设置为null,因为密码已经校验通过,不需要再保存。authorities= 用户的权限列表,供后续授权使用。isAuthenticated=true,标记为已认证。
(5) 存储到 SecurityContext 中
- 最后,过滤器会将新的
Authentication对象存储到SecurityContextHolder中,标志用户已认证。 - 核心代码:
java
SecurityContextHolder.getContext().setAuthentication(authentication); 3. "未认证" 到 "已认证" 的具体对比
| 属性 | 未认证状态 | 已认证状态 |
|---|---|---|
principal |
用户名(如字符串 "user") | 用户详细信息(UserDetails 对象) |
credentials |
用户输入的密码 | 通常为 null,因为密码已校验成功,不再需要存储 |
authorities |
null |
用户的权限列表(从 UserDetails 中提取) |
isAuthenticated() |
false |
true,表示认证通过 |
| 存储位置 | 仅存在于过滤器的临时变量中 | 存储在 SecurityContextHolder 中,供后续使用 |
4. 为什么要返回一个新的 Authentication 对象?
-
状态的区分:
- "未认证" 和 "已认证" 是两种状态,直接标记在
Authentication对象上。 - Spring Security 的设计中,
Authentication对象是不可变的,因此要返回一个新的对象表示状态的改变。
- "未认证" 和 "已认证" 是两种状态,直接标记在
-
安全性:
- 未认证的对象可能存储着用户的敏感数据(如明文密码)。
- 返回新的对象时,可以将密码设置为
null,
您说:
isAuthenticated = true 这个是默认就是true还是怎么说,AuthenticationManager 又怎么感知对象对象切换的
ChatGPT 说:
ChatGPT
1. isAuthenticated = true 是默认的吗?
不是默认的,isAuthenticated 的值是由 Authentication 对象的设计逻辑决定的,具体可以分为以下几种情况:
-
在 "未认证" 的状态下:
- 当你手动创建一个
Authentication对象(如UsernamePasswordAuthenticationToken)时,默认的isAuthenticated值是false,表示对象尚未认证。 - 例如:
java
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password); System.out.println(token.isAuthenticated()); // 输出 false
- 当你手动创建一个
-
在认证通过后:
- 如果认证成功,Spring Security 通常会返回一个新的
Authentication对象(如UsernamePasswordAuthenticationToken),并将其isAuthenticated设置为true。 - 设置为
true的动作通常是在代码中明确完成的。例如:javaUsernamePasswordAuthenticationToken authenticatedToken = new UsernamePasswordAuthenticationToken(userDetails, null, authorities); authenticatedToken.setAuthenticated(true); // 明确标记为已认
- 如果认证成功,Spring Security 通常会返回一个新的

浙公网安备 33010602011771号