简体   繁体   English

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

[英]Spring OAuth2 Security - Client Credentials - Custom AuthenticationProvider

I am writing an authentication service in our service architecture that will basically be an implementation of Spring OAuth2 Authorization Server. 我正在我们的服务体系结构中编写身份验证服务,该服务基本上将是Spring OAuth2 Authorization Server的实现。 From this I will need to authenticate provided credentials against many different sources to support a legacy environment. 因此,我将需要针对许多不同的来源对提供的凭据进行身份验证,以支持旧版环境。

I am primarily concentrated on using the client credentials flow where users (other services) will use their own credentials to obtain a token and, for the time being, will not require OAuth2's authorize or refresh. 我主要专注于使用客户端凭据流,其中用户(其他服务)将使用他们自己的凭据来获取令牌,并且暂时不需要OAuth2的授权或刷新。

I have successfully been able to spin up a Spring Boot application that is protected with Spring Security ( @EnableWebSecurity ). 我已经能够成功启动受Spring Security( @EnableWebSecurity )保护的Spring Boot应用程序。 I have also successfully set up the Authorization Server ( @EnableAuthorizationServer ) which provides the necessary endpoints ( /oauth/token ) to provide a token. 我还成功设置了授权服务器( @EnableAuthorizationServer ),该服务器提供了必要的端点( /oauth/token )以提供令牌。 I have been able to configure the Authorization Server with in memory and custom ClientDetailsService to successfully get a token. 我已经能够使用内存和自定义ClientDetailsS​​ervice配置授权服务器以成功获取令牌。 This is all just to demonstrate to myself that I can get something working. 这一切只是为了向自己证明我可以做些工作。

My problem is that I need to authenticate the credentials against a custom source. 我的问题是我需要根据自定义来源对凭据进行身份验证。 I do not have direct access to the passwords and certainly do not know how they are encoded. 我没有直接访问密码的权限,当然也不知道它们的编码方式。

After digging through the Spring code, I could see that through the DaoAuthenticationProvider , it accomplishes authentication by calling PasswordEncoder.matches() . 深入研究Spring代码之后,我可以看到通过DaoAuthenticationProvider ,它通过调用PasswordEncoder.matches()完成认证。 Unfortunately, I do not get a password from the ClientDetailsService , nor do I know how the password is encoded if I did, so a custom PasswordEncoder does not do me much good (also I would need match in multiple ways with only one PasswordEncoder ). 不幸的是,我没有从ClientDetailsService获得密码,也不知道密码是如何编码的,因此自定义的PasswordEncoder不能给我带来多大好处(而且我只需要一个PasswordEncoder就需要以多种方式进行匹配)。 So, I'm left with defining my own AuthenticationProvider (or AuthenticationManager ). 因此,我要定义自己的AuthenticationProvider (或AuthenticationManager )。

I was able to implement my own AuthenticationProvider and provide that to the Spring Security configuration. 我能够实现自己的AuthenticationProvider并将其提供给Spring Security配置。 This worked perfectly and I was able to defer the authentication to my own provider which can do whatever I see fit (such as delegating to another service to authenticate), but this only worked for the non-OAuth2 endpoints. 这可以很好地工作,并且我能够将身份验证延迟到我自己的提供程序,后者可以执行我认为合适的任何事情(例如,委派给另一个服务进行身份验证),但这仅适用于非OAuth2端点。

Now this is where everything starts to fall apart. 现在这是一切开始崩溃的地方。 For whatever reason, I cannot get the /oauth/token endpoint to use the defined AuthenticationManager or AuthenticationProvider s that I supply. 无论出于什么原因,我都无法使/oauth/token端点使用我提供的已定义的AuthenticationManagerAuthenticationProvider It always defaults to an AuthenticationManger with AnonymousAuthenticationProvider and DaoAuthenticationProvider . 它始终默认为带有AnonymousAuthenticationProviderDaoAuthenticationProviderAuthenticationManger

WebSecurity WebSecurity

Not much interesting here. 这里没什么有趣的。 I'm exposing the authentication manager globally in an attempt to register it in the OAuth2 configuration. 我正在全局公开身份验证管理器,以尝试在OAuth2配置中注册它。 I've left in some commented code to show some other things I've tried, but they all pretty much accomplish the same thing: it works with everything but the OAuth2 endpoints. 我留下了一些注释的代码来展示我尝试过的其他事情,但是它们几乎都完成了同样的事情:除了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 AuthServer

I've stripped this down to what I believe should be the correct configuration. 我将其简化为我认为应该是正确的配置。 I've purposefully left out ClientDetailsService as it's part of the provider configuration and it is working properly. 我故意省略了ClientDetailsService因为它是提供程序配置的一部分,并且工作正常。 I'm ignoring the TokenStore and using the defaults until I can get authentication out of the way. 在忽略身份验证之前,我将忽略TokenStore并使用默认值。 Again, the authenticationManager here should be the global one exposed from WebSecurity. 同样,这里的authenticationManager应该是WebSecurity公开的全局管理器。 I've also tried creating a new ProviderManager in the endpoints configurer, but that also did not work for me. 我也尝试在端点配置器中创建一个新的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);
    }
}

I've also tried extending AuthorizationServerSecurityConfiguration without success: 我也曾尝试扩展AuthorizationServerSecurityConfiguration但没有成功:

@Configuration
@EnableAuthorizationServer
public class AuthServerConfig2 extends AuthorizationServerSecurityConfiguration {

    @Autowired
    public AuthenticationProvider customAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider);
    }
}

I would expect my custom AuthenticationProvider would show in the list, but it will not with everything I've tried. 我希望我的自定义AuthenticationProvider会显示在列表中,但不会包含我尝试过的所有内容。

This may be intentional, but if that is the case, what is the correct way to go about providing custom authentication schemes? 这可能是有意为之,但如果是这种情况,提供自定义身份验证方案的正确方法是什么?

I would really like to avoid it, but am I truly required to implement a custom filter and bypass all of the nice things Spring provides? 我真的很想避免它,但是我真的需要实现一个自定义过滤器并绕过Spring提供的所有好东西吗? If yes, how would I go about doing that? 如果是,我将如何去做?

I ended up configuring an additional BasicAuthenticationFilter to the token endpoint. 我最终为令牌端点配置了另一个BasicAuthenticationFilter To me, this is a little messy as now there are two of the same filters in the Security Filter chain, but it does the trick. 对我来说,这有点混乱,因为现在“安全筛选器”链中有两个相同的筛选器,但是可以解决问题。

@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()));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM