Spring OAuth2 安全 - 客户端凭据 - 自定义 AuthenticationProvider

Posted

技术标签:

【中文标题】Spring OAuth2 安全 - 客户端凭据 - 自定义 AuthenticationProvider【英文标题】:Spring OAuth2 Security - Client Credentials - Custom AuthenticationProvider 【发布时间】:2019-07-06 16:14:33 【问题描述】:

我正在我们的服务架构中编写一个身份验证服务,它基本上是 Spring OAuth2 Authorization Server 的一个实现。因此,我需要针对许多不同的来源对提供的凭据进行身份验证,以支持旧环境。

我主要专注于使用客户端凭据流,用户(其他服务)将使用他们自己的凭据来获取令牌,并且暂时不需要 OAuth2 的授权或刷新。

我已经成功地启动了一个受 Spring Security (@EnableWebSecurity) 保护的 Spring Boot 应用程序。我还成功设置了授权服务器 (@EnableAuthorizationServer),它提供了必要的端点 (/oauth/token) 来提供令牌。我已经能够使用内存和自定义 ClientDetailsS​​ervice 配置授权服务器以成功获取令牌。这一切只是为了向自己证明我可以得到一些工作。

我的问题是我需要针对自定义来源对凭据进行身份验证。我没有直接访问密码的权限,当然也不知道密码是如何编码的。

翻阅Spring代码后发现,通过DaoAuthenticationProvider,它通过调用PasswordEncoder.matches()来完成认证。不幸的是,我没有从ClientDetailsService 获得密码,如果我这样做了,我也不知道密码是如何编码的,所以自定义PasswordEncoder 对我没有多大好处(我也需要以多种方式匹配,只有一个PasswordEncoder)。所以,我只能定义自己的 AuthenticationProvider(或 AuthenticationManager)。

我能够实现自己的 AuthenticationProvider 并将其提供给 Spring Security 配置。这非常有效,我能够将身份验证推迟到我自己的提供商,该提供商可以做任何我认为合适的事情(例如委托给另一个服务进行身份验证),但这仅适用于非 OAuth2 端点。

现在一切都开始崩溃了。无论出于何种原因,我都无法让/oauth/token 端点使用我提供的定义的AuthenticationManagerAuthenticationProviders。它始终默认为 AuthenticationMangerAnonymousAuthenticationProviderDaoAuthenticationProvider

网络安全

这里没什么意思。我正在全局公开身份验证管理器,试图在 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 提供的所有好东西吗?如果是,我该怎么做?

【问题讨论】:

【参考方案1】:

我最终为令牌端点配置了一个额外的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 安全 - 客户端凭据 - 自定义 AuthenticationProvider的主要内容,如果未能解决你的问题,请参考以下文章

Spring安全中的Oauth2客户端

仅具有客户端凭据的 Spring 安全端点(基本)

带有 spring-security 的 OAuth2 - 通过 HTTP 方法限制 REST 访问

来自 oauth2 安全休息 Web 服务的 Spring 休息服务 api 调用

通过 Spring Boot Keycloak 集成的 OAuth2 客户端凭据流

Spring Security Oauth2 - 向 AccessTokenRequest 添加凭据