user6123723
user6123723

Reputation: 11106

Adding Spring Security to an existing Spring Web App (using JavaConfig)

I have a Spring MVC Rest Web App for which I'm in the process of adding a layer of Spring Security.

While I'm going through the Spring documentation, I'm unable to pickup the meaning of Section 3.1.3. I'm copy/pasting the contents of the section below.

If we were using Spring elsewhere in our application we probably already had a WebApplicationInitializer that is loading our Spring Configuration. If we use the previous configuration we would get an error. Instead, we should register Spring Security with the existing ApplicationContext. For example, if we were using Spring MVC our SecurityWebApplicationInitializer would look something like the following:

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
      extends AbstractSecurityWebApplicationInitializer {

}

This would simply only register the springSecurityFilterChain Filter for every URL in your application. After that we would ensure that SecurityConfig was loaded in our existing ApplicationInitializer. For example, if we were using Spring MVC it would be added in the getRootConfigClasses()

public class MvcWebApplicationInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { SecurityConfig.class };
    }

    // ... other overrides ...
}

So, I already have the following

an Initializer.java (replacement of web.xml)
Config.java - Root Context
RestServlet.java - Servlet Context

Here is my Initializer.java

public class Initializer implements WebApplicationInitializer {

    public void onStartup(ServletContext container) throws ServletException {

      // Create the 'root' Spring application context
      AnnotationConfigWebApplicationContext rootContext =
        new AnnotationConfigWebApplicationContext();
      rootContext.register(Config.class);

      // Manage the lifecycle of the root application context
      container.addListener(new ContextLoaderListener(rootContext));
//      container.addListener(new ContextLoaderListener(rootContext));

      // Create the dispatcher servlet's Spring application context
      AnnotationConfigWebApplicationContext dispatcherContext =
        new AnnotationConfigWebApplicationContext();
      dispatcherContext.register(RestServlet.class);

      // Register and map the dispatcher servlet
      ServletRegistration.Dynamic dispatcher =
        container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
      dispatcher.setLoadOnStartup(1);
      dispatcher.addMapping("/");


    }
 }

To add the Spring Security layer, I added the following

SecurityConfig.java
SecurityInitializer.java

SecurityConfig.java (This is to test using in memory auth details).

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

       @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }
}

SecurityInitializer.java

public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer
              {


    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { SecurityConfig.class };
    }

Now, the problem is that I'm unsure about how to perform these steps. I don't know (based on section 3.2.3 of docs) if I should extend AbstractSecurityWebApplicationInitializer or AbstractAnnotationConfigDispatcherServletInitializer.

Another issue is that this is a REST application. I do not have any controllers that return jsps (and I do not want to!). My end goal is to use OAuth2, generate and issue tokens to the frontend webapp (based on Angular) and secure the REST api this way. Also add Facebook and Google+ login on top of this. But I'm taking baby steps with spring security and I'm stuck here. Wondering if any of those who have taken this path already can share their wisdom.

Upvotes: 3

Views: 4703

Answers (3)

user2533000
user2533000

Reputation: 1

actually having two context-loader listeners registered will fire a framework related exception. These two context-loader listener registration scenario happens when one already decorated the DispatcherServlet configuration through java-config, by extending the AbstractAnnotationConfigDispatcherServletInitializer -> root-config classes will be registered in the ContextLoaderListener context; now having the security aspect also enabled by extending the AbstractSecurityWebApplicationInitializer will attempt to create another ContextLoaderListener IF ANY root-configuration classes are given. So as an advice is if one already has a ContextLoaderListener with root-related-config beans to avoid registering other root-related-config beans through the AbstractSecurityWebApplicationInitializer.

Upvotes: 0

scav
scav

Reputation: 265

You can inject this as a normal @Configuration class as follows:

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    FooUserDetailsService fooUserDetailsService;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(this.fooUserDetailsService).passwordEncoder(passwordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .authorizeRequests()
                .antMatchers("/signup").anonymous()
                .antMatchers("/public/**").permitAll()
                .antMatchers("/auth/**").permitAll()
                .antMatchers("/api/**").hasRole("USER")
                .antMatchers("/**").hasAnyRole("USER", "ADMIN")
            .and()
                .csrf().disable()
                .formLogin()
                .loginProcessingUrl("/j_spring_security_check")
                .loginPage("/auth").failureUrl("/auth")
                .usernameParameter("j_username").passwordParameter("j_password")
                .defaultSuccessUrl("/")
            .and()
                .logout()
                .logoutUrl("/j_spring_security_logout")
                .logoutSuccessUrl("/auth");
    }
}

The paths here are just examples, and you will likely have to rewrite this to fit the needs that you have, such as removing the form login stuff if this is a pure REST API you are making.

To have this loaded, you can do as follows:

public class WebApplicationInitialiser implements WebApplicationInitializer {

    private static Class<?>[]  configurationClasses = new Class<?>[] {
        WebSecurityConfiguration.class
    };
}

Add the classes(as I assume you have more than one) to the context with createContext(configurationClasses);

I hope this was helpful.

Upvotes: 4

ebell
ebell

Reputation: 137

One of the next steps you need to do is complete "3.4. Authorize Requests" section. You will need to create controllers to enable you to create a RESTful service. Instead of returning a JSP page you can return JSON or XML. To create a RESTful web service please refer to Spring.io documentation (http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch18s02.html), this is a link to spring 3.0, there may be a newer version available for Spring 4.0 but this link should give you enough information to get you started on the REST side of things. Once you have your REST requests setup for example @RequestMapping("/users/{userid}", method=RequestMethod.GET).

Then you can now follow section 3.4 i.e. .antMatchers("/users/**").hasRole("USER")

The next step would be to look at Authentication and setup your data source that holds you users/password you have 2 option here I would suggest using the in-memory configuration first see "3.5.1. In Memory Authentication" for more info.

Upvotes: 1

Related Questions