美文网首页我爱编程
【angular 2踩过的坑】Angular2 : X-XSRF

【angular 2踩过的坑】Angular2 : X-XSRF

作者: 苹果小王子写字的地方 | 来源:发表于2017-10-10 21:09 被阅读1061次

    1. 问题背景


    在开发一个请假销假管理系统时,我采用前后端分离的技术方案,具体如下图所示:

    技术方案

    其中,后台登录的Rest API为http://localhost:9090/token/user/login.
    前端angular 2中利用Http请求Rest API的get/post/put/delete封装在api.service.ts中,具体代码如下:

    export class ApiService {
      private expiredTime: number = 30 * 60 * 1000; // token时间30分钟
      constructor(private http: Http,
                  private jwtService: JwtService) {
      }
    
      private setHeader(): Headers {
        let headersConfig = {
          'Content-Type': 'application/json',
          'Accept': 'application/json'
        };
        if (this.jwtService.getToken()) {
          headersConfig['Authorization'] = `Token ${this.jwtService.getToken()}`;
          headersConfig['token'] = this.jwtService.getToken();
          headersConfig['X-Auth-Token'] = this.jwtService.getToken();
        }
        return new Headers(headersConfig);
      }
    
      private formatErrors(error: any) {
        return Observable.throw(error.json());
      }
    
      get(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> {
        return this.http.get(`${environment.api_url}${path}`, {headers: this.setHeader(), search: params})
          .catch(this.formatErrors)
          .map((res: Response) => res.json());
      }
    
      put(path: string, body: Object = {}): Observable<any> {
        return this.http.put(`${environment.api_url}${path}`,
          JSON.stringify(body),
          {headers: this.setHeader()})
          .catch(this.formatErrors)
          .map((res: Response) => res.json());
      }
    
      post(path: string, body: Object = {}): Observable<any> {
        return this.http.post(
          `${environment.api_url}${path}`,
          JSON.stringify(body),
          {headers: this.setHeader()})
          .catch(this.formatErrors)
          .map((res: Response) => res.json());
      }
    
      delete(path: string): Observable<any> {
        return this.http.delete(
          `${environment.api_url}${path}`,
          {headers: this.setHeader()}
        )
          .catch(this.formatErrors)
          .map((res: Response) => res.json());
      }
    }
    
    

    其中setHeader()是用来设置请求头的方法。

    2. 问题描述


    在开发环境下运行angular 2工程时并没有出现任何错误。但是部署在生产环境下时,经常出现首次登录成功后,过段时间token过期时重新登录,会发现http在CORS跨域请求时首先发送OPTIONS探针请求返回http状态码200,此时后续的post登录请求却无法正常进行,查看chrome的Console后发现异常错误如下:

    Failed to load http://localhost:9090/token/user/login: 
    Request header field X-XSRF-TOKEN is not allowed by Access-Control-Allow-Headers in preflight response.
    
    错误

    3. 原因分析


    在Chrome浏览器中,大部分情况下默认Chrome Cookie保存在X-XSRF-TOKEN字段中,Chrome在发送OPTIONS探针请求时会自动将Access-Control-Request-Headers: x-xsrf-token Http Header添加到OPTIONS请求中,而java后台的HTTP CORS过滤器中尚未把 X-XSRF-TOKEN 添加到 Access-Control-Allow-Headers,因此后续的POST登录请求被拦截而无法被处理。

    4. 解决方法


    (1) Angular 2.0 Client

    在api.service.ts中的类ApiService中,将Access-Control-Request-Headers: x-xsrf-token添加到Headers中,即修改setHeader()函数为:

    private setHeader(): Headers {
        let headersConfig = {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'Access-Control-Allow-Headers': 'Content-Type, X-XSRF-TOKEN'
        };
    
        if (this.jwtService.getToken()) {
          headersConfig['Authorization'] = `Token ${this.jwtService.getToken()}`;
          headersConfig['token'] = this.jwtService.getToken();
          headersConfig['X-Auth-Token'] = this.jwtService.getToken();
        }
    
        return new Headers(headersConfig);
      }
    
    (2) Java Server:

    Access-Control-Allow-Headers中添加HeaderX-XSRF-TOKEN

    在spring-servlet.xml配置文件中添加以下代码:

     <mvc:cors>
            <mvc:mapping path="/**"
                         allow-credentials="true"
                         allowed-headers="Content-Type, Accept, token, Authorization, X-Auth-Token, X-XSRF-TOKEN, X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,Access-Control-Allow-Headers"
                         allowed-methods="POST, GET, PUT, OPTIONS, DELETE"/>
        </mvc:cors>
    

    或者, 修改CORS拦截器CORSIntercepter【在spring-servlet.xml中配置拦截器】的代码为:

    public class CORSIntercepter extends HandlerInterceptorAdapter {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "x-requested-with," +
                    "Content-Type,X-Amz-Date,Authorization,X-Api-Key," +
                    "X-Amz-Security-Token,X-XSRF-TOKEN,Access-Control-Allow-Headers");
            return false;
        }
    
    }
    

    又或者, 修改CORS过滤器CORSFilter【在web.xml中配置过滤器】的代码为:

    import javax.servlet.*;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * Created by bob on 2017/2/5.
     */
    public class CORSFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            System.out.println("Filtering on...........................................................");
            HttpServletResponse response = (HttpServletResponse) res;
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, token, Authorization, " +
                    "X-Auth-Token,X-XSRF-TOKEN,Access-Control-Allow-Headers");
            chain.doFilter(req, res);
        }
    
        @Override
        public void destroy() {
    
        }
    }
    

    Bob
    20171010

    相关文章

      网友评论

        本文标题:【angular 2踩过的坑】Angular2 : X-XSRF

        本文链接:https://www.haomeiwen.com/subject/hhubyxtx.html