使用SPA中的React路由器重定向Spring Security登录页面

时间:2017-01-09 16:50:59

标签: spring spring-security react-router single-page-application

我遇到配置问题,当在开发模式下运行时,Spring Security会重定向到我的React前端的登录页面(与React Router一起运行)。我相信我的前端没有接收重定向,这是由于我对服务器端路由如何与SPA路由交互的理解。

我的开发前端Web服务器(使用Facebook Create React App)在localhost:3000上运行,我的后端Spring Boot Tomcat在localhost:8080上运行。我启用了Spring Security,它在Tomcat服务器上工作正常(端口8080);访问localhost:8080转发到localhost:8080 / login。访问localhost:3000时,我遇到了跨源错误,因此在Spring中添加了CORS支持,如here所述。我还咨询了this Spring博客文章。它似乎工作,因为我不再得到跨源错误。但是,在未登录的情况下,localhost:3000到localhost:8080 / login的请求将返回以下内容:

HTTP/1.1 200 OK
X-Powered-By: Express
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=UTF-8
Content-Length: 1496
ETag: W/"5d8-80rXYG+kNfQ/xEJ7J2f1PA"
Vary: Accept-Encoding
Date: Tue, 10 Jan 2017 12:04:07 GMT
Connection: keep-alive

<!DOCTYPE html>
...

这是我的前端网络服务器中托管的index.html。这很好,但我需要它转发到localhost:3000 / login,这会将登录页面呈现为index.html。

我认为由于路由设置而没有转发到/ login,它来自localhost:3000/ - &gt; HttpSecurity.loginPage("/login")(春天) - &gt; registry.addViewController("/login").setViewName("forward:/index.html")(春天) - &gt; <Route path="/" component={ForwardToLandingPage} />(反应路由器)

最后出错了。我希望Tomcat服务器将前端重定向到:3000 / login,而前端则转到组件。似乎服务器端重定向和前端React Router到达正确位置之间存在问题。如果这太复杂而无法设置,我可能会在开发环境中删除整个Spring Security。

以下是我的配置:

反应路由配置

ReactDOM.render((
    <Router history={browserHistory}>
        <Route path="/" component={ForwardToLandingPage} />
        <Route path="/login" component={Login} />
    </Router>
    ),
    document.getElementById('root')
);

Spring MVC config

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {          
        // This forwarding is required, as my React app is rendering on a div
        // element with the ID 'root' in /index.html. But is this part of the
        // problem?
        registry.addViewController("/").setViewName("forward:/index.html");
        registry.addViewController("/login").setViewName("forward:/index.html");
    }
}

Spring CORS config

@Configuration
public class MyConfiguration {

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:3000");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

Spring Security配置

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable() // temporary fix
                .authorizeRequests()
                    .antMatchers("/h2-console", "/public/**", "/static/**").permitAll()
                    .anyRequest().authenticated()
                    .and()
                .formLogin()
                    .loginPage("/login")
                    .permitAll()
                    .and()
                .logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
    }
}

任何想法出了什么问题?

我想知道问题的解决方案是否应该涉及Tomcat,所以也检查了this

感谢您的帮助! :)

1 个答案:

答案 0 :(得分:-1)

我们使用的是较早版本的Spring安全版,但我不知道它是否支持CORS。我使用Spring安全性已有6年了,所以我刚刚创建了一个常规过滤器:

public class CORSFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        // The headers needs to be on all responses.
        String origin = httpServletRequest.getHeader("origin");
        if (origin == null) {
            // Make the developers life simpler. The browser adds the origin but it is a pain to
            // for developers to add this header while testing.
            origin = "*";
        }
        response.setHeader("Access-Control-Allow-Origin", origin.replace("\n", ""));

        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Accept, Authorization");

        // For options requests return immediately. Options does not require authentication so we want to return
        // here to avoid all yet unknown security risks.
        if (HttpMethod.OPTIONS.toString().equals(httpServletRequest.getMethod())) {
            return;
        }
        chain.doFilter(req, res);
    }

    public void init(FilterConfig filterConfig) {
    }

    public void destroy() {
    }
}

并在ChannelProcessingFilter之前添加,因此它成为过滤器链中的第一个过滤器。

 http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);
 http.formLogin().....
// all the other matchers and 

如果这对您不起作用,那么我不确定它只是一个CORS问题。