XSRF - 如何设置跨源cookie

时间:2017-01-24 23:27:29

标签: javascript spring http angular electron

我开发了REST API和两个JavaScript客户端(基于电子的单页应用程序和本机应用程序)。在两个客户端中,我的用户都通过OAuth2流进行身份验证:

  1. 将用户密码发送到服务器
  2. 获取access_token(纯文本)和refresh_token(在httponly cookie中)
  3. 当令牌过期时,他们刷新它向/刷新端点发送请求(服务器从cookie读取refresh_token)
  4. 现在我想实现csrf保护。所以我在我的后端(Spring)实现了它:

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
                    .authorizeRequests().antMatchers("/", "/index.html", "/token/**").permitAll()
                    .anyRequest().authenticated();
        }
    

    我的SPA工作正常,angular从cookie读取XSRF-TOKEN并以X-XSRF-TOKEN标头发送。我遇到了电子应用的问题。它无法访问cookie(由于来源不同 - 电子在file:// url上运行),因此无法设置X-XSRF-TOKEN标头。

    我该如何处理这个问题?有什么方法可以实现"交叉起源"曲奇饼?或者也许我可以通过电子魔术电子API获取cookie值(如果它可以访问文件系统,也许它可以访问在机器上创建的任何cookie)?

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题,最终设法通过实现一个有角度的http拦截器来解决此问题:

@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {

  constructor(private tokenExtractor: HttpXsrfTokenExtractor, //from angular
              private nativeAuthService: NativeAuthService) { //my native service
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    //this works in web
    const headerName = 'X-XSRF-TOKEN';
    const token = this.tokenExtractor.getToken() as string;
    if (token !== null && !req.headers.has(headerName)) {
      req = req.clone({ headers: req.headers.set(headerName, token) });
      return next.handle(req);
    }

    //this works in electron
    return this.nativeAuthService.getCookieValue('XSRF-TOKEN').pipe(
      mergeMap((xsrfToken: string) => {
        if (xsrfToken && !req.headers.has(headerName)) {
          req = req.clone({ headers: req.headers.set(headerName, xsrfToken) });
        }
        return next.handle(req);
      }),
    );
  }
}

我的nativeAuthService基本上只是在电子主过程中调用此方法:

export function getCookieValue(cookieName: string): Observable<string> {
  return from(session.defaultSession.cookies.get({ name: cookieName })
    .then((cookies: Electron.Cookie[]) => {
      if (cookies.length == 1) {
        return cookies[0].value;
      } else {
        if (cookies.length > 1) {
          throw Error(`There is more than one cookie with the name: ${ cookieName }.`);
        }
        return '';
      }
    }));
}