如何将 spring-security-oauth 服务器添加到具有经典身份验证的现有 Web 应用程序?

How to add spring-security-oauth server to an existing web application with classic authentication?

我有一个基于经典 cookie 的身份验证的 Web 应用程序。这是它的配置方式:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserService userService;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint());

        http.authorizeRequests()
            .antMatchers("/some/restricted/urls*").access("hasRole('admin')")
            .antMatchers("/some/public/urls").permitAll()
            .antMatchers("/**").access("hasRole('registered')");
    }

    @Autowired
    protected void configureAuthenticationManager(final AuthenticationManagerBuilder auth) throws Exception {
        auth
            .userDetailsService(userService)
            .passwordEncoder(passwordEncoder);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

现在我想启用外部应用程序以使用 OAuth2 访问我的一些用户数据。首先,我像这样添加身份验证:

@Configuration
@EnableAuthorizationServer
public class OAuthServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Autowired
    private AuthenticationManager auth;

    @Bean
    public JdbcTokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Bean
    protected AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    @Bean
    public JdbcClientDetailsService clientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        final ApprovalStoreUserApprovalHandler approvalHandler = new ApprovalStoreUserApprovalHandler();
        approvalHandler.setClientDetailsService(clientDetailsService());

        endpoints
            .authorizationCodeServices(authorizationCodeServices())
            .authenticationManager(auth)
            .tokenStore(tokenStore())
            .userApprovalHandler(approvalHandler)
            .pathMapping("/oauth/confirm_access", "/api/oauth2/confirm_access")
            .pathMapping("/oauth/token", "/api/oauth2/token")
            .pathMapping("/oauth/check_token", "/api/oauth2/check_token")
            .pathMapping("/oauth/token_key", "/api/oauth2/token_key")
            .pathMapping("/oauth/authorize", "/api/oauth2/authorize");
    }
}

一切似乎都运行良好。我的网络应用程序总体上似乎可以正常工作,我可以获得代码并将其交换为访问令牌。仅访问令牌本身是无用的,因此我想添加一项需要有效访问令牌的服务。为此,我添加了以下内容:

@Configuration
@EnableResourceServer
public class Oauth2ResourcesConfigurationAdapter extends ResourceServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager auth;
    @Autowired
    private OAuthServerConfig oAuthServerConfig;

    @Override
    public void configure(final ResourceServerSecurityConfigurer resources) throws Exception {
        resources
            .authenticationManager(auth)
            .tokenStore(oAuthServerConfig.tokenStore());
    }
}

然而这让我例外:

Caused by: java.lang.IllegalStateException: Cannot apply org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer@6dd1c3ed to already built object
    at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.add(AbstractConfiguredSecurityBuilder.java:192)

我阅读了调试过的源代码并 google 尽我所能,但我就是无法弄清楚这里发生了什么。我发现的最接近的提示是 this ticket on GitHub,但是它没有对问题进行解释,而且我按照票证中的建议编写自己的配置非常不成功,可能是因为我不知道问题是什么是,因此我需要改变。

同时 robsilvia 在上面列出的 github 工单中给出了答案。简而言之,它说,必须将此方法添加到 Oauth2ResourcesConfigurationAdapter

@Override
public void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().authenticated();
}

然而,这导致我的 oauth 配置干扰了我的经典 Web 应用程序安全配置,该配置不再正常工作。但是我确实发现了 "multiple HttpSecurity",因此我最终通过使用这个变体解决了这个问题:

@Override
public void configure(HttpSecurity http) throws Exception {
    http
        .requestMatcher(new OrRequestMatcher(
            new AntPathRequestMatcher("/path/to/oauth/endpoints/*"),
            new AntPathRequestMatcher("/oauth/protected/resource")
        ))
        .authorizeRequests().anyRequest().authenticated();
}