将Spring Boot oAuth2应用程序作为资源服务器运行并提供Web内容 [英] Run a Spring Boot oAuth2 application as resource server AND serving web content

查看:220
本文介绍了将Spring Boot oAuth2应用程序作为资源服务器运行并提供Web内容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using Spring Boot 1.5.13 and with that Spring Security 4.2.6 and Spring Security oAuth2 2.0.15.

我想为我们的Spring Boot应用程序找到最佳实践设置,该应用程序提供混合的内容集:REST API,以及一些为开发人员提供方便的着陆页"的网页,上面带有一些链接以及Swagger基于API的文档,它也是Web内容.

I want to find a best practice setup for our Spring Boot applications that serve a mixed set of content: A REST API, and some web pages that provide a convenience "landing page" for developers with some links on it, plus Swagger based API documentation, which is also web content.

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

I have a configuration that allows me to run the app with proper authorization code flow, hence I can access all web content via Browser and get authenticated by the configured IdP (in my case PingFederate), plus I can make API calls from within the Browser, i.e. directly or with a REST Client, e.g. with 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调用都建立一个会话.

But when I want to call a REST API programmatically/outside the Browser with a bearer token in the header, e.g. with curl, the authorization code flow kicks in and redirects to the local login endpoint. What I want is that API calls accept the bearer token for authentication, without creating a session, and that all web content/mvc calls in the Browser establish a 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都得到以下输出:

Adding the @EnableResourceServer annotation to the above SecurityConfig class (and adding security.oauth2.resource.filter-order=3 in the application properties file, I can make the curl command work, but then the authorization code flow is broken, I get the following output in the Browser for all URLs in my application:

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

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

现在有没有办法使此szenario正常工作?如果是,那会是什么样子?还是仅在更高版本的Spring Boot + Security + oAuth2中受支持?

Now is there a way to get this szenario working nicely? If yes, how would that look like? Or is it only supported in later versions of Spring Boot+Security+oAuth2?

位于推荐答案

我找到了解决方案:它需要多个HttpSecurity配置.我是通过阅读Matt Raible撰写的精彩文章找到的,该文章位于

I found the solution: It takes multiple HttpSecurity configurations. I found out by reading the great article written by Matt Raible at https://developer.okta.com/blog/2018/02/13/secure-spring-microservices-with-oauth where he introduced me to the notion of requestMatchers(.). This is how I finally implemented it:

 @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令牌的验证.

With that I can access the service with a Browser, leading to a authorization code flow. But accessing the API (or actually any part of the service) leads to a validation of the provided Bearer token.

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

And to illustrate the way how some endpoints can be exluded/made public in such a case, here's how I configure the actuator endpoints and one very simple 'ping' endpoint I've added myself:

 @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端点实现:

And my implementation of the /cfhealth endpoint:

 @Controller
 @Slf4j
 public class MainController {

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

 }

如果这是Spring Security配置的最佳实践方法,或者有更好的方法,我很乐意向其他人学习.在过去的几周中,我在该主题上花费了很多时间,并且花了相当多的精力来掌握Spring Security的基本概念.

I'm happy to learn from others if that's the best practice way of Spring Security configuration or if there are better ways to do it. I've spent quite some time on the topic in the last few weeks on it, and it takes quite some effort to grasp the basic Spring Security concepts.

这篇关于将Spring Boot oAuth2应用程序作为资源服务器运行并提供Web内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆