公共和私人资源使用不同的路径Jersey + Spring启动
我正在使用Spring boot + Jersey + Spring安全性,我想拥有公共和私有端点,我想要一个架构如下:
- / rest – 我的根上下文
- / public – 我想将我的公共端点放在这个上下文中,它必须在
/rest/public/pings
类的根上下文中 - / private – 我想将我的私有端点放在这个上下文中,它必须在
/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(); } }
问题是如何在my / rest上下文中注册两个应用程序路径,一个用于/ public,另一个用于/ private?
注意:我尝试创建另一个ResourceConfig,如下所示:
@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或servletfilter运行。 spring boot如何配置servlet和filter分别是通过ServletRegistrationBean
和FilterRegistrationBeans
。 要了解该配置如何在幕后工作,您可以查看JerseyAutoConfiguration
的源代码
在JerseyAutoConfiguration
,您可以看到注入了ResourceConfig
,这是用于创建Jersey servlet或Jerseyfilter的ResourceConfig
(取决于您选择的配置)。 所以错误的原因是你不能有不明确的bean,你有两个ResourceConfig
bean。 所以Spring不知道要注入哪一个。
但是你可以做的是为每个ResourceConfig
使用两个不同的servlet。 问题是Spring Boot只为你挂了一个针对Jersey的servlet,所以你需要自己配置另一个servlet。 有两种选择:
-
对其中一个Jersey应用程序使用Spring Boot自动配置,并为另一个应用程序添加另一个
ServletRegistrationBean
。 需要注意的一点是,创建的ServletRegistrationBean
的ResourceConfig
不应该是Spring组件(即没有@Component
或@Configuration
),否则您仍将面临同样的错误。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。@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; }
就个人而言,我更喜欢第二种选择,因为当它们都在一个地方时,更容易推断配置。
另外需要注意的是,两个Jersey应用程序将完全独立,这意味着您需要为两个应用程序注册提供程序(如filter)
您将不被允许为您的资源类创建两个bean。 您也可以使用单个资源类来实现您想要实现的目标。
这是一个例子:
@Path("rest") public class SampleResourceClass { @Path("/public/pings") @GET public Responce getPings(){ /* Code Here */ } @Path("/private/accounts") @GET public Response getAccounts(){ /* Code Here */ } }
您看到的错误与您的安全配置无关,您可能需要查看此票证, https://github.com/spring-projects/spring-boot/issues/3260
如果要允许所有到端点/public
端点的流量,可以将RequestMatcher
添加到Spring Security忽略列表中。
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/rest/public/**"); } protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatcher("/rest/private/**") .anyRequest().authenticated().and() .httpBasic().and() .csrf().disable() } }
http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc