【项目问题】token存储在哪里以及localStorage的跨域问题

1,090 阅读7分钟

Token一般存放在哪里? - 掘金 (juejin.cn)

面试官: 既然有了 cookie 为什么还要 localStorage?😕😕😕 - 掘金 (juejin.cn)

回答

(23.10.12更新)首先,localStorage和cookie都存在跨域的问题,也都会发送到服务器端,因此,不要从这两个方面回答。可以回答 localStorage的API简单易用,可以方便地获取和修改存储的数据,同时便于多个标签页之间的数据共享

cookie如果想要不同标签页之间共享,需要将自己的域名设置为顶级域名,否则会出现跨域问题

考虑这个问题的首先我们应该知道,token 就是一个字符串,而使用 cookie 的话,大小是满足的,所以考察的点就不在这个内存上面了。

localStorage 存储 token与使用 cookie存储token的不同,可以从以下几个方面考虑:

  1. 安全性: 在某些情况下,开发者可能认为将令牌存储在 Cookie 中存在一些安全风险,比如请求伪造CSRF,可以采取适当的安全措施,例如使用 HttpOnlySecure 标志,以减少潜在的安全问题。使用 localStorage 则会面临XSS(跨站脚本攻击)问题,需要在这个方面进行防护。
  2. 令牌过期处理: Cookie 可以具有过期时间,可以在设置失效时间后自动删除,这有助于确保 Token 的安全性和有效性。使用 localStorage 存储令牌可以让令牌在浏览器关闭后仍然保持有效,这在某些应用场景下是有用的。例如,用户可能关闭了浏览器,然后再次打开时仍然保持登录状态,而不需要重新输入凭据;

secure属性 当设置为true时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以不会被窃取到Cookie 的具体内容。

HttpOnly属性 如果在Cookie中设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能有效的防止XSS攻击。

1. token是什么

Token 其实就是访问资源的凭证。

一般是用户通过用户名和密码登录成功之后,服务器将登录凭证做数字签名,加密之后得到的字符串作为token。

2.token存放位置

它在用户登录成功之后会返回给客户端,客户端主要以下几种存储方式:

  1. 储存在 localStorage 中,每次调用接口时放在http请求头里面,长期有效
  2. 储存在 sessionStorage 中,每次调用接口时,把它当为一个字段传给后台,浏览器关闭自动清除
  3. 储存在 cookie 中,每次调用接口会自动发送,不过缺点是不能跨域

3. 这三个位置有什么不同

localStorage和sessionStorage

优点

容易访问:localStorage的API简单易用,可以方便地获取和修改存储的数据。

便于多个标签页之间的数据共享。

cookie如果想要不同标签页之间共享,需要将自己的域名设置为顶级域名,否则会出现跨域问题

跨域请求时可以携带localStorage,而不能携带cookie

缺点

将 Token 存储在 webStorage(localStorage,sessionStorage) 中可以通过同域的js访问,这样导致很容易受到 xss 攻击,特别是项目中引入很多第三方js库的情况下,如果js脚本被盗用,攻击者就可以轻易访问你的网站。

XSS攻击:Cross-Site Scripting(跨站脚本攻击)简称XSS,是一种代码注入攻击。攻击者通过在目标网站注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可以获取用户的敏感信息如Cookie、SessionID等,进而危害数据安全。

cookie

优点

将token存储在cookie中的优点是:

  • 安全性更高:cookie在传输过程中会被加密,避免了敏感数据在传输过程中被篡改或者被截获。
  • 可以设置过期时间:cookie可以设置过期时间,这样可以控制token的有效期,减少token被滥用的风险。
  • 对于一些后端框架,比如Spring Security,需要在cookie中存储token才能进行认证。
  • 可以制定httponly,来防止被JavaScript读取,也可以制定secure,来保证token只在HTTPS下传输。

缺点

  • 不符合Restful 最佳实践。 Restful最佳实践
  • 容易遭受CSRF攻击(可以在服务器端检查Refer和Origin)

CSRF: 跨站请求伪造,简单的说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如:发邮件、发信息、甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出去的。CSRF并不能够拿到用户的任何信息,它只是欺骗用户浏览器,让其以用户的名义进行操作。

在请求拦截器中注入token是为了什么

在这个项目里,我在请求拦截器里注入了token,是为什么?因为token存储在localStorage中,之后每次请求都要将token放到请求头中进行请求。将token注入到请求拦截器中,之后每次请求都不用自己手动再加入token了。

每次请求手动加入token

export default {
    data: function () {
            return {
              name:'',
              token:''
            }
    },
    
    created(){
    	//页面加载时就从本地通过localstorage获取存储的token值
        this.token =  localStorage.getItem('token')
    },
     mounted() {
        this.$axios({
        	method: 'get',
        	url: '/api/v1/user',
        	headers: {
            	'Content-Type': "application/json;charset=UTF-8",
            	//把token放到请求头才能请求,这里的'Bearer '表示是后台希望更加的安全,依据后台给的信息看到底是加还是不加
            	'Authorization': 'Bearer ' + this.token,
        	}
      	})
      	.then(res=>{                    //请求成功后执行函数
            if(res.data.code === 0){
              //请求成功之后给用户名赋值
              this.name=res.data.data.username
              console.log("登录成功")
            }else{
              console.log("登录失败")
            }
          })
          .catch(err=>{                   //请求错误后执行函
            console.log("请求错误")
          })
        },
}

请求拦截器中加入token

首先在封装的http模块添加请求拦截器

import axios from 'axios'

const http = axios.create({
  baseURL: 'http://geek.itheima.net/v1_0',
  timeout: 5000
})
// 添加请求拦截器
http.interceptors.request.use((config)=> {
    return config
  }, (error)=> {
    return Promise.reject(error)
})

// 添加响应拦截器
http.interceptors.response.use((response)=> {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response
  }, (error)=> {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error)
})

export { http }

接着使用mobx设置用户登录的store,具体步骤在React 简单pc项目搭建 - 掘金 (juejin.cn),将token保存在localStorage中

请求拦截器中加入token

http.interceptors.request.use(config => {
  // if not login add token
  const token = getToken()
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

localStorage存在跨域问题吗?

很明显,是存在的!想要解决这个问题可以使用postMessage来解决。 localstorage跨域问题_localstorage跨域读取-CSDN博客

使用postMessage解决跨域问题

blog.csdn.net/huangpb123/…

postMessage() 方法,该方法允许有限的通信 —— 通过异步消息传递的方式 —— 在来自不同源的脚本之间。

postMessage 可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递
  • 页面与嵌套的 iframe 消息传递
  • 多窗口之间消息传递

想要使用 postMessage 实现跨域通信和页面间数据通信,只要记住 window 提供的 postMessage 方法和 message 事件就ok了。

不同源的两个窗口之间建立双向数据通信

/**
* localhost:10002/index页面
**/
// 接收消息
window.addEventListener('message', (e) => {
     console.log(e.data)
})
// 发送消息
const targetWindow = window.open('http://localhost:10001/user');
setTimeout(()=>{
     targetWindow.postMessage('来自10002的消息', 'http://localhost:10001')
}, 3000)

/**
* localhost:10001/user页面
**/
window.addEventListener('message', (e) => {
     console.log(e.data)
     if (event.origin !== "http://localhost:10002") 
         return;
     e.source.postMessage('来自10001的消息', e.origin)
})

页面与嵌套的iframe消息传递

以下是www.domain1.com/a.html 页面的内容

<iframe id="iframe" src="http://www.domain2.com/b.html"></iframe>
 
<script>
var iframe = document.getElementById('iframe');
 
iframe.onload = function() {
   // 向domain2发送跨域数据
   iframe.contentWindow.postMessage('来自domain1的消息', 'http://www.domain2.com');
};
 
// 接受domain2返回数据
window.addEventListener('message',(e) => {
    console.log(e.data);
}, false);
</script>

www.domain2.com/b.html 页面的内容

<script>
// 接收domain1的数据
window.addEventListener('message',(e) => {
    console.log(e.data);
    if(e.origin !== 'http://www.domain1.com')
    return;
    // 发送消息给domain1
    window.parent.postMessage('来自domain2的消息', e.origin);
}, false);
</script>

window.parent.postMessage是为了解决domain2向domain1传递信息时会出现的跨域问题。因为在domain1中,使用iframe内嵌了一个domain2的页面,因此使用 window.parent

localStorage遇到数据超出5mb了该怎么办

localstorage跨域问题_localstorage跨域读取-CSDN博客

localstorage存储不是5mb 是每个域5m 超了申请其他的域/修改ng配置 postmessge通信往其他域上存取