对公共和私有资源使用不同的路径 Jersey + Spring boot

2022-01-21 00:00:00 rest spring java spring-boot jersey

我正在使用 Spring boot + Jersey + Spring security,我想要公共和私有端点,我想要如下架构:

I'm using Spring boot + Jersey + Spring security, I want to have public and private endpoints, I want an schema as follow:

  • /rest -- 我的根上下文
  • /public -- 我想将我的公共端点放在这个上下文中,它必须在根上下文中,例如 /rest/public/pings
  • /private -- 我想将我的私有端点放在这个上下文中,它必须在根上下文中,例如 /rest/private/accounts
  • /rest -- My root context
  • /public -- I want to place my public endpoints in this context, It must be inside of the root context like /rest/public/pings
  • /private -- I want to place my private endpoints in this context, It must be inside of the root context like /rest/private/accounts

我的配置如下:

球衣配置:

@Configuration
@ApplicationPath("/rest")
public class RestConfig extends ResourceConfig {
    public RestConfig() {
        register(SampleResource.class);
    }
}

Spring 安全配置:

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

........

    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/rest/public/**").permitAll();
        http.antMatcher("/rest/**").authorizeRequests().anyRequest().fullyAuthenticated().and().httpBasic();
        http.csrf().disable();
    }

}

问题是如何在我的/rest 上下文中注册两个应用程序路径,一个用于/public,另一个用于/private?

The question is how can I register two application paths inside of my /rest context, one for /public and the other one for /private ?

注意:我尝试创建另一个 ResourceConfig,如下所示:

NOTE: I tried to create another ResourceConfig as follow:

@Configuration
@ApplicationPath("/rest/public")
public class RestPublicConfig extends ResourceConfig{
    public RestPublicConfig() {
        register(PingResource.class);
    }
}

但我收到下一个错误:

 No qualifying bean of type [org.glassfish.jersey.server.ResourceConfig] is defined: expected single matching bean but found 2: restConfig,restPublicConfig

感谢您的帮助:)

推荐答案

在 servlet 容器中,Jersey 运行时作为 servlet 或 servlet 过滤器运行.spring boot 是如何配置 servlet 和过滤器的,分别是通过 ServletRegistrationBeans 和 FilterRegistrationBeans.要了解该配置如何在幕后工作,您可以查看 JerseyAutoConfiguration

In a servlet container, the Jersey runtime, runs as either a servlet or as a servlet filter. How spring boot configures servlets and filters is through ServletRegistrationBeans and FilterRegistrationBeans, respectively. To get an idea of how that configuration works behind scenes, you can look at the source code for the JerseyAutoConfiguration

JerseyAutoConfiguration中可以看到注入了一个ResourceConfig,也就是用于创建Jersey servlet或Jersey的ResourceConfig过滤器(取决于您选择的配置).所以错误的原因是你不能有模棱两可的bean,你有两个ResourceConfig bean.所以 Spring 不知道该注入哪一个.

In the JerseyAutoConfiguration, you can see that a ResourceConfig is injected, and that is the ResourceConfig used to create the Jersey servlet or Jersey filter (depending on your choice of configuration). So the reason for the error is that you can't have ambiguous beans, which you have two ResourceConfig beans. So Spring doesn't know which one to inject.

您可以 做的是为每个ResourceConfig 使用两个不同的servlet.问题是 Spring Boot 只为 Jersey 连接了一个 servlet,因此您需要自己配置另一个.有两种选择:

What you can do though, is use two different servlets for each ResourceConfig. The problem is that Spring Boot only hooks you up with one servlet for Jersey, so you need to configure the other one yourself. There are two options:

  1. 为其中一个 Jersey 应用程序使用 Spring Boot 自动配置,并为您的另一个应用程序添加另一个 ServletRegistrationBean.需要注意的一点是,您创建的 ServletRegistrationBeanResourceConfig 不应是 Spring 组件(即没有 @Component@配置),否则你仍然会面临同样的错误.

  1. Use the Spring Boot auto-configuration for one of the Jersey applications, and add another ServletRegistrationBean for your other one. The one thing to note is that the ResourceConfig for your created ServletRegistrationBean should not be a Spring component (i.e. no @Component or @Configuration), or else you will still face the same error.

public class PublicConfig extends ResourceConfig {
    public PublicConfig() {
        register(PingResource.class);
    }
}
...
// in your Spring Boot configuration class
@Bean
public ServletRegistrationBean publicJersey() {
    ServletRegistrationBean publicJersey 
            = new ServletRegistrationBean(new ServletContainer(new PublicConfig()));
    publicJersey.addUrlMappings("/rest/public/*");
    publicJersey.setName("PublicJersey");
    publicJersey.setLoadOnStartup(0);
    return publicJersey;
}

  • 根本不要使用 Spring Boot 配置.只需创建两个 ServletRegistrationBean.在这种情况下,您的 ResourceConfig 类都不应该是 Spring bean.

  • Don't use the Spring Boot configuration at all. Just create two ServletRegistrationBeans. In this case, none of your ResourceConfig classes should be Spring beans.

    @Bean
    public ServletRegistrationBean publicJersey() {
        ServletRegistrationBean publicJersey 
                = new ServletRegistrationBean(new ServletContainer(new PublicConfig()));
        publicJersey.addUrlMappings("/rest/public/*");
        publicJersey.setName("PublicJersey");
        publicJersey.setLoadOnStartup(0);
        return publicJersey;
    }
    
    @Bean
    public ServletRegistrationBean privateJersey() {
        ServletRegistrationBean privateJersey 
               = new ServletRegistrationBean(new ServletContainer(new PrivateConfig()));
        privateJersey.addUrlMappings("/rest/private/*");
        privateJersey.setName("PrivateJersey");
        privateJersey.setLoadOnStartup(1);
        return privateJersey;
    }
    

  • 就个人而言,我更喜欢第二种选择,因为当它们都在一个地方时,更容易推理配置.

    Personally, I prefer the second option, as it is easier to reason about the configurations when they are all in one place.

    另外需要注意的是,两个 Jersey 应用程序将完全独立,这意味着您需要为两个应用程序注册提供程序(如过滤器)

    Another thing to note is that the two Jersey applications will be completely independent, meaning you will need to register providers (like filters) for both applications

    相关文章