Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 766
user6123723 Avatar asked Jul 14 '26 14:07

user6123723


1 Answers

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.

like image 154
scav Avatar answered Jul 17 '26 15:07

scav



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!