Utilice rutas diferentes para recursos públicos y privados Jersey + Spring boot

Estoy usando Spring boot + Jersey + Spring security, quiero tener puntos finales públicos y privados, quiero un esquema de la siguiente manera:

  • / rest – Mi contexto raíz
  • / public – Quiero colocar mis puntos finales públicos en este contexto, debe estar dentro del contexto raíz como /rest/public/pings
  • / private – Quiero colocar mis endpoints privados en este contexto, debe estar dentro del contexto raíz como /rest/private/accounts

Tengo mi configuración de la siguiente manera:

Configuración de Jersey :

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

Configuración de seguridad de 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(); } } 

La pregunta es ¿cómo puedo registrar dos rutas de aplicación dentro de mi contexto / rest, una para / public y la otra para / private?

NOTA: Intenté crear otro ResourceConfig de la siguiente manera:

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

Pero estoy obteniendo el siguiente error:

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

Gracias por tu ayuda 🙂

En un contenedor de servlet, el tiempo de ejecución de Jersey se ejecuta como un servlet o como un filtro de servlet. La manera en que Spring Boot configura los servlets y los filtros es a través de ServletRegistrationBean y FilterRegistrationBeans , respectivamente. Para tener una idea de cómo funciona esa configuración detrás de las escenas, puede ver el código fuente de JerseyAutoConfiguration

En JerseyAutoConfiguration , puede ver que se inyecta ResourceConfig , que es el ResourceConfig utilizado para crear el servlet Jersey o el filtro Jersey (según la configuración que elija). Entonces, la razón del error es que no puede tener beans ambiguos, que tiene dos beans ResourceConfig . Entonces Spring no sabe cuál inyectar.

Sin embargo, lo que puede hacer es usar dos servlets diferentes para cada ResourceConfig . El problema es que Spring Boot solo te conecta con un servlet para Jersey, por lo que debes configurar el otro tú mismo. Hay dos opciones:

  1. Use la configuración automática Spring Boot para una de las aplicaciones de Jersey y agregue otra ServletRegistrationBean para la otra. Una cosa a tener en cuenta es que ResourceConfig para su ServletRegistrationBean creado no debe ser un componente de Spring (es decir, no @Component o @Configuration ), de lo contrario, seguirá enfrentando el mismo 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; } 
  2. No use la configuración Spring Boot en absoluto. Solo crea dos ServletRegistrationBean s. En este caso, ninguna de sus clases ResourceConfig debería ser 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; } 

Personalmente, prefiero la segunda opción, ya que es más fácil razonar sobre las configuraciones cuando están todas en un solo lugar.

Otra cosa a tener en cuenta es que las dos aplicaciones de Jersey serán completamente independientes, lo que significa que tendrá que registrar proveedores (como filtros) para ambas aplicaciones

No se le permitirá crear dos beans para su clase de recursos. También puedes lograr lo que estás tratando de lograr usando una única clase de recursos.

Aquí hay un ejemplo:

 @Path("rest") public class SampleResourceClass { @Path("/public/pings") @GET public Responce getPings(){ /* Code Here */ } @Path("/private/accounts") @GET public Response getAccounts(){ /* Code Here */ } } 

El error que está viendo no está relacionado con su configuración de seguridad, es posible que desee echar un vistazo a este ticket, https://github.com/spring-projects/spring-boot/issues/3260

Si desea permitir todo el tráfico a puntos finales pasados /public , puede agregar RequestMatcher a la lista de RequestMatcher de 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