Spring OAuth2 安全 - 客户端凭据 - 自定义 AuthenticationProvider
Spring OAuth2 Security - Client Credentials - Custom AuthenticationProvider
我正在我们的服务架构中编写一个身份验证服务,它基本上是 Spring OAuth2 授权服务器的一个实现。由此我需要根据许多不同的来源验证提供的凭据以支持遗留环境。
我主要专注于使用客户端凭据流程,用户(其他服务)将使用他们自己的凭据来获取令牌,并且暂时不需要 OAuth2 的授权或刷新。
我已经成功启动了 Spring 受 Spring 安全性 (@EnableWebSecurity
) 保护的启动应用程序。我还成功设置了授权服务器 (@EnableAuthorizationServer
),它提供了必要的端点 (/oauth/token
) 来提供令牌。我已经能够在内存中配置授权服务器并自定义 ClientDetailsService 以成功获取令牌。这一切只是为了向自己证明我可以得到一些东西。
我的问题是我需要根据自定义源验证凭据。我无法直接访问密码,当然也不知道它们是如何编码的。
在深入研究Spring代码后,我可以看到通过DaoAuthenticationProvider
,它通过调用PasswordEncoder.matches()
来完成身份验证。不幸的是,我没有从 ClientDetailsService
获得密码,我也不知道密码是如何编码的,所以自定义 PasswordEncoder
对我没有多大好处(我也需要匹配多种方式只有一种 PasswordEncoder
)。所以,我只剩下定义自己的 AuthenticationProvider
(或 AuthenticationManager
)。
我能够实现自己的 AuthenticationProvider
并将其提供给 Spring 安全配置。这非常有效,我能够将身份验证推迟到我自己的提供者,它可以做我认为合适的任何事情(例如委托给另一个服务来进行身份验证),但这只适用于非 OAuth2 端点。
现在一切都开始崩溃了。无论出于何种原因,我无法让 /oauth/token
端点使用我提供的已定义 AuthenticationManager
或 AuthenticationProvider
。它始终默认为带有 AnonymousAuthenticationProvider
和 DaoAuthenticationProvider
的 AuthenticationManger
。
网络安全
这里没什么意思。我正在全局公开身份验证管理器,以尝试在 OAuth2 配置中注册它。我在一些注释代码中留下了一些我尝试过的其他东西,但它们几乎都完成了同样的事情:它适用于除 OAuth2 端点之外的所有东西。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public AuthenticationProvider customAuthenticationProvider;
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
// return new ProviderManager(Arrays.asList(customAuthenticationProvider));
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider);
}
// @Autowired
// public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// auth.authenticationProvider(customAuthenticationProvider);
// }
}
AuthServer
我已将其简化为我认为应该是正确的配置。我故意遗漏了 ClientDetailsService
,因为它是提供程序配置的一部分并且工作正常。我将忽略 TokenStore
并使用默认设置,直到我可以完成身份验证。同样,这里的 authenticationManager
应该是从 WebSecurity 公开的全局的。我也尝试在端点配置器中创建一个新的 ProviderManager
,但这对我也不起作用。
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
我也试过扩展 AuthorizationServerSecurityConfiguration
但没有成功:
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig2 extends AuthorizationServerSecurityConfiguration {
@Autowired
public AuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
}
我希望我的自定义 AuthenticationProvider
会显示在列表中,但它不会出现在我尝试过的所有内容中。
这可能是有意的,但如果是这样,提供自定义身份验证方案的正确方法是什么?
我真的很想避免它,但我真的需要实现自定义过滤器并绕过 Spring 提供的所有好东西吗?如果是,我该怎么做?
我最终为令牌端点配置了一个额外的 BasicAuthenticationFilter
。对我来说,这有点混乱,因为现在安全过滤器链中有两个相同的过滤器,但它确实有效。
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private UserService userService;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("isAuthenticated()")
.checkTokenAccess("isAuthenticated()");
// override the default basic authentication filter in order to provide
// a custom authentication manager
oauthServer.addTokenEndpointAuthenticationFilter(new BasicAuthenticationFilter(authenticationManager, new BasicAuthenticationEntryPoint()));
}
}
我正在我们的服务架构中编写一个身份验证服务,它基本上是 Spring OAuth2 授权服务器的一个实现。由此我需要根据许多不同的来源验证提供的凭据以支持遗留环境。
我主要专注于使用客户端凭据流程,用户(其他服务)将使用他们自己的凭据来获取令牌,并且暂时不需要 OAuth2 的授权或刷新。
我已经成功启动了 Spring 受 Spring 安全性 (@EnableWebSecurity
) 保护的启动应用程序。我还成功设置了授权服务器 (@EnableAuthorizationServer
),它提供了必要的端点 (/oauth/token
) 来提供令牌。我已经能够在内存中配置授权服务器并自定义 ClientDetailsService 以成功获取令牌。这一切只是为了向自己证明我可以得到一些东西。
我的问题是我需要根据自定义源验证凭据。我无法直接访问密码,当然也不知道它们是如何编码的。
在深入研究Spring代码后,我可以看到通过DaoAuthenticationProvider
,它通过调用PasswordEncoder.matches()
来完成身份验证。不幸的是,我没有从 ClientDetailsService
获得密码,我也不知道密码是如何编码的,所以自定义 PasswordEncoder
对我没有多大好处(我也需要匹配多种方式只有一种 PasswordEncoder
)。所以,我只剩下定义自己的 AuthenticationProvider
(或 AuthenticationManager
)。
我能够实现自己的 AuthenticationProvider
并将其提供给 Spring 安全配置。这非常有效,我能够将身份验证推迟到我自己的提供者,它可以做我认为合适的任何事情(例如委托给另一个服务来进行身份验证),但这只适用于非 OAuth2 端点。
现在一切都开始崩溃了。无论出于何种原因,我无法让 /oauth/token
端点使用我提供的已定义 AuthenticationManager
或 AuthenticationProvider
。它始终默认为带有 AnonymousAuthenticationProvider
和 DaoAuthenticationProvider
的 AuthenticationManger
。
网络安全
这里没什么意思。我正在全局公开身份验证管理器,以尝试在 OAuth2 配置中注册它。我在一些注释代码中留下了一些我尝试过的其他东西,但它们几乎都完成了同样的事情:它适用于除 OAuth2 端点之外的所有东西。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public AuthenticationProvider customAuthenticationProvider;
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
// return new ProviderManager(Arrays.asList(customAuthenticationProvider));
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider);
}
// @Autowired
// public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// auth.authenticationProvider(customAuthenticationProvider);
// }
}
AuthServer
我已将其简化为我认为应该是正确的配置。我故意遗漏了 ClientDetailsService
,因为它是提供程序配置的一部分并且工作正常。我将忽略 TokenStore
并使用默认设置,直到我可以完成身份验证。同样,这里的 authenticationManager
应该是从 WebSecurity 公开的全局的。我也尝试在端点配置器中创建一个新的 ProviderManager
,但这对我也不起作用。
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
我也试过扩展 AuthorizationServerSecurityConfiguration
但没有成功:
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig2 extends AuthorizationServerSecurityConfiguration {
@Autowired
public AuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
}
我希望我的自定义 AuthenticationProvider
会显示在列表中,但它不会出现在我尝试过的所有内容中。
这可能是有意的,但如果是这样,提供自定义身份验证方案的正确方法是什么?
我真的很想避免它,但我真的需要实现自定义过滤器并绕过 Spring 提供的所有好东西吗?如果是,我该怎么做?
我最终为令牌端点配置了一个额外的 BasicAuthenticationFilter
。对我来说,这有点混乱,因为现在安全过滤器链中有两个相同的过滤器,但它确实有效。
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private UserService userService;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("isAuthenticated()")
.checkTokenAccess("isAuthenticated()");
// override the default basic authentication filter in order to provide
// a custom authentication manager
oauthServer.addTokenEndpointAuthenticationFilter(new BasicAuthenticationFilter(authenticationManager, new BasicAuthenticationEntryPoint()));
}
}