2016-02-13 6 views
0

Я следил за учебником из Spring Blog (https://spring.io/guides/tutorials/spring-security-and-angular-js/). Но я начал с существующего приложения Spring, поэтому я не использую Spring Boot для начала, и мне нужно найти способ реализовать компоненты в гибридном стиле XML и Java Configuration.AngularJS HTTP POST Ожидаемый токен CSRF не найден

Вот мой 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 настроен быть после 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); 
     } 
} 

и, конечно же, пользовательский выход из системы URL:

<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 довольно прост. Первоначально я показываю форму входа и просто делаю запрос HTTP POST в конечную точку Spring/company/login, но каким-то образом приложение AngularJS не получает токен CSRF, который ему нужен ... Поэтому я добавил HTTP GET-запрос при запуске, чтобы запросить от открытый URL (access = "allowAll()"), чтобы получить XSRF-TOKEN для моих предстоящих запросов. Вход и выход прекращаются до тех пор, пока я не войду в систему снова. Ошибка: «POST http://localhost:8080/company/login 403 (Запрещено)» и «Недопустимый токен CSRF был найден по параметру запроса« _csrf »или заголовок« X-XSRF-TOKEN »«

Я думаю, что что-то не так с файлами cookie в моем браузере , Я вижу тот же самый XSRF-TOKEN, перед тем как перейти к Spring, когда я вывожу данные cookie в свой фильтр CORS, а Spring CsrfFilter Spring отказал в дальнейшем запросе, потому что токен 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 

Возможно, мне не хватает некоторых функций на стороне выхода из системы? Как я могу обновить XSRF-TOKEN, если мой запрос никогда не получит пропуск CsrfFilter Spring?

Пожалуйста, не стесняйтесь спрашивать меня, если необходимо. Я очень хочу, чтобы этот вопрос решить, потому что я уже потратил так много времени, пытаясь выяснить, что не так :(

ответ

0

Так, вскоре после того, как я отправил этот ответ, я понял вопрос ...

Конфигурация бэкэнд-сервера в порядке, проблема связана с HTTP-запросом AngularJS. Поскольку я следую учебнику из блога 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, а затем сделать войдите в систему POST-запрос после запроса GET:

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

Пожалуйста, дайте мне какое-либо предложение, если что-то не так.

Смежные вопросы