运行Spring Boot oAuth2应用程序作为资源服务器并提供Web内容

时间:2018-05-13 08:49:10

标签: spring-boot spring-security spring-security-oauth2 pingfederate oidc

我使用Spring Boot 1.5.13以及Spring Security 4.2.6和Spring Security oAuth2 2.0.15。

我想为我们的Spring Boot应用程序找到最佳实践设置,这些应用程序提供一组混合内容:一个REST API,以及一些提供便利性和着陆页面的网页。对于有一些链接的开发人员,加上基于Swagger的API文档,这也是Web内容。

我有一个允许我使用适当的授权代码流运行应用程序的配置,因此我可以通过浏览器访问所有Web内容并通过配置的IdP(在我的情况下为PingFederate)进行身份验证,此外我可以从在浏览器中,即直接或与REST客户端,例如使用RESTClient。

这是我的安全配置:

@Slf4j
@Configuration
@EnableWebSecurity
@EnableOAuth2Sso // this annotation must stay here!
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login**", "/webjars/**", "/css/**").permitAll()
                .antMatchers("/cfhealth").permitAll()
                .antMatchers("/").permitAll()
                .antMatchers("/protected", "/api/**").authenticated();
    }

    @Bean
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }

}

和oAuth2配置:

@Configuration
@Slf4j
public class OAuth2Config extends ResourceServerConfigurerAdapter {

    @Value("${pingfederate.pk-uri}")
    String pingFederatePublicKeyUri;

    @Autowired
    PingFederateKeyUtils pingFederateKeyUtils;

    @Override
    public void configure(ResourceServerSecurityConfigurer config) {
        config.tokenServices(tokenServices());
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        String certificate = pingFederateKeyUtils.getKeyFromServer(pingFederatePublicKeyUri);
        String publicKey = pingFederateKeyUtils.extractPublicKey(certificate);
        converter.setVerifier(pingFederateKeyUtils.createSignatureVerifier(publicKey));
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }

}

但是,当我想以编程方式/在浏览器外部使用标头中的承载令牌调用REST API时,例如使用curl,授权代码流启动并重定向到本地登录端点。我想要的是API调用接受承载令牌进行身份验证,而不创建会话,并且浏览器中的所有Web内容/ mvc调用都会建立会话。

curl -i -H "Accept: application/json" -H "Authorization: Bearer $TOKEN" -X GET http://localhost:8080/authdemo/api/hello

将@EnableResourceServer注释添加到上面的SecurityConfig类中(并在应用程序属性文件中添加security.oauth2.resource.filter-order = 3,我可以使curl命令工作,但随后授权代码流被破坏,我在浏览器中为我的应用程序中的所有URL获得以下输出:

<oauth> <error_description> Full authentication is required to access this resource </error_description> <error>unauthorized</error> </oauth>

现在有办法让这个szenario很好地工作吗?如果是的话,那会是什么样子?或者仅在Spring Boot + Security + oAuth2的更高版本中支持它?

Spring Boot with Security OAuth2 - how to use resource server with web login form?的问题非常相似

1 个答案:

答案 0 :(得分:1)

我找到了解决方案:它需要多个HttpSecurity配置。我通过阅读Matt Raible在https://developer.okta.com/blog/2018/02/13/secure-spring-microservices-with-oauth写的伟大文章找到了他,他向我介绍了requestMatchers(。)的概念。这就是我最终实现它的方式:

compileSdkVersion 28
defaultConfig {
    applicationId "com.austurn.keikonew.keiko"
    minSdkVersion 15
    targetSdkVersion 28
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

通过这种方式,我可以使用浏览器访问该服务,从而获得授权代码流。但访问API(或实际上是服务的任何部分)会导致验证提供的承载令牌。

为了说明在这种情况下如何将某些端点排除/公开的方式,这里是我如何配置执行器端点以及我自己添加的一个非常简单的“ping”端点:

 @Configuration
 @EnableResourceServer
 @EnableWebSecurity(debug = true)
 @EnableOAuth2Sso
 public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

     @Bean
     public RequestContextListener requestContextListener() {
         return new RequestContextListener();
     }

     @Override
     public void configure(HttpSecurity http) throws Exception {
         http
             .requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
             .authorizeRequests().anyRequest().fullyAuthenticated();
     }

 }

我的/ cfhealth端点的实现:

 @Configuration
 @Order(1)
 public class ActuatorSecurity extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatcher(new OrRequestMatcher(EndpointRequest.to("health", "info"),
                 new AntPathRequestMatcher("/cfhealth"))).authorizeRequests().anyRequest().permitAll();
    }

 }

我很高兴向其他人学习,如果这是Spring Security配置的最佳实践方式,或者有更好的方法可以做到这一点。在过去的几周里,我花了很多时间研究这个主题,并且需要花费一些精力来掌握基本的Spring Security概念。