前言
在学 Javaweb 的时候,我们就一直被强调 javaweb
中有四大作用域:pageContext域
,request域
,session域
和 application域
。
pageContext域
里的变量只能在当前页面使用。request域
中对象的有效范围是当前的请求范围。session域
中对象的有效范围是当前的会话范围。application域
中对象的有效范围是整个应用活跃的方位。
了解基本概念后,综上我们可以得出若要进行网络通信,我们一般都要使用到 request
和 session
。由于 request
的作用域只是在请求范围内有效,每次请求后数据就会自动销毁。但有时候我们有想要存储一些能够存储久的数据,例如登录信息
,用户信息
等,request域
是做不到的。
服务端的 session
和 客户端(浏览器)的 cookie
的性质很相似,例如他们都能存储一定期限的数据。如果说Cookie机制是客户身上的“通行证”,那么Session机制就是多个“通行证”组成的“客户明细表”。「从理论上讲,这两个内存间是联系不到一块的。但是由于这两者相似的性质, java 的 jsp
,spring
的 thymeleaf模板
就将他们关联了起来。让人们产生 session
和 cookie
是一致的。」 以 jsp
为例,jsp
是一个特殊的 servlet
程序,虽然我们在里面编写的是 html
语句,但他实际上是会通过 servlet
的一些手段从而转变为前端页面。而 spring
和 thymeleaf
模板也用了这样的方式。因为 session
能够存储时间久的数据,故我们能够用 session
来存储 登录信息
和用户信息
等。
session/cookie 的弊端:
cookie
不是很安全,别人可以分析存放在本地的cookie
并进行cookie
欺骗。- 单个
cookie
在客户端的限制是3K
,就是说一个站点在客户端存放的cookie
不能超过3k
。 session
会在一定时间内保存在服务器上。当访问量怎多,会占用服务器的性能。- 如果是在服务器集群下,或者跨域的服务导向架构,就要求
session
域中的数据共享,即每台服务器都能够读取session
。这个时候我们就只能将session
数据持久化,这是一个非常糟糕的办法。
更加重大的问题
如果用户在浏览器的设置里禁用了 cookie
;或者你的网站是使用前后分离的技术来实现。根本就无法简单的使用 session
就能解决了。一是由于浏览器中没了 cookie
,你的数据无法存储了,二是前后端的联系被撕裂了,单独设置服务端的 session
无法同步到 浏览器的 cookie
中。此时我们就需要用另一个思想:自己在前后端都分别开辟一个空间来存储用户信息。
token
就是这样的一种思想。其流程为:
- 客户端使用用户名跟密码请求登录。
- 服务端收到请求,去验证用户名与密码。
- 验证成功后,服务端再生成一个独一无二的字符串(
Token
令牌),并且将这个令牌保存到服务器的缓存中,再把这个 令牌发送给客户端。 - 客户端收到
Token
以后可以把它存储起来,比如放在Cookie
里或者LocalStorage
里。 - 客户端每次向服务端请求资源的时候需要带着服务端签发的
Token
。 - 服务端收到请求,然后去验证客户端请求里面带着的
Token
,如果验证成功,就向客户端返回请求的数据。
token 带来的好处
此外,token
除了能解决这个现实问题,也带来了许多的好处。
- 无状态,可扩展:在客户端存储的
tokens
是无状态的,并且能够被扩展。基于这种无状态和不存储session
信息,负载均衡器能够将用户信息从一个服务器传到其他服务器上。 - 安全:请求中发送
token
而不再发送cookie
,能够防止CSRF
(跨站请求伪造)。即使在客户端使用cookie
存储token
,cookie
也仅仅是一个存储机制而不是用于认证。并且token
是有时效的,一段时间后用户需要重新验证。 - 可扩展性:能够与其他程序共享数据。
- 多平台跨域:每次都是携带绑定自己信息的令牌访问服务器,当在服务集群环境时,不是只能访问某一台指定的服务器,而是通过从缓存服务器中拿出数据,并更具负载均衡器来选择处理的服务器。
Token 的实现
准备阶段
- 使用
Mysql
数据库 - 开启
Redis
缓存 - 使用
Springboot
框架
代码段
1. 登录
1 | 复制代码<form id="login_form" class="login_form"> |
1 | 复制代码@Resource |
2. 资源访问请求
1 | 复制代码function requestToken(){ |
1 | 复制代码@RequestMapping("/getUser") |
3. 注销
1 | 复制代码function logout(){ |
1 | 复制代码@RequestMapping("/logout") |
本文转载自: 掘金