http 是无状态的,即我们的一次请求结束后,下一次请求,服务端他并不知道是哪个用户发来的。
我们在业务开发中通常是不需要关注是哪个客户端发来的,更多的是关注是哪个用户发来的。
基于这个特点,我们在处理业务逻辑时,就得想方设法地在下一次请求时让服务端知道我是哪个用户。
为什么是下一次呢?
因为首先我们得先登录,才能告诉下一次请求是哪个,否则我们的很多业务就没法开展,这就是所谓的会话管理。
那我们在项目里通常是怎么去管理我们的会话呢?
下面介绍常用的三种方式:
在早期的 web 应用中,我们通常都是使用这种方式来管理会话,它也叫服务端 session 管理,这里快速给大家介绍下它的处理逻辑:
我在网上找了一张流程图,感兴趣可以看下:
这种方式有一个比较大的优点就是安全性好,因为客户端和服务端保持会话的只有 sessionid,只要这个 id 足够的随机,那么别人就很难伪造冒充。
即使伪造了 id 也得是后台存在这个 id 才行。
也不是说这种方式就绝对安全,除非他们通过 CSRF(跨站请求伪造)或者 http 劫持的方式,这又是另个话题了。
这种方式也存在缺点:
前两个问题现在已经有比较成熟的解决方案了,那就是可以使用 Redis 这种中间服务来托管 session 的增删改查。
对于第三个问题,解决起来就比较麻烦了,前后端都需要做处理,因为它最关键部分是 cookie,所以只要保证多个应用之间的 cookie 能互通跨域就 OK。
这也是为啥现在的新系统,都很少使用这种方式了,有比较多的局限性。
这种方式也不是不能用了,毕竟与后来出现的两种管理方式比,这种管理方式他的安全性相对来说是最高的。
比较适合单体服务网站使用。
由于第一种方式会增加服务端的负担和架构的复杂性,所以后来的人想,那我把用户的登录凭证直接存到客户端这样就不会增加服务端的负担了哇。
于是就有了第二种,cookie-based 的管理方式。
即:当用户登录成功后,直接把用户的信息写到 cookie 里面,这样的话,上面的前两个缺点都解决了。
他的流程大致为:
我又找了一张流程图,请看:
这种方式最大的优点就是实现了服务端的无状态化,就是服务端不用再去管理会话了,服务端只需要处理 创建和验证 cookie 里面的登陆凭证就好了。
但是也存在缺点:
最关键的是,不是所有客户端都是浏览器,如果是 APP 这类不太好管理 cookie 的客户端,这两种方式都不太适用。
于是人们便又有了新方式!
这是目前用的比较多的方式,他本质上和第二种没太大区别,只不过第二种登凭证是放 cookie 里面,token-based 是丢给客户端自己管理。
只需要下次请求时把 token(登录凭证) 放请求头里,或者和服务端约定好的地方,只要能获取到的地方,就能达到验证的目的,从而进行会话的管理。
看下流程图:
这种方式服务端不再通过固定的 cookie 进行 token 传递,所以他的灵活性更大,我们只需要:
这种方式解决了第一种方式遗留的的三个问题:资源占用,共享,跨域。
但是也让想伪造 token 的朋友们,更加容易伪造了,所以这种方式最关键的部分就是签名了,签名被破译就等于完全打开了你家的大门。
市面上在处理 token 的问题上,用得比较多就是 JWT 标准。
JWT 本身并没有任何技术实现,他只是定义了 token 的生成的过程和方法,因此不同开发语言也就有了非常多开源的库,实际开发中没太大必要自己再去重复造一个 JWT 的轮子。
第三种方式使用起来固然便捷,但是相对第一种和第二种来说,token 被伪造的可能性高出不是一点半点。
最关键的地方就是签名的算法,相关的加密 Key 千万千万不能泄露了。
这三种方式都有一个终结问题,就是 CSRF 攻击(跨站请求伪造)。
其原理就是通过各种方法偷偷拿到别人的登录凭证,然后伪造请求。
其解决方案针对不同的攻击方式解决方案不一样,完全取决于开发人员对这种攻击方式的了解程度,所以亲们,有空多看看 CSRF 这方面的知识吧!
这里提供一种非常具有代表性的攻击方式:
假如你的 token 是放在 cookie 里面的,正常情况下别人是很难拿到的。
但是假如你的网站里,用户有填写外链的地方,比如评论,此时如果其他地方填写了他的服务地址,又或者你挂了外网的图片地址等。
这样你网站在请求这些外网地址的时候,也就间接的泄露了你的 cookie,假如这个地址是别人的恶意地址,后果就可想而知了。
这就是一种典型的 CSRF 跨站攻击方式。
安全无小事,防范需谨慎!