【问题标题】:Run a Spring Boot oAuth2 application as resource server AND serving web content将 Spring Boot oAuth2 应用程序作为资源服务器运行并提供 Web 内容
【发布时间】:2018-10-23 04:07:29
【问题描述】:

我正在使用 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)进行身份验证,另外我可以从在浏览器中,即直接或使用 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

在上面的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>

现在有没有办法让这个场景很好地工作?如果是,那会是什么样子?还是只有更高版本的Spring Boot+Security+oAuth2才支持?

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

【问题讨论】:

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


    【解决方案1】:

    我找到了解决方案:它需要多个 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 令牌的验证。

    为了说明在这种情况下如何排除/公开某些端点,下面是我如何配置执行器端点和我自己添加的一个非常简单的“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 概念需要相当多的努力。

    【讨论】:

    • 我也在努力让同一个应用程序作为资源服务器和 SSO 客户端工作。为什么你的ResourceServerConfig 类用EnableOAuth2Sso 注释? Oauth2 客户端部分没有单独的配置吗?
    猜你喜欢
    • 2022-08-03
    • 1970-01-01
    • 2021-02-07
    • 2019-04-17
    • 2019-02-26
    • 1970-01-01
    • 2014-09-15
    • 2020-03-16
    • 2019-02-27
    相关资源
    最近更新 更多