本文继续第1部分,演示SpringBootOAuth身份验证服务器如何在内部处理令牌请求。
Spring安全OAuth服务器变得不受欢迎了。然而,更换还在开发中。因此,我认为值得检查一下当前的OAuth服务器是如何工作的。首先,这些知识帮助我们快速了解新服务器是如何实现相同功能的。其次,比较和对比当前的OAuth服务器和新的OAuth服务器是很有趣的。
我们构建了分布式SpringCloud系统的安全组件。该服务使用JWT力学。令牌使用服务器上的私钥签名;所有其他服务都用可用的公钥验证令牌。
在本文第一篇中,演示第1部分中配置的授权服务器如何构建筛选链,并为工作流操作令牌端点。第一部分:
图1.我们授权服务器的工作流程。
首先,令牌请求传递过滤器链,其中请求的客户:秘密对被验证。然后请求到达令牌端点。最后,请求被发送给令牌授予者,其中请求的用户名:密码或者验证访问令牌或刷新令牌,以生成JWT或访问令牌。检查电码关于细节。在我们深入研究它之前,让我们回顾一下Springbean生命周期。
春豆生命周期
春靴SpringApplication.run(...),在完成了大量配置环境和创建bean定义的工作之后,实例化Springbean。然后,注入bean依赖项(@Autowire)。接下来,定制init()方法(用@PostConstruct)被称为。之后,调用bean的自定义实用程序方法。对于我们的系统,我将这个过程分成“init”-bean实例化阶段和“main”-依赖注入和自定义init()方法调用。
这个bean创建过程看起来很简单。然而,如果豆子循环地相互依赖,情况就不是这样了。在这篇文章的后面,我们会看到这样的情况。但在此之前,让我解释一下我的表示法。
表示法
在这篇文章中,我使用了一些非标准的符号。
基本上,它是通常的协作图符号,并嵌入了一些伪代码。其他符号元素在其相应的数字上得到澄清。让我们看看引擎盖下面的工作流。
步骤1.初始化bean
一开始,我们@Configuration-创建带注释的bean:
图2.本系统授权服务器中的bean init阶段。“i”表示“bean实例化”。
这里配置了客户端详细信息服务。OAuth框架自动关联客户:秘密数据源到OAuth客户端详细信息桌子。我们只需要将数据源提供给
ClientDetailServiceConfiguration。这就是这里发生的情况:ClientDetailService创建bean(I.3),然后将bean注入
ClientDetailServiceConfiguration(一.4)。
注意
AuthorizationServerEndpointConfiguration @PostConstruct方法这么早就被调用了。这是因为这里有一个循环依赖关系:
AuthorizationServerEndpointConfiguration依赖于它的配置器(AuthorizationConfig)。另一方面,配置程序依赖于
AuthorizationServerEndpointConfiguration来创建TokenGranter。
步骤2.启动过滤链
接下来,配置初始化的bean。WebSecurityConfiguration类,随@EnableWebSecurity注释,控制过程。这些配置被组装成一个单独的(每个服务器)WebSecurity对象。
图3.如何WebSecurityConfiguration起作用了。“m”表示“依赖注入和自定义init()方法调用”。
WebSecurityConfiguration收集所有的后代WebSecurityConfigurer 类并将它们提供给
setFilterChainProxySecurityConfigurer若要将配置程序设置为WebSecurity对象。然后,WebSecurityConfiguration打电话springSecurityFilterChain() 若要使用已设置的配置程序构建WebSecurity对象,请执行以下操作。为了构建自己,WebSecurity从它的父级调用各种方法(图3),最重要的是,init(.)、配置(.)、build(.)的
WebSecurityConfigurerAdapter(WebSecurityConfigrer的子女)。
WebSecurity.build()最终创建一个securityFilterChain每个
WebSecurityConfigurerAdapter的后代。在我们的系统中有三个这样的适配器:AuthorizationConfig, SecurityConfig, H2Config。对于第二个,我们在我们的系统中不使用相应的过滤器链,我们只需要适配器的基础结构来构建客户:秘密认证管理器
步骤3.配置主过滤器链
在这个步骤中,系统构建了一个用于AuthorizationConfig。为了做到这一点,
AuthorizationServerSecurityConfiguration (他的后裔)WebSecurityConfigurer和
WebSecurityConfigurerAdapter)调用init(.)、配置(.)和构建(.)方法:
图4.如何AuthorizationServerSecurityConfiguration配置好了。还显示了对前面步骤的依赖关系。
这里,这个
WebSecurityConfigurerAdapter.init(WebSecurity web)方法创建其HttpSecurity对象;每个适配器有一个这样的对象。关于步骤M.7
AuthorizationConfig.configure(AuthServerSecurityConfigurer security)叫做。关于步骤M.8 asecurityFilterChain获取为
WebSecurityConfigurerAdapter.
注意WebSecurity和HttpSecurity行为也一样。这是因为它们扩展了相同的抽象类。图5(A)总结了这些关系。它们的工作方式如图5(B)所示:
图5.系统中某些类的父子关系。
我们走吧WebSecurity举个例子。模式是:
- WebSecurity收集配置器并将其传输到AbstractConfiguredSecurityBuilder.
- 建筑商呼叫doBuild()这反过来又呼唤init().
- Init遍历所有配置程序,并使用WebSecurity web作为争论。
- 同样适用于configure()和performBuild()方法。
步骤4.生成用户名和密码授权管理器
接下来,系统使用SecurityConfig以及它的父适配器来构建一个用户名:密码认证管理器:
图6.授权管理器是如何构建的。包括前面步骤的片段。
在这里,所有有趣的事情都发生在适配器的getHttp()方法。这个authenticationManager()方法获取userDetailsService从…SecurityConfig。然后userDetailsService被喂给DaoAuthenticationProvider。最后,我们得到了一个完全配置的可行的用户名:密码认证管理器
步骤5.建立主过滤链。
最后,我们需要完成构建主过滤器链,其中/oauth/token端点位于。
图7.客户如何:秘密BasicAuthenticationFilter配置好了。
这里详细显示了M.4-M.5步骤。然后,
AuthorizationServerSecurityConfigurer集ClientDetailsService就像userDetailService并将此服务放到
AuthenticationManagerBuilder。接下来,init(.)打电话HttpBasicConfigurer将经理设置为BasicAuthenticationFilter。因此,我们的主要安全过滤链是建立起来的。让我们看看所有这些部分是如何在令牌端点中一起工作的。
令牌端点
此端点的代码被存储。这里。其关键方法是:
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(
Principal principal, @RequestParam Map<String, String> parameters)
throws HttpRequestMethodNotSupportedException {
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
}
String clientId = getClientId(principal);
ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
// Only validate client details if a client is authenticated during this request.
// Double check to make sure that the client ID is the same in the token request and authenticated client.
if (StringUtils.hasText(clientId) && !clientId.equals(tokenRequest.getClientId())) {
throw new InvalidClientException("Given client ID does not match authenticated client");
}
if (authenticatedClient != null) {
oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
}
if (!StringUtils.hasText(tokenRequest.getGrantType())) {
throw new InvalidRequestException("Missing grant type");
}
if (tokenRequest.getGrantType().equals("implicit")) {
throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
}
if (isAuthCodeRequest(parameters) && !tokenRequest.getScope().isEmpty()) {
// The scope was requested or determined during the authorization step
logger.debug("Clearing scope of incoming token request");
tokenRequest.setScope(Collections.<String>emptySet());
} else if (isRefreshTokenRequest(parameters)) {
if (StringUtils.isEmpty(parameters.get("refresh_token"))) {
throw new InvalidRequestException("refresh_token parameter not provided");
}
// A refresh token has its own default scopes, so we should ignore any added by the factory here.
tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
}
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type");
}
return getResponse(token);
}
如何在SpringBootOAuth服务器中实现双因素认证?2:Under the Hood
原创2022-03-09 09:40·超级晴天ii
本文继续第1部分,演示SpringBootOAuth身份验证服务器如何在内部处理令牌请求。
Spring安全OAuth服务器变得不受欢迎了。然而,更换还在开发中。因此,我认为值得检查一下当前的OAuth服务器是如何工作的。首先,这些知识帮助我们快速了解新服务器是如何实现相同功能的。其次,比较和对比当前的OAuth服务器和新的OAuth服务器是很有趣的。
我们构建了分布式SpringCloud系统的安全组件。该服务使用JWT力学。令牌使用服务器上的私钥签名;所有其他服务都用可用的公钥验证令牌。
在本文第一篇中,演示第1部分中配置的授权服务器如何构建筛选链,并为工作流操作令牌端点。第一部分:
图1.我们授权服务器的工作流程。
首先,令牌请求传递过滤器链,其中请求的客户:秘密对被验证。然后请求到达令牌端点。最后,请求被发送给令牌授予者,其中请求的用户名:密码或者验证访问令牌或刷新令牌,以生成JWT或访问令牌。检查电码关于细节。在我们深入研究它之前,让我们回顾一下Springbean生命周期。
春豆生命周期
春靴SpringApplication.run(...),在完成了大量配置环境和创建bean定义的工作之后,实例化Springbean。然后,注入bean依赖项(@Autowire)。接下来,定制init()方法(用@PostConstruct)被称为。之后,调用bean的自定义实用程序方法。对于我们的系统,我将这个过程分成“init”-bean实例化阶段和“main”-依赖注入和自定义init()方法调用。
这个bean创建过程看起来很简单。然而,如果豆子循环地相互依赖,情况就不是这样了。在这篇文章的后面,我们会看到这样的情况。但在此之前,让我解释一下我的表示法。
表示法
在这篇文章中,我使用了一些非标准的符号。
基本上,它是通常的协作图符号,并嵌入了一些伪代码。其他符号元素在其相应的数字上得到澄清。让我们看看引擎盖下面的工作流。
步骤1.初始化bean
一开始,我们@Configuration-创建带注释的bean:
图2.本系统授权服务器中的bean init阶段。“i”表示“bean实例化”。
这里配置了客户端详细信息服务。OAuth框架自动关联客户:秘密数据源到OAuth客户端详细信息桌子。我们只需要将数据源提供给
ClientDetailServiceConfiguration。这就是这里发生的情况:ClientDetailService创建bean(I.3),然后将bean注入
ClientDetailServiceConfiguration(一.4)。
注意
AuthorizationServerEndpointConfiguration @PostConstruct方法这么早就被调用了。这是因为这里有一个循环依赖关系:
AuthorizationServerEndpointConfiguration依赖于它的配置器(AuthorizationConfig)。另一方面,配置程序依赖于
AuthorizationServerEndpointConfiguration来创建TokenGranter。
步骤2.启动过滤链
接下来,配置初始化的bean。WebSecurityConfiguration类,随@EnableWebSecurity注释,控制过程。这些配置被组装成一个单独的(每个服务器)WebSecurity对象。
图3.如何WebSecurityConfiguration起作用了。“m”表示“依赖注入和自定义init()方法调用”。
WebSecurityConfiguration收集所有的后代WebSecurityConfigurer 类并将它们提供给
setFilterChainProxySecurityConfigurer若要将配置程序设置为WebSecurity对象。然后,WebSecurityConfiguration打电话springSecurityFilterChain() 若要使用已设置的配置程序构建WebSecurity对象,请执行以下操作。为了构建自己,WebSecurity从它的父级调用各种方法(图3),最重要的是,init(.)、配置(.)、build(.)的
WebSecurityConfigurerAdapter(WebSecurityConfigrer的子女)。
WebSecurity.build()最终创建一个securityFilterChain每个
WebSecurityConfigurerAdapter的后代。在我们的系统中有三个这样的适配器:AuthorizationConfig, SecurityConfig, H2Config。对于第二个,我们在我们的系统中不使用相应的过滤器链,我们只需要适配器的基础结构来构建客户:秘密认证管理器
步骤3.配置主过滤器链
在这个步骤中,系统构建了一个用于AuthorizationConfig。为了做到这一点,
AuthorizationServerSecurityConfiguration (他的后裔)WebSecurityConfigurer和
WebSecurityConfigurerAdapter)调用init(.)、配置(.)和构建(.)方法:
图4.如何AuthorizationServerSecurityConfiguration配置好了。还显示了对前面步骤的依赖关系。
这里,这个
WebSecurityConfigurerAdapter.init(WebSecurity web)方法创建其HttpSecurity对象;每个适配器有一个这样的对象。关于步骤M.7
AuthorizationConfig.configure(AuthServerSecurityConfigurer security)叫做。关于步骤M.8 asecurityFilterChain获取为
WebSecurityConfigurerAdapter.
注意WebSecurity和HttpSecurity行为也一样。这是因为它们扩展了相同的抽象类。图5(A)总结了这些关系。它们的工作方式如图5(B)所示:
图5.系统中某些类的父子关系。
我们走吧WebSecurity举个例子。模式是:
- WebSecurity收集配置器并将其传输到AbstractConfiguredSecurityBuilder.
- 建筑商呼叫doBuild()这反过来又呼唤init().
- Init遍历所有配置程序,并使用WebSecurity web作为争论。
- 同样适用于configure()和performBuild()方法。
步骤4.生成用户名和密码授权管理器
接下来,系统使用SecurityConfig以及它的父适配器来构建一个用户名:密码认证管理器:
图6.授权管理器是如何构建的。包括前面步骤的片段。
在这里,所有有趣的事情都发生在适配器的getHttp()方法。这个authenticationManager()方法获取userDetailsService从…SecurityConfig。然后userDetailsService被喂给DaoAuthenticationProvider。最后,我们得到了一个完全配置的可行的用户名:密码认证管理器
步骤5.建立主过滤链。
最后,我们需要完成构建主过滤器链,其中/oauth/token端点位于。
图7.客户如何:秘密BasicAuthenticationFilter配置好了。
这里详细显示了M.4-M.5步骤。然后,
AuthorizationServerSecurityConfigurer集ClientDetailsService就像userDetailService并将此服务放到
AuthenticationManagerBuilder。接下来,init(.)打电话HttpBasicConfigurer将经理设置为BasicAuthenticationFilter。因此,我们的主要安全过滤链是建立起来的。让我们看看所有这些部分是如何在令牌端点中一起工作的。
令牌端点
此端点的代码被存储。这里。其关键方法是:
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(
Principal principal, @RequestParam Map<String, String> parameters)
throws HttpRequestMethodNotSupportedException {
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
}
String clientId = getClientId(principal);
ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
// Only validate client details if a client is authenticated during this request.
// Double check to make sure that the client ID is the same in the token request and authenticated client.
if (StringUtils.hasText(clientId) && !clientId.equals(tokenRequest.getClientId())) {
throw new InvalidClientException("Given client ID does not match authenticated client");
}
if (authenticatedClient != null) {
oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
}
if (!StringUtils.hasText(tokenRequest.getGrantType())) {
throw new InvalidRequestException("Missing grant type");
}
if (tokenRequest.getGrantType().equals("implicit")) {
throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
}
if (isAuthCodeRequest(parameters) && !tokenRequest.getScope().isEmpty()) {
// The scope was requested or determined during the authorization step
logger.debug("Clearing scope of incoming token request");
tokenRequest.setScope(Collections.<String>emptySet());
} else if (isRefreshTokenRequest(parameters)) {
if (StringUtils.isEmpty(parameters.get("refresh_token"))) {
throw new InvalidRequestException("refresh_token parameter not provided");
}
// A refresh token has its own default scopes, so we should ignore any added by the factory here.
tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
}
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type");
}
return getResponse(token);
}
尽管看起来很长,但它是一个普通的春季安全保护端点。有一个主体参数和请求参数作为输入参数。注意主体有关于客户:秘密对用户,而不是用户的用户名:密码-这是因为 **这个BasicAuthenticationFilter设置为只检查客户:秘密一对。该方法验证客户端:秘对,创建TokenRequest,然后调用TokenGranter.grant(tokenRequest.getGrantType(), tokenRequest) 。在我们的示例中,这是在第一部分。最后,响应是从授予的OAuth2AccessToken.
结论
在这篇文章中,我演示了SpringOAuth服务器如何在5个步骤中构建主过滤器链。这些步骤是:bean初始化,启动过滤器链,配置主过滤链,构建用户名和密码授权管理器,构建主过滤链。此外,我还展示了令牌端点是如何工作的。
最后
谢谢大家的观看,小伙伴们如果觉得我写得不错,可以点赞+收藏!更多Java进阶学习资料、2022大厂面试真题,完整资料已经给大家打包完毕,需要资料的小伙伴可以点击获取学习资料!