4
votes

I am trying to to restrict unauthenticated users from accessing any routes in our Reactjs single page application. We are using a HashRouter to handle the routing on the UI side, so our urls look like http://localhost/app/#/Home, http://localhost/app/#/Login, etc. We are using Spring Security OAuth2 with JWT.

When a request to the application, all requests come in as /app/, as opposed to /app/#/Login, /app/#/Home, etc. This makes sense to me as the routing is done on the client to render the correct route, but it causes problems trying to secure the application. In order to allow access to the Login route, I need to allow access to /app/ to all in Spring Security, which opens everything up.

Is it possible to secure a React Hash Route in Spring Security, or will I have to handle this on the UI side in the Router?

Login Config

@EnableWebSecurity(debug = true)
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class LoginConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .antMatcher("/app/#/login")
                .httpBasic().disable()
                .formLogin().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .antMatchers(HttpMethod.GET, "/favicon.ico").permitAll()
                .antMatchers("/app/#/login").permitAll()
                .anyRequest().authenticated()
        ;
    }
}

Login Config Grants Access To All

@EnableWebSecurity(debug = true)
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class LoginConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .antMatcher("/app/**")
                .httpBasic().disable()
                .formLogin().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .antMatchers(HttpMethod.GET, "/favicon.ico").permitAll()
                .antMatchers("/app/**").permitAll()
                .anyRequest().authenticated()
        ;
    }
}

Default Config For All Other Requests

@EnableWebSecurity(debug = true)
@Configuration
@Order(2)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .formLogin().disable()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .antMatchers(HttpMethod.GET, "/**/favicon.ico").permitAll()
                .antMatchers(HttpMethod.POST, "/oa/oauth/token").permitAll()
                .anyRequest().authenticated()
        ;
    }
}

Thanks in advance!

1

1 Answers

5
votes

You can't. When building an SPA, you shift your security focus to secure the API endpoints that the SPA is accessing instead of securing "pages" or "SPA routes". So, just protect the server endpoints that provide data and functionality to the SPA.

For example, if a user is not allowed to access "admin" functionality, instead of trying to block the route /app/#Admin, you make sure and block all the admin-related api endpoints (like /api/admin, /api/adduser, ...).

This doesn't mean you shouldn't also have code within the SPA to hide forbidden links or prevent the user from visiting that route -- you should because it makes for a better user experience. Just know that that hiding the routes in the SPA is purely for UX, not security. Security is handled by protecting the api.