Spring - Share UserDetailsService across WebMVC & Security - java

I'm implementing a web application with some basic login mechanism using Spring & Spring Security. All fine, but I'm facing a question (maybe it's not a problem, let's see).
My WebApp is structured like this (No XML Config, Servlet-3.0 and Spring 4.0):
WebAppInitializer
Intention: Configure WebApplication
public class WebAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(AppConfig.class);
applicationContext.refresh();
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(Dispatcher);
context.setParent(applicationContext);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher-servlet", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
}
}
SecurityWebApplicationInitializer.java
Intention: Add Security
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
There are also classes:
AppConfig (#Configuration, #ComponentScan for xyz.services)
DispatcherConfig (#Configuration, #EnableWebMVc and #ComponentScan for xyz.controller)
SecurityConfig (#Configuration, #EnableWebSecurity, #ComponentScan for xyz.services)
CustomerUserDetailsService implementing UserDetailsService (#Service)
My problem/question:
I want to use the * CustomerUserDetailsService* in Security for the AuthenticationManagerBuilder and in my controllers - is there a way to have only one instance of the UserDetailsService? In this configuration it will create two instances: one for the Application Context and one for the Security Context. Did I miss anything in the configuration? Or is this the favourite way to have it configured?
Another question beside that is: is there any way to use this configuration supporting #Secured annotation on my controller? There was no way to get this working.

Related

Why classes extending WebSecurityConfigurerAdapter and not declaring any bean are annotated with configuration?

According to #Configuration documentation:
Indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime, for example:
#Configuration
public class AppConfig {
#Bean
public MyBean myBean() {
//instantiate, configure and return bean ...
}
}
As I remember always I came across classes extending WebSecurityConfigurerAdapter which didn't contain any #Bean methods and were annotated with #Configuration.
It is even in official blog and some examples, see:
https://spring.io/blog/2013/07/03/spring-security-java-config-preview-web-security
#Configuration
#EnableWebSecurity
public class HelloWebSecurityConfiguration
extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
or here: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html
#Order(1) 2
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") 3
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("ADMIN")
)
.httpBasic(withDefaults());
}
}
Why this classes are annotated with #Configuration even though there are no #Bean methods?
Beans are imported using the secondary "#Enable" annotation
Spring features such as asynchronous method execution, scheduled task execution, annotation driven transaction management, and even Spring MVC can be enabled and configured from #Configuration classes using their respective "#Enable" annotations. See #EnableAsync, #EnableScheduling, #EnableTransactionManagement, #EnableAspectJAutoProxy, and #EnableWebMvc for details.
from EnableWebSecurity:
Add this annotation to an #Configuration class to have the Spring Security configuration defined in any WebSecurityConfigurer or more likely by extending the WebSecurityConfigurerAdapter base class and overriding individual methods:

How to register MessageDispatcherServlet in a classic (non Boot) Spring application

I am currently in the process of developing a web application that is accessible via SOAP using Spring 5.1.3 with Spring-WS and have no clue how to register an additional servlet (in this case, MessageDispatcherServlet for Spring-WS) using Java config. I should note that this is a non Boot application.
I consulted the official Spring docs for help, however this guide is geared towards Spring Boot (which uses ServletRegistrationBean, which is exclusive to Spring Boot). According to the guide, the MessageDispatcherServlet is registered like this:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
}
This looks nice and straightforward apart from the fact that ServletRegistrationBean resides in org.springframework.boot.web.servlet => Spring Boot => not available to me.
How do I register my MessageDispatherServlet in a "non Boot" standard Spring application? Thanks a lot for any hints or advice.
Thanks for any pointers everyone. I managed to register the MessageDispatcherServlet via WebApplicationInitializer:
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(WebConfig.class);
container.addListener(new ContextLoaderListener(context));
// Message Dispatcher Servlet (SOAP)
MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
messageDispatcherServlet.setApplicationContext(context);
messageDispatcherServlet.setTransformWsdlLocations(true);
ServletRegistration.Dynamic messageDispatcher = container.addServlet("messageDispatcher", messageDispatcherServlet);
messageDispatcher.setLoadOnStartup(1);
messageDispatcher.addMapping("/ws/*");
}
}
Pretty straightforward once you know how to do it :D

Is #Autowired required on configureGlobal(..) when using #EnableWebSecurity?

In this example there is an #Autowired annotation on the configureGlobal method:
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
Is that necessary or does Spring automatically inject the AuthenticationBuilder on methods annotated with #EnableWebSecurity??
The code snippet is pulled from when-to-use-spring-securitys-antmatcher
According to Spring documentation #EnableWebSecurity is an annotation that only switches off the default web application security configuration in order to let you add some custom features like the configureGlobal.
configureGlobal should be #Autowired in order to get the AuthenticationManagerBuilder bean and define the authentication type for the application.
In conclusion #EnableWebSecurity doesn't inject beans, it only provides a way to customize the web security application.
#EnableWebSecurity

Use different paths for public and private resources Jersey + Spring boot

I'm using Spring boot + Jersey + Spring security, I want to have public and private endpoints, I want an schema as follow:
/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
I have my configuration as follow:
Jersey configuration:
#Configuration
#ApplicationPath("/rest")
public class RestConfig extends ResourceConfig {
public RestConfig() {
register(SampleResource.class);
}
}
Spring security configuration:
#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();
}
}
The question is how can I register two application paths inside of my /rest context, one for /public and the other one for /private ?
NOTE: I tried to create another ResourceConfig as follow:
#Configuration
#ApplicationPath("/rest/public")
public class RestPublicConfig extends ResourceConfig{
public RestPublicConfig() {
register(PingResource.class);
}
}
But I'm getting the next error:
No qualifying bean of type [org.glassfish.jersey.server.ResourceConfig] is defined: expected single matching bean but found 2: restConfig,restPublicConfig
Thanks for your help :)
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
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.
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:
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;
}
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.
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
You won't be allowed to create two beans for your Resource Class. You can achieve what you are trying to achieve using a single Resource Class as well.
Here is an example:
#Path("rest")
public class SampleResourceClass {
#Path("/public/pings")
#GET
public Responce getPings(){
/* Code Here */
}
#Path("/private/accounts")
#GET
public Response getAccounts(){
/* Code Here */
}
}
The error you are seeing is not related to your security config, you may want to take a look at this ticket, https://github.com/spring-projects/spring-boot/issues/3260
If you want to permit all traffic to endpoints past /public you can add the RequestMatcher to the Spring Security ignore list.
#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

Java Spring 4 - why i need manually scan for beans in WebApplicationInitializer

I'm trying to setup a Web application with Spring 4.1 and Wicket 6.18. I want to use the full code approach. I have two test classes annotated with #Configuration and with #Bean. I want them to be discovered when i startup my app in Tomcat but it is not working unless i manually scan the base package in my custom WebApplicationInitialzer. By manually i mean to invoke AnnotationConfigWebApplicationContext.scan().
I looked through quite a few tutorials about the code based approach and didn't saw they do this. Even in the official spring docs they don't do this.
What i'm doing wrong that i need this and how to correct it?
My custom WebApplicationInitialzer looks like this:
public class WebAppInitializer implements WebApplicationInitializer {
private static final Logger logger = LoggerFactory.getLogger(WebAppInitializer.class);
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
container.addListener(new ContextLoaderListener(context));
context.scan("pl.myhouse"); //why do i need this???
FilterRegistration filter = container.addFilter("wicket.myproject", WicketFilter.class);
filter.setInitParameter("applicationClassName", WicketApplication.class.getName());
filter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, "/*");
filter.addMappingForUrlPatterns(null, false, "/*");
}
}
You both #The Head Rush and #Aeseir are correct. I was missing the rootContext.register(Appconfig.class) as well as the #ComponentScan. I even could share my knowledge further: Configuring Spring Wicket using Java Conf instead of xml :)

Categories

Resources