security状态切换

在 Spring Security 中,"未认证" 到 "已认证" 的转换是通过 Authentication 对象的状态变化 来实现的。以下是具体的实现原理和步骤:


1. 核心原理

  • "未认证":最初,Authentication 对象中没有认证成功的标志,例如用户凭据(用户名和密码)仅供验证使用。
  • "已认证":一旦验证成功,会创建一个新的 Authentication 对象,标记为已认证(isAuthenticated = true),并且这个对象中包含用户的详细信息、权限信息等。

Spring Security 通过 AuthenticationManagerProvider 的协作来完成这个状态切换,核心在于构造和返回一个已认证的 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 对象?

  1. 状态的区分

    • "未认证" 和 "已认证" 是两种状态,直接标记在 Authentication 对象上。
    • Spring Security 的设计中,Authentication 对象是不可变的,因此要返回一个新的对象表示状态的改变。
  2. 安全性

    • 未认证的对象可能存储着用户的敏感数据(如明文密码)。
    • 返回新的对象时,可以将密码设置为 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 的动作通常是在代码中明确完成的。例如:
      java
      UsernamePasswordAuthenticationToken authenticatedToken = new UsernamePasswordAuthenticationToken(userDetails, null, authorities); authenticatedToken.setAuthenticated(true); // 明确标记为已认
posted @   langpo  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示