sonoerin
sonoerin

Reputation: 5175

spring security mapping for wildcards

Using Spring-Boot 1.1.17, Spring-MVC with Spring-Security:

I have several subdomains that I want to to allow unauthenticated users (Visitors) access to. For example:

If a invalid customer site is attempted, then my controller would either throw an exception or redirect back to / (mysite.com/) Naturally other parts of the domain (mysite.com/customerA/myaccount) will require login.

I haven't really figured out how to do this with spring security and spring-mvc. Here is what I am attempting so far:

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterAfter(new CSRFTokenGeneratorFilter(), CsrfFilter.class)
                .authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers( "/**/" ).permitAll()
                .antMatchers("/login").permitAll()
                .antMatchers("/wizard").permitAll()
                .antMatchers("/menu").permitAll()
                .antMatchers("/error").permitAll()
                .antMatchers("/resources/**").permitAll()
                .antMatchers("/css/**").permitAll()
                .antMatchers("/js/**").permitAll()
                .antMatchers("/fonts/**").permitAll()
                .antMatchers("/libs/**").permitAll();

        http
                .formLogin()
                .loginPage("/loginPage")
                .permitAll()
                .loginProcessingUrl("/login")
                .failureUrl("/login?error")
                .defaultSuccessUrl("/?tab=success")
                .and()
                .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
                .permitAll()
                .and()
                .csrf();

        http
                .sessionManagement()
                .maximumSessions(1)
                .expiredUrl("/login?expired")
                .maxSessionsPreventsLogin(true)
                .and()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .invalidSessionUrl("/");

        http
                .authorizeRequests().anyRequest().authenticated();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        auth.userDetailsService( customUserDetailsService ).passwordEncoder( encoder );
    }

    @Override
    public void configure(WebSecurity security){
        security.ignoring().antMatchers("/css/**","/fonts/**","/libs/**");
    }
}

And my homepage controller:

@Controller
@RequestMapping("/{officeName}/")
public class HomeController {
private AuthenticatedUser getVisitor(@PathVariable String officeName) {

.. do something with the office if found, redirect otherwise
         if (!StringUtils.isEmpty(officeName)) {
            Office office = officeService.findByName( officeName );
            return office.getUrl();

        }
        return "/";
}

When I try to access that url, I get the following errors:

 o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/customerA/]
 s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /customerA/
 s.w.s.m.m.a.RequestMappingHandlerMapping : Did not find handler method for [/customerA/]
 o.s.w.s.handler.SimpleUrlHandlerMapping  : Matching patterns for request [/customerA/] are [/**]
 o.s.w.s.handler.SimpleUrlHandlerMapping  : URI Template variables for request [/customerA/] are {}
 o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapping [/customerA/] to HandlerExecutionChain with handler [org.springframework.web.servlet.resource.ResourceHttpRequestHandler@2f295527] and 1 interceptor
 o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/customerA/] is: -1
 o.s.w.s.r.ResourceHttpRequestHandler     : Trying relative path [customerA] against base location: ServletContext resource [/]
 o.s.w.s.r.ResourceHttpRequestHandler     : Trying relative path [customerA] against base location: class path resource [META-INF/resources/]
 o.s.w.s.r.ResourceHttpRequestHandler     : Trying relative path [customerA] against base location: class path resource [resources/]
 o.s.w.s.r.ResourceHttpRequestHandler     : Trying relative path [customerA] against base location: class path resource [static/]
 o.s.w.s.r.ResourceHttpRequestHandler     : Trying relative path [customerA] against base location: class path resource [public/]
 o.s.w.s.r.ResourceHttpRequestHandler     : No matching resource found - returning 404 

I tried adding this ServletRegistrationBean:

@Bean
public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
    ServletRegistrationBean registration = new ServletRegistrationBean( dispatcherServlet );

    registration.addUrlMappings("/", "/testCustomer/*"  );

    for ( Office office : officeService.findAllActiveOffices() ) {
        registration.addUrlMappings( office.getUrl() + "/*" );
    }
    return registration;
}

But this would seem to only work if the application knows of the customer at startup, not dynamically in the case of customer signup.

Is there a way to configure this to handle these types of wildcards?

Upvotes: 2

Views: 21724

Answers (1)

Andrea
Andrea

Reputation: 16560

You can try with a configuration like the following:

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private UserDetailsService _userService;

  @Autowired
  private PasswordEncoder _passwordEncoder;

  /**
   * Defines the password encoder used by Spring security during the
   * authentication procedure.
   */
  @Bean
  public PasswordEncoder passwordEncoder() {
    // default strength = 10
    return new BCryptPasswordEncoder();
  }

  /**
   * Sets security configurations for the authentication manager
   */
  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth)
      throws Exception {
    auth
      .userDetailsService(_userService)
      .passwordEncoder(_passwordEncoder);
    return;
  }

  /**
   * Configures where Spring Security will be disabled (security = none).
   * From spring reference: "Typically the requests that are registered [here]
   * should be that of only static resources. For requests that are dynamic,
   * consider mapping the request to allow all users instead."
   */
  @Override
  public void configure(WebSecurity web) throws Exception {
      web.ignoring()
        .antMatchers(
          "/css/**",
          "/js/**",
          "/fonts/**",
          "/resources/**",
          "/libs/**");
      return;
  }

  /**
   * Sets security configurations in the HttpSecurity object.
   */
  @Override
  protected void configure(HttpSecurity http) throws Exception {

    // Set security configurations
    http
      .authorizeRequests()
        // the following urls are allowed for any user (no authentication)
        .antMatchers(
            "/",
            "/login",
            "/menu")
            .permitAll()
        // any other url must be authenticated
        .anyRequest().authenticated()
        .and()
      // define the login page url
      .formLogin()
        .loginPage("/login")
        .permitAll()
        .and()
      // define the logout url
      .logout()
        .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
        .logoutSuccessUrl("/login?logout")
        .permitAll();

    return;
  } // method configure

} // class WebSecurityConfig

Adding your personal configurations... You can try to add the following controller:

@Controller
public class HomeController {

  @RequestMapping("/{officeName}/")
  public AuthenticatedUser getVisitor(@PathVariable String officeName) {

    // .. do something with the office if found, redirect otherwise
    if (!StringUtils.isEmpty(officeName)) {
        Office office = officeService.findByName( officeName );
        return office.getUrl();
    }

    return "/";
  }
}

If the user is correctly authenticated he should access the url at the officeName.

Upvotes: 2

Related Questions