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

标签 spring-boot spring-security spring-security-oauth2 pingfederate openid-connect

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

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

我有一个配置,允许我使用适当的授权代码流运行应用程序,因此我可以通过浏览器访问所有 Web 内容并通过配置的 IdP(在我的情况下为 PingFederate)进行身份验证,此外我可以从浏览器中进行 API 调用,即直接或使用 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;
    }

}

但是,当我想以编程方式/在浏览器外部使用 header 中的不记名 token 调用 REST API 时,例如使用 curl,授权代码流开始并重定向到本地登录端点。我想要的是 API 调用接受承载 token 进行身份验证,而不创建 session ,并且浏览器中的所有 Web 内容/mvc 调用都建立一个 session 。
curl -i -H "Accept: application/json" -H "Authorization: Bearer $TOKEN" -X GET http://localhost:8080/authdemo/api/hello

在上面的SecurityConfig类中添加@EnableResourceServer注解(并在应用程序属性文件中添加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?很相似

最佳答案

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

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

 }

有了它,我可以使用浏览器访问该服务,从而导致授权代码流。但是访问 API(或实际上是服务的任何部分)会导致对所提供的 Bearer token 进行验证。

为了说明在这种情况下如何排除/公开某些端点的方式,以下是我配置执行器端点和我自己添加的一个非常简单的“ping”端点的方法:
 @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();
    }

 }

我对/cfhealth 端点的实现:
 @Controller
 @Slf4j
 public class MainController {

     @GetMapping(value = "/cfhealth")
     @ResponseBody
     public String cfhealth() {
         return "ok";
     }

 }

如果这是 Spring Security 配置的最佳实践方式或者是否有更好的方法,我很高兴向他人学习。在过去的几周里,我在这个主题上花了很多时间,掌握基本的 Spring Security 概念需要付出相当多的努力。

关于spring-boot - 运行 Spring Boot oAuth2 应用程序作为资源服务器并提供 Web 内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50314361/

相关文章:

java - Spring 安全秒 :authorize tag using Java Config

spring - 身份验证在 Spring Boot 1.5.2 和 Oauth2 中不起作用

spring-security - 是否有更简单的方法来加载 Spring OAuth 客户端配置

java - Spring Boot 在 Debug模式下阻止 H2 控制台

java - 交易注解错误

java - Spring/Hibernate 连接泄漏与 ScrollableResults

java - Spring BOOT 错误

spring - 拦截 url 模式/** 导致 404 错误

heroku - 配置在Heroku上使用的Spring TextEncryptor的正确方法是什么

java - 如何在 Spring Boot 2 oauth2 中获取 token ?