Oauth2.0 补充

SecurityContextHolder

  • SecurityContextHolder 用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保存在 SecurityContextHolder 中。
  • SecurityContextHolder 默认使用 ThreadLocal 策略来存储认证信息。看到 ThreadLocal 也就意味着,这是一种与线程绑定的策略。
  • Spring Security 在用户登录时自动绑定认证信息到当前线程,在用户退出时,自动清除当前线程的认证信息。但这一切的前提,是你在web场景下使用Spring Security。
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) {
	String username = ((UserDetails)principal).getUsername();
} else {
	String username = principal.toString();
}
  • getAuthentication()返回了认证信息,再次 getPrincipal() 返回了身份信息,
  • UserDetails便是Spring对身份信息封装的一个接口

Authentication

package org.springframework.security.core;// <1>

public interface Authentication extends Principal, Serializable { // <1>
    Collection<? extends GrantedAuthority> getAuthorities(); //权限信息列表,默认是GrantedAuthority接口的一些实现类,通常是代表权限信息的一系列字符串。

    Object getCredentials();//密码信息,用户输入的密码字符串,在认证过后通常会被移除,用于保障安全。

    Object getDetails();//细节信息,web应用中的实现接口通常为 WebAuthenticationDetails,它记录了访问者的ip地址和sessionId的值。

    Object getPrincipal();//最重要的身份信息,大部分情况下返回的是UserDetails接口的实现类,也是框架中的常用接口之一。

    boolean isAuthenticated();

    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

AuthenticationManager

初次接触 Spring Security 的朋友相信会被AuthenticationManagerProviderManagerAuthenticationProvider …这么多相似的Spring认证类搞得晕头转向,但只要稍微梳理一下就可以理解清楚它们的联系和设计者的用意。

  • AuthenticationManager(接口)是认证相关的核心接口,也是发起认证的出发点,因为在实际需求中,我们可能会允许用户使用用户名+密码登录,同时允许用户使用邮箱+密码,手机号码+密码登录,甚至,可能允许用户使用指纹登录
  • 所以说 AuthenticationManager 一般不直接认证,AuthenticationManager接口的常用实现类 ProviderManager 内部会维护一个List<AuthenticationProvider>列表,存放多种认证方式,实际上这是委托者模式的应用(Delegate)。
  • 核心的认证入口始终只有一个:AuthenticationManager,不同的认证方式:用户名+密码(UsernamePasswordAuthenticationToken),邮箱+密码,手机号码+密码登录则对应了三个 AuthenticationProvider。
// 关键认证部分的ProviderManager源码如下:
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
		InitializingBean {

    // 维护一个AuthenticationProvider列表
    private List<AuthenticationProvider> providers = Collections.emptyList();
          
    public Authentication authenticate(Authentication authentication)
          throws AuthenticationException {
       Class<? extends Authentication> toTest = authentication.getClass();
       AuthenticationException lastException = null;
       Authentication result = null;

       // 依次认证
       for (AuthenticationProvider provider : getProviders()) {
          if (!provider.supports(toTest)) {
             continue;
          }
          try {
             result = provider.authenticate(authentication);

             if (result != null) {
                copyDetails(authentication, result);
                break;
             }
          }
          ...
          catch (AuthenticationException e) {
             lastException = e;
          }
       }
      // 如果有Authentication信息,则直接返回
      if (result != null) {
        if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
          //移除密码
          ((CredentialsContainer) result).eraseCredentials();
        }
        //发布登录成功事件
        eventPublisher.publishAuthenticationSuccess(result);
        return result;
	   }
	   ...
       //执行到此,说明没有认证成功,包装异常信息
       if (lastException == null) {
          lastException = new ProviderNotFoundException(messages.getMessage(
                "ProviderManager.providerNotFound",
                new Object[] { toTest.getName() },
                "No AuthenticationProvider found for {0}"));
       }
       prepareException(lastException, authentication);
       throw lastException;
    }
}
  • ProviderManager 中的List,会依照次序去认证,认证成功则立即返回,若认证失败则返回null,下一个AuthenticationProvider会继续尝试认证,如果所有认证器都无法认证成功,则ProviderManager 会抛出一个ProviderNotFoundException异常。

Spring Security是如何完成身份认证的?

  1. 用户名和密码被过滤器获取到,封装成Authentication,通常情况下是 UsernamePasswordAuthenticationToken 这个实现类。
  2. AuthenticationManager 身份管理器负责验证这个Authentication
  3. 认证成功后,AuthenticationManager身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会被移除)Authentication实例。
  4. SecurityContextHolder安全上下文容器将第3步填充了信息的Authentication,通过SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。

Oauth2.0 常见异常

  • UsernameNotFoundException:用户名未找到。
  • BadCredentialsException:认证错误。可以用于密码错误、验证码错误等。
  • RememberMeAuthenticationException:记住我 认证错误。 在登录中常常有 记住我 选项,因为这个地方导致的错误。
  • AccountStatusException:用户账户状态异常。例如被封号之类的。
  • NonceExpiredException:Nonce过期异常。
  • AuthenticationCredentialsNotFoundException:SecurityContext中不存在Authentication异常。
  • AuthenticationServiceException:认证服务异常。可能是后端数据库之类的出现问题等等情况。
  • ProviderNotFoundException:Provider找不到异常。由ProviderManager抛出。
  • SessionAuthenticationException:会话认证异常。通常是因为同一个用户打开了多个会话。
  • InsufficientAuthenticationException:认证信息不足异常?可能是由于 记住我 选项引发的。

Oauth2.0 系列文章

以下是同步到语雀的、可读性好一点,CSDN 继续看的点专栏就好。
Oauth2.0 核心篇
Oauth2.0 安全性(以微信授权登陆为例)
Oauth2.0 认证服务器搭建
Oauth2.0 添加验证码登陆方式
Oauth2.0 资源服务器搭建
Oauth2.0 自定义响应值以及异常处理
Oauth2.0 补充

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值