首页 文章

AngularJS HTTP POST未找到预期的CSRF令牌

提问于
浏览
0

我正在关注Spring Blog(https://spring.io/guides/tutorials/spring-security-and-angular-js/)的教程 . 但我从一个现有的Spring应用程序开始,因此我没有使用Spring Boot开始,我必须找到一种方法来实现XML和Java Configuration混合风格的组件 .

这是我的CORS过滤器:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCORSFilter implements Filter {

    public SimpleCORSFilter(){

    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response=(HttpServletResponse) resp;

        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, PATCH");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        //x-auth-token is a custom header for Spring Security AngularJS implementation
        response.setHeader("Access-Control-Allow-Headers", "Options, authentication, authorization, X-Auth-Token, Origin, X-Requested-With, Content-Type, Accept, XSRF-TOKEN");
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            System.out.println("OPTIONS request from AngularJS");
            response.setStatus(HttpServletResponse.SC_OK);
        }
        chain.doFilter(req, response);
    }

    @Override
    public void destroy() {}

这是我的CsrfHeaderFilter.java,几乎只是从教程中复制而来:

@Component
public class CsrfHeaderFilter extends OncePerRequestFilter{

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        System.out.println("CsrfHeaderFilter vvv");
        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
        if(csrf != null){
            Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
            String token = csrf.getToken();
            System.out.println("CSRFToken Value: "+token);
            if(cookie == null || token != null && !token.equals(cookie.getValue()) ){
                cookie = new Cookie("XSRF-TOKEN", token); //use XSRF-TOKEN as the response header for CSRF token
                cookie.setPath("/");
                response.addCookie(cookie);
            }
        }
        System.out.println("CsrfHeaderFilter ^^^");
        filterChain.doFilter(request, response);
    }

和CsrfHeaderFilter配置为在Spring的CsrfFilter之后:

<sec:custom-filter ref="csrfHeaderFilter" after="CSRF_FILTER" />
<sec:csrf token-repository-ref="csrfTokenRepository"/>

csrfTokenRepository:

@Configuration
public class CustomCsrfTokenRepository {

    @Bean
    public CsrfTokenRepository csrfTokenRepository(){
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName("X-XSRF-TOKEN");
        repository.setParameterName("_csrf");
        return repository;
    }
}

我有自己的身份验证过滤器和自定义登录URL:

public class CustomerAuthenticationTokenProcessingFilter extends AbstractAuthenticationProcessingFilter{

    private static final String SECURITY_TOKEN_HEADER = "x-auth-token";
    private static final String AUTHORIZATION_HEADER = "authorization";

    @Autowired
    private CustomerTokenAuthenticationService tokenAuthenticationService;

    @Autowired
    CustomerAuthenticationService customerAuthenticationService;
    @Autowired
    @Qualifier("customerAuthenticationManager")
    AuthenticationManager authenticationManager;

    protected CustomerAuthenticationTokenProcessingFilter(){
        super("/company/login"); 
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {
            Authentication authentication = null;
            //Authentication Logics...
            ...
        return authentication;
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
            Authentication authResult) throws IOException, ServletException {
        SecurityContextHolder.getContext().setAuthentication(authResult);
      }
}

当然还有自定义注销网址:

<sec:logout invalidate-session="true" delete-cookies="JSESSION,XSRF-TOKEN" 
    logout-url="/resource/logout" success-handler-ref="customerLogoutSuccessHandler"/>

customerLogoutSuccessHandler:

public class CustomerLogoutSuccessHandler implements LogoutSuccessHandler{

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {

        if (authentication != null && authentication.getDetails() != null) {
            try {

                SecurityContextHolder.clearContext();
                System.out.println("User Successfully Logout"); 
                response.setStatus(HttpServletResponse.SC_OK);

            } catch (Exception e) {  
                e.printStackTrace();  
                e = null;  
            }  
        }  
    }

}

AngularJS代码非常简单 . 最初我显示登录表单,只是向Spring的/公司/登录 endpoints 发出HTTP POST请求,但不知怎的,我的AngularJS应用程序没有获得它需要的CSRF令牌......所以我在启动时添加了一个HTTP GET请求来请求来自一个开放的URL(access = "permitAll()"),以便为即将到来的请求获取XSRF-TOKEN . 登录和注销工作正常,直到我再次登录 . 错误是“POST http://localhost:8080/company/login 403(Forbidden)" and "在请求参数'_csrf'或 Headers 'X-XSRF-TOKEN'上找到无效的CSRF令牌”

我认为我的浏览器中的cookie有问题 . 当我在CORS过滤器中输出cookie数据时,我可以在传递给Spring之前看到相同的XSRF-TOKEN,并且Spring的CsrfFilter拒绝了进一步的请求,因为CSRF令牌不正确 .

FilterChainProxy DEBUG - /company/login at position 3 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
CsrfFilter DEBUG - Invalid CSRF token found for http://localhost:8080/company/login

也许我在登出部分缺少一些功能?如果我的请求永远不会通过Spring的CsrfFilter,我如何更新XSRF-TOKEN?

如有必要,请随时向我询问更多细节 . 我真的想解决这个问题,因为我已经花了很多时间试图弄清楚出了什么问题:(

1 回答

  • 0

    所以,在我发布这个答案后不久,我发现了问题......

    后端服务器配置很好,问题实际上来自AngularJS HTTP请求 . 由于我正在关注Spring博客的教程,因此我也使用了教程中的AngularJS代码,因为我对AngularJS还不熟悉 . 我创建了更多的函数和控制器来满足我的需要,我使用$ http.get和.success()和.error(),这是一个异步调用 . 不知何故,这导致了Csrf令牌问题 . 所有这些异步http请求让我非常困惑 . 所以我决定找到一种方法,等待Http请求在下一个Http请求之前完成,看看会发生什么 . 我有一个工厂来处理登录和注销 .

    这可能不是最好的方法,但这是AngularJS代码的一部分:

    //Move the initial GET request to a variable
                var init = function(){
                      return $http(
                                {
                                    url : '/my/url/',
                                    method : 'GET',
                                }
                      ).then(function(result){
                          //bunch of debugging messages
                          return result.data;
                      })
    
                }
    

    以下是如何调用我的初始GET请求来获取CSRF令牌,然后在GET请求完成后发出登录POST请求:

    var initPromise = init();
                    initPromise.then(function(result){
                        $http(
                                {
                                    url : '/url/to/login',
                                    method : 'POST',
                                    headers : headers
                                }
                        ).success(function(data, config, headers) {
                           //debugging messages of course
                            //getting my data
                            ...
    
                            if(data_validation_is_true){
                                $http({
                                    url : '/my/secured/resource/url',
                                    method : 'GET',
                                    headers : 
                                    {
                                        'X-Auth-Token' : token_I_Received
                                    }
                                }).success(function(data, config, headers){
                                    console.log('/inventory/resource/user request with token, response headers: '+JSON.stringify(headers()));
                                    if (authenticated) {
                                          //authentication successful logic
                                          ...
                                        } else {
                                          //authentication unsuccessful logic
                                          ...
                                        }
    
                                }).error(function(data){
                                    //error handling
                                    ...
                                });
                            }
                          }).error(function(data, status) {
                            //error handling
                                    ...
                          });
                    })
    

    如果有任何问题,请给我任何建议 .

相关问题