Spring 安全性:在 401 情况下重定向到单页应用

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

当我在浏览器中输入我的 React 应用程序的任何路由时,例如:http://localhost/login,请求到达我的服务器,我的服务器响应 401 未授权。

当请求不是授权后端时api我想在我的反应路由中处理请求。

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());
    }
}

有没有办法在 RestAuthenticationEntryPoint 中将请求转发到 index.html?

你可以在 react 中处理一个 401:

axios.post('http://localhost:8080/login', data)
    .then(resp => ...)
    .catch(e => {
        if (e.response && e.response.status===401) 
            history.push('/error');
    });

或者您可以修改您的 AuthenticationHandler:

    @Override
    public void commence(HttpServletRequest httpServletRequest,
                         HttpServletResponse httpServletResponse,
                         AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.sendRedirect("http://localhost:3000/error");
    }

我决定从 RestAuthenticationEntryPoint 抛出 404 Not Found 异常,因为我认为它比 401 Unathorized 更符合这个用例:

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

并将找不到异常重定向到前端:

    @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";
    }

}

这种方法的缺点是我不能从任何控制器抛出 404,因为它不会返回到前端,但我可以接受。