Cookie、Session、Token、JWT 介绍
一、Cookie 技术
Cookie的中文翻译是曲奇,小甜饼的意思。cookie其实就是一些数据信息,类型为“小型文本文件”,存储于电脑上的文本文件中。
Cookie是浏览器实现的一种数据存储技术。用于存储服务器返回给客服端的信息,发送给浏览器(客户端也可进行cookie设置,客户端进行保存。在下一次访问该同一网站时,客户端会将保存的cookie一同发给服务器,服务器再利用cookie进行一些操作。利用cookie我们就可以实现自动登录,保存游览历史,身份验证等功能。
一般情况下,cookie是以键值对进行表示的(key-value),例如name=jack,这个就表示cookie的名字是name,cookie携带的值是jack。
1.1 Cookie的相关属性
Cookie的特点:
- cookie存储在客户端(浏览器),发送请求时自动携带放在请求头中。
- cookie只能以文本的方式保存字符串类型的数据。
- 单个cookie保存的数据不能超过4KB。
- cookie的安全性不高,别人可以分析存放在本地的cookie并进行cookie欺骗。
- cookie默认不可跨域,可通过特殊的操作如设置withCredentials属性为true实现跨域。
**Cookie的生命周期: **cookie有2种存储方式,一种是会话性,一种是持久性。
- 会话性:如果cookie为会话性,那么cookie仅会保存在客户端(浏览器)的内存中,当我们关闭客服端(浏览器)时cookie也就失效了
- 持久性:如果cookie为持久性,那么cookie会保存在用户的硬盘中,直至生存期结束或者用户主动将其销毁。
cookie我们是可以进行设置的,我们可以人为设置cookie的有效时间,什么时候创建,什么时候销毁。
Cookie的属性:
cookie的每个属性都通过 name=value 的形式进行设置,属性之间通过 分号加空格 进行分割。我们可以根据需求去自定义cookie中需要传递的数据,以下是cookied的一些内置原生的可设置的基本属性:
属性名称 | 说明 |
---|---|
domain | 指定cookie所属域名,默认当前域名 |
path | 指定cookie在哪个域名路由下生效,默认为/ |
maxAge | 指定cookie的有效时长,单位为秒。当值小于0时表示当前会话关闭后即失效,为-1时表示删除该cookie。默认为-1。 |
expires | 指定cookie的有效截至日期(现已被maxAge取代)。 |
secure | 指定cookie是否仅被使用安全协议传输。当该值为true,则cookie仅在https中生效,http无效。默认为false |
httpOnly | 若该属性有值,则客户端无法通过JS脚本去获取cookie信息,但仍可以在浏览器的Application工具中手动修改cookie。具备一定程度的安全性。 |
1.2 Cookie的常用方法
客户端设置Cookie选项:expires , domain, path, secure (只有在https协议的网页中, 客户端设置secure类型cookie才能生效), 但无法设置 httpOnly选项。
- document.cookie = "name=xiaoming; age=12 "
**服务端设置Cookie的选项:**expires, domain, path, secure, HttpOnly。
-
new Cookie(String name, String value):创建一个Cookie对象,必须传入cookie的名字和cookie的值
-
getValue():得到cookie保存的值
-
getName():获取cookie的名字
-
setMaxAge(int expiry):设置cookie的有效期,默认为-1。这个如果设置负数,表示客服端关闭,cookie就会删除。0表示马上删除。正数表示有效时间,单位是秒。
-
setPath(String uri):设置cookie的作用域
-
response.addCookie(Cookie cookie):将cookie给客户端进行保存
-
resquest.getCookies():得到客服端传过来的所有cookie对象
二、Session 技术
因为HTTP的无状态性,所以我们没有办法在HTTP发送请求的时候知道当前用户的状态,也就是比如说,当前是哪个用户的之类的这种信息,所以这个时候我们需要session来标识当前的状态。
Session技术是一种用于维护客户端和服务器之间关联的技术。其基本概念在于“会话”,在网络应用中称为**“会话控制”**,指的是一段时间内单个客户端与Web服务器的一连串相关的交互过程。
当用户首次访问Web应用时,Web应用服务器会为每一个游览器(客户端)创建一个唯一的Session ID,这个session是服务器端共享,每个游览器(客户端)独享的。并将这个ID以Cookie的形式返回给浏览器。浏览器在接收到这个Cookie后,会存储它,并在后续的请求中自动发送给服务器。服务器在收到请求后,通过反序列化Session ID来查找对应的Session对象,进而处理请求。
2.1 Session实现原理
session是每一个游览器(客户端)所唯一的,这个是怎么实现的呢?其实,在访问一个网站时,在HTTP请求中往往会携带一个cookie,这个cookie的名字是JSESSIONID,这个JSESSIONID表示的就是session的id,这个是由服务器创建的,并且是唯一的。服务器在使用session时,会根据JSESSIONID来进行不同操作。
-
用户首次访问:用户通过浏览器首次访问Web应用。
-
服务器创建Session:服务器接收到请求后,为用户创建一个唯一的Session对象,并生成一个对应的Session ID。
-
发送Session ID:服务器将Session ID以Cookie的形式返回给浏览器。
-
浏览器存储Cookie:浏览器接收到Cookie后,将其存储在本地。
-
用户后续请求:用户在后续访问Web应用的页面时,浏览器会自动将存储的Session ID发送给服务器。
-
服务器验证Session ID:服务器接收到请求后,从请求中提取出Session ID,并根据这个ID查找对应的Session对象。
-
处理请求:服务器根据找到的Session对象处理请求,并返回响应给浏览器。
2.2 Session的生命周期
Session的生命周期主要涉及Session的创建、使用以及销毁的过程。
当用户首次访问Web应用时,Web应用服务器会为每一个游览器(客户端)创建一个唯一的Session ID,这个session是服务器端共享,每个游览器(客户端)独享的(默认30min)。
至于Session的销毁,有几种情况会导致其失效:
- 用户关闭浏览器:在某些情况下,浏览器关闭会导致Session销毁。但请注意,这通常是通过Session超时机制来实现的,而不是立即销毁。服务器会在一段时间后检测长时间没有活动的Session,并将其从内存中清除。
- 服务器超时销毁:即使用户没有关闭浏览器,如果服务器检测到某个Session长时间没有活动,它也会自动销毁该Session。
- 手动销毁:在某些情况下,如用户注销或管理员操作,可以通过代码显式销毁Session。
需要注意的是,Session是存储在服务器端的,因此其生命周期受到服务器内存和配置的限制。同时,由于HTTP协议的无状态性,Session机制为Web应用提供了一种有效的状态管理方式。
总的来说,Session的生命周期从创建开始,在用户与服务器进行交互的过程中持续存在,直到满足销毁条件时被销毁。这个过程确保了用户在整个会话期间能够保持其状态信息,从而提供了更好的用户体验。
2.3 Session常用方法
- resquest.getSession():得到请求游览器(客户端)对应的session。如果没有,那么就创建应该新的session。如果有那么就返回对应的session
- setAttribute(String s, Object o):在session存放属性
- getAttribute(String s):从session中得到s所对应的属性
- removeAttribute(String s):从session中删除s对应的属性
- getId():得到session所对应的id
- invalidate():使session立即无效
- setMaxInactiveInterval(int i):设置session最大的有效时间。注意,这个有效时间是两次访问服务器所间隔的最大时间,如果超过最大的有效时间,那么这个session就失效了。
2.4 Session应用场景
Session对象在Web开发中有多种应用场景。首先,它可以用于用户认证和授权。在用户登录后,可以将认证信息(如用户名、密码等)存储在Session中,以便在整个会话期间进行身份验证和授权操作。
其次,Session对象也常用于购物车功能,它可以用来存储用户的购物车信息,包括商品的数量、价格和其他相关信息,从而方便地在不同页面之间传递和更新购物车数据。
此外,Session对象还可以被用来缓存一些临时的数据,以提高系统的性能和响应速度,例如:保存验证码。
2.5 session和cookie的比较
-
cookie保存在客户端,session保存在服务端
-
cookie作用于他所表示的path中(url中要包含path),范围较小。session代表客户端和服务器的一次会话过程,web页面跳转时也可以共享数据,范围是本次会话,客户端关闭也不会消失。会持续到我们设置的session生命周期结束(默认30min)
-
我们使用session需要cookie的配合。cookie用来携带JSESSIONID
-
cookie存放的数据量较小,session可以存储更多的信息。
-
cookie由于存放在客服端,相对于session更不安全
-
由于session是存放于服务器的,当有很多客户端访问时,肯定会产生大量的session,这些session会对服务端的性能造成影响。
2.6 Session 总结
Session就是一个存储于服务器的特殊对象,通过session可以实现数据共享,session有一个JSESSIONID,这个是session的唯一标识,使用它可以查找到session。session是会话级别的,对于每一个客户端来说是独享它所拥有的session的,我们使用session在进行页面跳转时,服务端可以利用session进行数据共享。session由服务器进行控制。session的创建和销毁都是服务器进行管理的。服务器会为每一个客户端创建一个session。
Session技术的优点在于其唯一性、方便调用以及不会过多占用资源。每个客户端都会产生唯一的Session ID,确保数据的准确性。同时,Session数据存储在服务器端,方便在任意页面调用。然而,其缺点在于Session在客户端是以Cookie方式保存的,如果客户端禁用了Cookie,那么Session就无法正常工作。
如果是浏览器禁用了cookie功能,或者是Cookie在跨域上有诸多的限制( withCredentials = true 实现跨域 );也就只能够使用URL重写来实现session存储的功能( 通过将SessionId跟在url参数后实现 )。
SessionID 是连接Cookie 和 Session 的桥梁。Session 记录服务器与客户端会话状态的机制,使服务端有状态化,可以记录会话信息。
三、Token 技术
虽然Session具备一定的安全性,但是它的问题在于扩展性不好。比如涉及到服务器集群的场景,要求每台服务器都能够读取session。这种场景一般解决方案是session共享,经典应用需求是A和B两个关联网站间的单点登录,但是这种方案一般工程量较大。于是便出现了另一种服务器无需保存session的技术方案:Token。 以及移动端对Cookie 的支持不是很友好,而Session需要基于Cookie来实现,所以移动端常用的是token。
Token在计算机领域有多种含义和应用,但通常指的是一种用于身份验证和授权的机制。它是一串字符串,用作服务器和客户端之间通信(访问接口API时)所需要的资源**凭证 ** ,以确保通信的安全和有效性。
3.1 Token的实现原理和机制:
-
Token的生成:
- 登录验证:当用户通过用户名和密码或其他认证方式登录系统时,后端服务器会验证这些凭据的有效性。
- 信息封装:验证通过后,后端会生成一个Token字符串。这个Token通常包含用户的一些关键信息,如用**户ID、角色、权限、过期时间、time当前时间戳、sign签名(防止伪造)**等。
- 加密与安全:为了提高安全性,Token通常会使用加密算法进行签名或加密,确保其在传输过程中不被篡改。
- 返回给客户端:生成的Token会被返回给客户端,通常是通过HTTP响应的头部或其他方式。
-
Token的使用:
- 存储Token:客户端收到Token后,会将其保存在本地,如浏览器的Cookie、LocalStorage或移动设备的本地存储中。
- 携带Token:在后续的请求中,客户端会在请求的头部或其他指定位置携带这个Token,以证明其身份和授权。
- 验证Token:服务器收到请求后,会提取出Token,并对其进行验证( 查询数据库获取用户信息进行验证 )。这包括检查Token的有效性、签名或加密是否正确,以及Token是否过期等。
- 处理请求:如果Token验证通过,服务器会处理该请求并返回相应的响应。如果Token无效或过期,服务器可能会返回错误或要求用户重新登录。
-
Token的类型:
-
访问令牌(Access Token):用于访问受保护的资源或执行特定的操作。
-
刷新令牌(Refresh Token):用于在访问令牌过期时获取新的访问令牌,而不需要用户重新登录。
Access Token 的有效期比较短,当 Acesss Token 由于过期而失效时,使用 Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,用户就只能重新登录了。
Refresh Token 及过期时间可存储在服务器的数据库中,只有在申请新的 Acesss Token 时才会验证,不会对业务接口响应时间造成影响,也不需要向 Session 一样一直保持在内存中以应对大量的请求。
-
会话令牌(Session Token):用于标识用户的会话,通常与特定的会话相关联。
-
其他类型:如邀请码、密保令牌(如U盾)等,用于特定的身份验证或授权场景。
-
-
安全性考虑:
- 过期时间:设置Token的过期时间可以减少潜在的安全风险,即使Token被窃取,攻击者也只能在有限的时间内使用它。
- 签名与加密:使用签名算法可以确保Token在传输过程中不被篡改,而加密可以保护Token中的敏感信息不被泄露。
- HTTPS:使用HTTPS协议传输Token可以确保其在网络中的安全传输,防止中间人攻击。
总之,Token是实现身份验证和授权的重要机制,它通过在客户端和服务器之间传递安全的凭证来确保通信的安全性和有效性。在实际应用中,需要根据具体场景和需求选择合适的Token类型和实现方式。
3.2 Token的特点
-
每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里。
-
token签发后存储在客户端( 将用户信息存在在客户端了 ),不占用服务器资源,可减轻服务器压力。
-
使用Token无需担心跨域问题,支持移动端设备,可自由使用。
-
Token使服务端无状态化(Token不会记录客户端之间的状态、会话信息,也不会存储客户端相关信息,只会记录用户的身份,以保客户端发送的请求是合法的),可扩展性好,可以在多个服务间共享。如需要实现有状态化可增加Session。
-
token 完全由应用管理,所以它可以避开同源策略(跨域)
3.3 Token 和 Session 的区别
- Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
- Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重复攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。
- 如果你的用户数据可能需要和 第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了。
四、JWT 技术
JWT(JSON Web Token)是一种特定的Token格式,用于在各方之间安全地传输信息。它由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。
- 头部(Header):通常由两部分组成,一是声明类型,例如JWT;二是声明所使用的算法,例如HMACSHA256或RSA等。
- 负载(Payload):包含了需要传输的数据,如用户的身份信息、权限等。这部分数据可以是自定义的,从而实现了JWT的可扩展性。
- 签名(Signature):由头部、负载和一个密钥生成,用于验证JWT是否被篡改。JWT可以使用HMAC算法或者使用RSA的公钥/私钥对进行签名。
其中,负载部分可以包含用户的身份信息、权限等自定义数据,从而实现JWT的可扩展性。而签名部分则用于验证JWT是否被篡改,确保其安全性。
4.1 JWT的实现原理
JWT(JSON Web Token)的实现原理基于一种开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于在各方之间作为JSON对象安全地传输信息。JWT的实现原理可以概括为以下几个关键步骤:
-
生成JWT
JWT的生成过程通常发生在身份验证成功后,由服务器完成。它包含三个主要部分:头部(Header)、负载(Payload)和签名(Signature)。
- 头部(Header):头部通常包含两部分信息:令牌的类型(即JWT)和所使用的签名算法(如HMAC SHA256或RSA)。这些信息被JSON编码,然后进行Base64编码,形成JWT的第一部分。
- 负载(Payload):负载部分包含声明(Claims),即关于实体(通常是用户)和其他数据的声明。这些声明可以分为三种类型:注册声明(如发行人、过期时间等)、公共声明(可自定义的声明)和私有声明(提供者和消费者之间共享的声明)。负载同样被JSON编码和Base64编码。
- 签名(Signature):签名部分是对头部和负载的编码后的数据进行签名,以确保数据在传输过程中没有被篡改。签名是使用服务器指定的密钥(对于HMAC算法)或服务器的私钥(对于RSA或ECDSA)以及之前指定的签名算法完成的。
-
传输JWT
一旦JWT被生成,它就可以被发送到客户端,通常是通过HTTP响应的头部或作为URL的参数。客户端然后可以在后续的请求中发送JWT,以便进行身份验证和授权。
-
验证JWT
服务器在接收到客户端发送的JWT后,会对其进行验证。验证过程包括:
- 解码JWT:服务器首先解码头部、负载和签名部分。
- 验证签名:服务器使用相同的密钥或公钥以及签名算法来验证JWT的签名。如果签名无效,服务器将拒绝该JWT。
- 检查负载中的声明:服务器会检查负载中的声明,如发行人、过期时间等,以确保它们满足预期的要求。
-
使用JWT中的信息
一旦JWT被验证为有效,服务器就可以使用负载中的信息来执行相应的操作,如授权用户访问特定的资源或服务。
安全性注意事项:
- 密钥管理:用于签名JWT的密钥必须妥善保管,防止泄露。如果使用私钥/公钥对,私钥应该在安全的环境中存储和使用。
- 算法选择:应使用强签名算法,如RSA或ECDSA,而不是较弱的算法,如HMAC。
- 令牌过期:应在负载中设置合理的过期时间,以防止过期的JWT被滥用。
- HTTPS传输:JWT应通过HTTPS进行传输,以防止中间人攻击。
综上所述,JWT的实现原理涉及生成、传输、验证和使用令牌的过程,同时需要关注密钥管理、算法选择和安全性等方面的最佳实践。
4.2 Token和JWT的区别
Token,在更广泛的意义上,通常指一个代表用户或其他实体身份的符号或票据。它可以是任何形式的标识符,用于在系统中识别用户或进行权限检查。Token的应用范围非常广泛,不局限于JWT这种特定格式。都是访问资源的令牌,均可记录userInfo,都使服务端无状态化。
因此,JWT和Token的主要区别在于JWT是Token的一种具体实现形式,具有特定的结构和安全机制。而Token是一个更广泛的概念,可以包含各种不同的格式和用途。在实际应用中,JWT因其安全性、可扩展性和便捷性,被广泛应用于现代Web应用程序的身份验证和授权机制中。而一般的Token则可能根据具体需求和应用场景,具有不同的设计和实现方式。
Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
在实际应用中,JWT(JSON Web Token)和Token的主要区别体现在以下几个方面:
- 格式与结构:
- JWT是一种特定的Token格式,它使用JSON对象来存储信息,并通过Base64编码进行传输。JWT由头部、负载和签名三部分组成,其中头部包含加密算法和类型信息,负载包含用户信息、权限等数据,签名则是对头部和负载的加密结果。
- Token则是一个更广泛的概念,通常是一个字符串,包含用户的身份信息和其他相关信息,例如用户ID、过期时间等。它的格式和结构可以根据具体需求和应用场景进行定制。
- 安全性:
- JWT使用签名来验证Token的有效性,确保Token在传输过程中没有被篡改。这是通过使用秘密(如HMAC算法)或RSA/ECDSA的公钥/私钥对进行签名来实现的。
- 一般的Token可能没有这种签名机制,它们的安全性更多地依赖于存储和传输的安全性。然而,一些Token实现可能也会包含签名或其他加密措施来增强安全性。
- 扩展性:
- JWT由于其结构化的设计,可以存储更多的信息,例如用户的角色、权限等。这使得JWT在需要传递更多用户信息的场景中非常有用。
- 一般的Token可能只能存储有限的信息,适用于简单的身份验证场景。如果需要传递更多信息,可能需要在应用层面进行额外的处理。
- 使用场景:
- JWT因其结构清晰、安全性高和扩展性好的特点,在前后端分离项目中常用于身份验证和授权。它还可以用于信息交换,确保发送者的身份和信息的完整性。
- Token的使用场景则更加广泛,包括网络安全、API访问控制和数字货币交易等。在网络安全中,Token用于身份验证和授权访问;在API开发中,Token用于控制API的访问权限;在数字货币交易中,Token代表数字资产。
总结来说,JWT和Token在实际应用中的主要区别在于格式与结构、安全性、扩展性和使用场景。JWT作为一种特定的Token格式,具有更高的安全性和扩展性,适用于需要传递更多用户信息和进行复杂身份验证的场景。而一般的Token则更加灵活,可以根据具体需求进行定制,适用于各种身份验证和授权场景。