SpringSecurity Oauth2 - 密码认证获取访问令牌源码分析

本文主要是介绍SpringSecurity Oauth2 - 密码认证获取访问令牌源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1. 授权服务器过滤器
      • 1. 常用的过滤器
      • 2. 工作原理
    • 2. 密码模式获取访问令牌
      • 1. 工作流程
      • 2. 用户凭证验证
        • 1. ResourceOwnerPasswordTokenGranter
        • 2. ProviderManager
        • 3. CustomAuthProvider
        • 4. 认证后的结果

1. 授权服务器过滤器

在Spring Security中,OAuth2授权服务器的过滤器是处理OAuth2授权流程的核心组件之一。它们在请求进入授权服务器时被应用,以确保请求的合法性,并执行授权和令牌处理。

1. 常用的过滤器

AuthorizationEndpointFilter:

  • 处理/oauth/authorize请求,它是OAuth2授权流程的核心过滤器。
  • 负责处理客户端请求授权码或访问令牌的过程。这个过滤器会检查请求的有效性、处理用户的认证信息、并将请求引导至授权页面(通常是一个登录页面或授权确认页面)。

TokenEndpointFilter:

  • 处理/oauth/token请求,这是获取访问令牌和刷新令牌的核心过滤器。
  • 负责处理各种授权方式的令牌颁发过程,包括授权码模式、密码模式、客户端凭据模式等。

ClientCredentialsTokenEndpointFilter:

  • 专门处理客户端凭据授权模式的令牌请求。
  • 负责验证客户端的身份并直接发放访问令牌,因为客户端凭据模式不涉及用户的授权确认。

CheckTokenEndpointFilter:

  • 处理令牌的检查请求,通常位于/oauth/check_token路径下。
  • 用于验证令牌的有效性,通常是资源服务器用来验证访问令牌是否有效的一个过滤器。

OAuth2LoginAuthenticationFilter:

  • 处理OAuth2登录流程,处理授权码登录或隐式授权流程中的登录请求。
  • 这个过滤器在接收到OAuth2的登录请求后,执行OAuth2的认证流程,并在认证成功后生成相应的OAuth2授权信息。

2. 工作原理

① 过滤器链:在Spring Security的配置中,这些过滤器通常被配置在一个过滤器链中,这样每个请求都会经过这些过滤器,并根据请求路径、请求参数和方法来决定该请求需要经过哪些过滤器的处理。

② 安全配置:Spring Security OAuth2的配置通过AuthorizationServerConfigurerAdapter类来进行。在这个类中,你可以配置上述的过滤器,并指定授权路径、令牌路径、客户端详细信息服务、以及各种授权方式的处理逻辑。

③ 令牌存储:这些过滤器通常会结合令牌存储(例如内存存储、数据库存储或JWT存储)一起使用,以管理访问令牌和刷新令牌的生成、存储、验证和撤销。

2. 密码模式获取访问令牌

1. 工作流程

在这里插入图片描述

① 验证客户端:首先,TokenEndpointFilter 会调用 ClientCredentialsTokenEndpointFilter 验证 client_idclient_secret,确保客户端的合法性。

② 用户凭证验证:如果客户端验证通过,ResourceOwnerPasswordTokenGranter 会验证 usernamepassword。如果用户凭证正确,则生成访问令牌。

③ 生成和返回令牌:如果所有验证通过,过滤器会生成访问令牌并返回给客户端。

这套流程确保了在密码模式下,只有正确的客户端和用户组合才能成功获取访问令牌。

2. 用户凭证验证

1. ResourceOwnerPasswordTokenGranter
public class ResourceOwnerPasswordTokenGranter extends AbstractTokenGranter {private static final String GRANT_TYPE = "password";private final AuthenticationManager authenticationManager;public ResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager,AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);}protected ResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {super(tokenServices, clientDetailsService, requestFactory, grantType);this.authenticationManager = authenticationManager;}@Overrideprotected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());String username = parameters.get("username");String password = parameters.get("password");// Protect from downstream leaks of passwordparameters.remove("password");Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);((AbstractAuthenticationToken) userAuth).setDetails(parameters);try {userAuth = authenticationManager.authenticate(userAuth);}catch (AccountStatusException ase) {//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)throw new InvalidGrantException(ase.getMessage());}catch (BadCredentialsException e) {// If the username/password are wrong the spec says we should send 400/invalid grantthrow new InvalidGrantException(e.getMessage());}if (userAuth == null || !userAuth.isAuthenticated()) {throw new InvalidGrantException("Could not authenticate user: " + username);}OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);		return new OAuth2Authentication(storedOAuth2Request, userAuth);}
}

在这里插入图片描述

2. ProviderManager
public class ProviderManager implements AuthenticationManager, MessageSourceAware,InitializingBean {private static final Log logger = LogFactory.getLog(ProviderManager.class);private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();private List<AuthenticationProvider> providers = Collections.emptyList();protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();private AuthenticationManager parent;private boolean eraseCredentialsAfterAuthentication = true;public ProviderManager(List<AuthenticationProvider> providers) {this(providers, null);}public ProviderManager(List<AuthenticationProvider> providers,AuthenticationManager parent) {Assert.notNull(providers, "providers list cannot be null");this.providers = providers;this.parent = parent;checkState();}public void afterPropertiesSet() {checkState();}private void checkState() {if (parent == null && providers.isEmpty()) {throw new IllegalArgumentException("A parent AuthenticationManager or a list "+ "of AuthenticationProviders is required");}}public Authentication authenticate(Authentication authentication)throws AuthenticationException {Class<? extends Authentication> toTest = authentication.getClass();AuthenticationException lastException = null;AuthenticationException parentException = null;Authentication result = null;Authentication parentResult = null;boolean debug = logger.isDebugEnabled();for (AuthenticationProvider provider : getProviders()) {if (!provider.supports(toTest)) {continue;}if (debug) {logger.debug("Authentication attempt using "+ provider.getClass().getName());}try {result = provider.authenticate(authentication);if (result != null) {copyDetails(authentication, result);break;}}catch (AccountStatusException | InternalAuthenticationServiceException e) {prepareException(e, authentication);// SEC-546: Avoid polling additional providers if auth failure is due to// invalid account statusthrow e;} catch (AuthenticationException e) {lastException = e;}}if (result == null && parent != null) {// Allow the parent to try.try {result = parentResult = parent.authenticate(authentication);}catch (ProviderNotFoundException e) {// ignore as we will throw below if no other exception occurred prior to// calling parent and the parent// may throw ProviderNotFound even though a provider in the child already// handled the request}catch (AuthenticationException e) {lastException = parentException = e;}}if (result != null) {if (eraseCredentialsAfterAuthentication&& (result instanceof CredentialsContainer)) {// Authentication is complete. Remove credentials and other secret data// from authentication((CredentialsContainer) result).eraseCredentials();}// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published itif (parentResult == null) {eventPublisher.publishAuthenticationSuccess(result);}return result;}// Parent was null, or didn't authenticate (or throw an exception).if (lastException == null) {lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",new Object[] { toTest.getName() },"No AuthenticationProvider found for {0}"));}// If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent// This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published itif (parentException == null) {prepareException(lastException, authentication);}throw lastException;}@SuppressWarnings("deprecation")private void prepareException(AuthenticationException ex, Authentication auth) {eventPublisher.publishAuthenticationFailure(ex, auth);}/*** Copies the authentication details from a source Authentication object to a* destination one, provided the latter does not already have one set.** @param source source authentication* @param dest the destination authentication object*/private void copyDetails(Authentication source, Authentication dest) {if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;token.setDetails(source.getDetails());}}public List<AuthenticationProvider> getProviders() {return providers;}public void setMessageSource(MessageSource messageSource) {this.messages = new MessageSourceAccessor(messageSource);}public void setAuthenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");this.eventPublisher = eventPublisher;}public void setEraseCredentialsAfterAuthentication(boolean eraseSecretData) {this.eraseCredentialsAfterAuthentication = eraseSecretData;}public boolean isEraseCredentialsAfterAuthentication() {return eraseCredentialsAfterAuthentication;}private static final class NullEventPublisher implements AuthenticationEventPublisher {public void publishAuthenticationFailure(AuthenticationException exception,Authentication authentication) {}public void publishAuthenticationSuccess(Authentication authentication) {}}
}

在这里插入图片描述

3. CustomAuthProvider
@Component
public class CustomAuthProvider implements AuthenticationProvider {@Autowiredprivate CustomUserDetailService userDetailService;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {String username = authentication.getName();String password = (String) authentication.getCredentials();UserDetails userDetails = userDetailService.loadUserByUsername(username);UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken= new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities());usernamePasswordAuthenticationToken.setDetails(authentication.getDetails());return usernamePasswordAuthenticationToken;}@Overridepublic boolean supports(Class<?> authentication) {return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);}
}

在这里插入图片描述

4. 认证后的结果

在这里插入图片描述

这篇关于SpringSecurity Oauth2 - 密码认证获取访问令牌源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1128691

相关文章

Java利用docx4j+Freemarker生成word文档

《Java利用docx4j+Freemarker生成word文档》这篇文章主要为大家详细介绍了Java如何利用docx4j+Freemarker生成word文档,文中的示例代码讲解详细,感兴趣的小伙伴... 目录技术方案maven依赖创建模板文件实现代码技术方案Java 1.8 + docx4j + Fr

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

基于SpringBoot+Mybatis实现Mysql分表

《基于SpringBoot+Mybatis实现Mysql分表》这篇文章主要为大家详细介绍了基于SpringBoot+Mybatis实现Mysql分表的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录基本思路定义注解创建ThreadLocal创建拦截器业务处理基本思路1.根据创建时间字段按年进

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

微信公众号脚本-获取热搜自动新建草稿并发布文章

《微信公众号脚本-获取热搜自动新建草稿并发布文章》本来想写一个自动化发布微信公众号的小绿书的脚本,但是微信公众号官网没有小绿书的接口,那就写一个获取热搜微信普通文章的脚本吧,:本文主要介绍微信公众... 目录介绍思路前期准备环境要求获取接口token获取热搜获取热搜数据下载热搜图片给图片加上标题文字上传图片

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@