Skip to content
Advertisement

Spring Security: redirect to single page app in case of 401

When I type into browser any route of my React app, for example: http://localhost/login, the request hits my server, and my server responds with 401 Unauthorized.

When request is not an authorized backend api I’d like to handle the request in my react routing.

WebSecurityConfig.java:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            ...
            .formLogin()
                .disable()
            .authorizeRequests()
                .antMatchers(
                    "/error",
                    "/",
                    "/favicon.ico",
                    "/static/**",
                    "/api/auth/**",
                    "/api/oauth2/**",
                    "/api/courses/**",
                    "/api/stripe/**",
                    "/api/lesson/content")
                    .permitAll()
                .anyRequest()
                    .authenticated()
                .and()
            ...
            .exceptionHandling()
                .authenticationEntryPoint(new RestAuthenticationEntryPoint())
                .and();

    http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}

RestAuthenticationEntryPoint.java:

public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest,
                         HttpServletResponse httpServletResponse,
                         AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
                e.getLocalizedMessage());
    }
}

Is there a way to forward the request to index.html in RestAuthenticationEntryPoint?

Advertisement

Answer

I’ve decided to throw 404 Not Found exception from RestAuthenticationEntryPoint, because I think it matches this use case more than 401 Unathorized:

    @Override
    public void commence(HttpServletRequest httpServletRequest,
                         HttpServletResponse httpServletResponse,
                         AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND,
                e.getLocalizedMessage());
    }

And redirect not found exception to the front end:

    @Bean
    public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> containerCustomizer() {
        return container -> {
            container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,
                    "/notFound"));
        };
    }
@Controller
public class CustomErrorController {

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/notFound")
    public String error() {
        return "forward:/index.html";
    }

}

The disadvantage of this approach is that I can’t throw 404 from any controller, because it won’t be returned to the frontend, but I can live with it.

Advertisement