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

时间:2019-02-12 22:50:59

标签: java spring authentication spring-security spring-security-oauth2

我正在我们的服务体系结构中编写身份验证服务,该服务基本上将是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端点使用我提供的已定义的AuthenticationManagerAuthenticationProvider。它始终默认为带有AuthenticationMangerAnonymousAuthenticationProvider的{​​{1}}。

WebSecurity

在这里没什么有趣的。我正在全局公开身份验证管理器,以尝试在OAuth2配置中注册它。我留下了一些注释的代码来展示我尝试过的其他事情,但是它们几乎都完成了同样的事情:除OAuth2端点外,它都适用于所有其他事物。

DaoAuthenticationProvider

AuthServer

我将其简化为我认为应该是正确的配置。我有意省略了@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); // } } ,因为它是提供程序配置的一部分,并且工作正常。在忽略身份验证之前,我将忽略ClientDetailsService并使用默认值。同样,这里的TokenStore应该是WebSecurity公开的全局变量。我还尝试在端点配置器中创建一个新的authenticationManager,但这对我也不起作用。

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); } } 会显示在列表中,但不会包含我尝试过的所有内容。

这可能是故意的,但如果是这种情况,提供自定义身份验证方案的正确方法是什么?

我真的很想避免它,但是我真的需要实现一个自定义过滤器并绕过Spring提供的所有好东西吗?如果是,我该怎么做?

1 个答案:

答案 0 :(得分:0)

我最终为令牌端点配置了额外的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()));
    }
}