Я следил за учебником из 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?
Пожалуйста, не стесняйтесь спрашивать меня, если необходимо. Я очень хочу, чтобы этот вопрос решить, потому что я уже потратил так много времени, пытаясь выяснить, что не так :(