[英]AngularJS HTTP POST Expected CSRF token not found
我正在關注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的/ company / login端點執行HTTP POST請求,但是不知何故,我的AngularJS應用程序未獲得所需的CSRF令牌...因此,我在啟動時添加了HTTP GET請求以從一個開放的URL(access =“ permitAll()”),以便為我的即將到來的請求獲取XSRF-TOKEN。 登錄和注銷工作正常,直到我再次登錄。 錯誤為“ POST http:// localhost:8080 / company / login 403(禁止)”和“在請求參數'_csrf'或標頭'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?
如有必要,請隨時詢問我更多詳細信息。 我真的很想解決這個問題,因為我已經花了很多時間試圖找出問題所在:(
因此,在我發布此答案后不久,我就發現了這個問題...
后端服務器配置很好,問題實際上出在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
...
});
})
如果有什么問題,請給我任何建議。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.