XSRF 又名 CSRF),跨站请求伪造,它是前端常见的一种攻击方式,我们先通过一张图来认识它的攻击手段。
image.png

需求

利用token校验解决 CSRF。
原理:结合上面原理图,当web B向 web A发送请求时(withCredentials=true),由于会自动携带web a 的cookie。现在修改web a 的鉴权方式,把token从cookie获取,手动放到请求header中。后台从header获取。
由于web B不能读取 web a 域下的cookie,所以不能手动放进header,从而解决CSRF。
每次发送请求的时候,从 cookie 中读取对应的 token 值,然后添加到请求 headers中。我们允许用户配置 xsrfCookieName 和 xsrfHeaderName,其中 xsrfCookieName 表示存储 token 的 cookie 名称,xsrfHeaderName 表示请求 headers 中 token 对应的 header 名称。
提供 xsrfCookieName 和 xsrfHeaderName 的默认值,当然用户也可以根据自己的需求在请求中去配置 xsrfCookieName 和 xsrfHeaderName。

  1. // 本域名请求,可以把cookie的token获取,放进header中
  2. axios.get('/more/get', {
  3. xsrfCookieName: 'XSRF-TOKEN-test',
  4. xsrfHeaderName: 'X-XSRF-TOKEN-test'
  5. }).then(res => {
  6. console.log(res)
  7. })
  8. // server1 向 server2发送请求,即使cookie名称对应上,也不能手动添加token到header中
  9. axios({
  10. method: 'post',
  11. url: 'http://localhost:8088/more/server2',
  12. xsrfCookieName: 'XSRF-TOKEN-D', // server2 cookie名称
  13. xsrfHeaderName: 'X-XSRF-TOKEN-D', // server2 token名称
  14. withCredentials: true
  15. }).then(res => {
  16. console.log(res)
  17. })

更多防止 XSRF攻击,阅读 《如何防止CSRF攻击?

代码实现

添加类型声明

export interface AxiosRequestConfig {
  // ...
  xsrfCookieName?: string
  xsrfHeaderName?: string
}

添加默认值

const defaultConfig: AxiosRequestConfig = {
  // ...
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN'
}

功能实现

手动读取cookie添加到header中

    // 防止xsrf攻击
    if ((withCredentials || isURLSameOrigin(url)) && xsrfCookieName) {
      const xsfrValue = cookie.read(xsrfCookieName)

        if (xsfrValue && xsrfHeaderName) headers[xsrfHeaderName] = xsfrValue
    }

实现cookie工具函数

const cookie = {
  read(name: string): string | null {
    // 正则提取cookie值
    const reg = new RegExp(`(^|;\\s*)(${name})=([^;]*)`)
    const match = document.cookie.match(reg)

    return match ? match[3] : null
  }
}


export default cookie

实现是否同源判断函数

// url是否和当前域同源
export function isURLSameOrigin(url: string): boolean {
  const targetOrgin = resolveUrl(url)

  return targetOrgin.protocol === currentOrigin.protocol && targetOrgin.host === currentOrigin.host
}

const a = document.createElement('a')
const currentOrigin = resolveUrl(window.location.href)
function resolveUrl(url: string): URLOrigin {
  a.setAttribute('url', url)
  const { protocol, host } = a

  return {
    protocol,
    host
  }
}