cookie 是浏览器里面能存储的一种数据,仅仅是存储功能。
cookie由服务器生成,发送给浏览器,浏览器把cookie以键值对的形式保存文件内,以后请求该网站时会发到服务器。cookie是存在客户端上的,故浏览器有一些限制保证cookie不会被恶意使用。同时也不会占据太多磁盘空间,所以每个域的cookie数量是有限的。
接口测试的时候会默认存在requests请求中(python)
session 的意思简单来说就是会话。类似在生活中沟通事情,只要一直在进行,这个沟通就没有结束,直到两者不想沟通结束。----需要注意的是,有时候登录服务器通过加密形式返回的session_id本质是token。
类似的道理,服务器要知道跟谁在沟通。为了区分,服务器给客户端分配“身份标识”(字符串),之后向服务器发请求,都带上这个“身份标识”(字符串),服务器就知道沟通(请求)的是谁了,服务器也会有一份校验的标识。沟通的对象(客户端)怎么保存这个“身份标识”,有很多种的形式,浏览器作为客户端一般默认 cookie存储。
(1)服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。
(2)session有有效期,他是指停留在当前页面不操作开始计算,比如设置了2小时,当你操作的时候登录状态一直保持,当你不操作的时候,等过了2小时你再去访问,此时登录就失效了
具体的实现是通过客户端与服务器的对比实现的,一般采用cookie存放session
生成:浏览器第一次访问服务器,服务器会创建一个session,同时为该session生成一个唯一的会话的key,也就是sessionid。
然后,将sessionid及对应的session分别作为key和value保存到缓存中,也可以持久化到数据库中或者redis当中。
服务器再把sessionid,以cookie的形式发送给客户端,这样浏览器下次再访问时,会直接带着cookie中的sessionid,然后服务器根据sessionid找到对应的session进行匹配。
总结:session会在服务器中存放,一般放在redis中,速度会快一些。存session和失效时间,查找session是否失效,从而判断登录状态,如果有效可以进行请求
基于token的验证,其实简单来讲就是:
用户登录成功后,服务器通过一系列算法返回一个字符串,以后每次的请求都需要这个串。
复杂来说Token的身份验证过程:
用图表示就是:
注意:服务器端校验的时候,不是跟过去的token对比,而是解码判断token数据是否有效,一般判断时间戳
加密算法:
RSA、MD5、SHA
JWT:我们在做接口测试的时候,直接调用登录接口就可以登录成功获取到token。但是有的时候,系统间调用的接口还会用到JWT
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准
在python中是import jwt包(安装pip install jwt,安装出来的是PyJWT),常用的用法是jwt.encode(payload ,key, algorithm)
payload栽荷:
存放一些标准的内容,比如iss、exp、alg(声明算法)等。非必须
key密钥(也有的写为secret):
与其他系统规定好的密钥(暗号),无默认、必传
algorithm
指明签名算法方式,默认HS256
封装的方法,调用的时候直接get_jwt_encode(key=secret_key, iss=iss)
使用Django中的JSONWebTokenAuthentication源码和原理
jwt token本质存储的是明文的用户名失效时间等内容
在登录的时候调用 jwt_encode_handler login_views.py文件
'token': jwt_encode_handler(payload)
在判断是否是登录的时候调用 jwt_decode_handler
JSONWebTokenAuthentication继承
BaseJSONWebTokenAuthentication
def authenticate(self, request): """ Returns a two-tuple of `User` and token if a valid signature has been supplied using JWT-based authentication. Otherwise returns `None`. """ jwt_value = self.get_jwt_value(request) if jwt_value is None: return None try: payload = jwt_decode_handler(jwt_value) except jwt.ExpiredSignature: msg = _('Signature has expired.') raise exceptions.AuthenticationFailed(msg) except jwt.DecodeError: msg = _('Error decoding signature.') raise exceptions.AuthenticationFailed(msg) except jwt.InvalidTokenError: raise exceptions.AuthenticationFailed() user = self.authenticate_credentials(payload) return (user, jwt_value)
payload = jwt_decode_handler(jwt_value)
两次jwt.decode参数不一样,返回结果不同,下面判断时间
def jwt_decode_handler(token): options = { 'verify_exp': api_settings.JWT_VERIFY_EXPIRATION,} unverified_payload = jwt.decode(token, None, False) secret_key = jwt_get_secret_key(unverified_payload) return jwt.decode(
token,api_settings.JWT_PUBLIC_KEY or secret_key, api_settings.JWT_VERIFY,
options=options, leeway=api_settings.JWT_LEEWAY, audience=api_settings.JWT_AUDIENCE, issuer=api_settings.JWT_ISSUER, algorithms=[api_settings.JWT_ALGORITHM])
utils文件中还有jwt_payload_handler方法,用来编码的
有用的是
jwt.decode(token, None, False)
_jwt_global_obj = PyJWT() encode = _jwt_global_obj.encode decode = _jwt_global_obj.decode
其中
verify用来判断是否校验时间,如果校验使用_validate_claims方法
def decode(self, jwt, # type: str key='', # type: str verify=True, # type: bool algorithms=None, # type: List[str] options=None, # type: Dict **kwargs): payload, _, _, _ = self._load(jwt) if options is None: options = {'verify_signature': verify} else: options.setdefault('verify_signature', verify) decoded = super(PyJWT, self).decode( jwt, key=key, algorithms=algorithms, options=options, **kwargs ) try: payload = json.loads(decoded.decode('utf-8')) except ValueError as e: raise DecodeError('Invalid payload string: %s' % e) if not isinstance(payload, Mapping): raise DecodeError('Invalid payload string: must be a json object') if verify: merged_options = merge_dict(self.options, options) self._validate_claims(payload, merged_options, **kwargs) return payload
如果校验时间执行_validate_claims,其中有代码片段,三段,一直到if exp < (now - leeway),现在时间和解析出的时间进行比对
now = timegm(datetime.utcnow().utctimetuple())
if 'exp' in payload and options.get('verify_exp'): self._validate_exp(payload, now, leeway)
def _validate_exp(self, payload, now, leeway): try: exp = int(payload['exp']) except ValueError: raise DecodeError('Expiration Time claim (exp) must be an' ' integer.') if exp < (now - leeway): raise ExpiredSignatureError('Signature has expired')
import jwt import datetime import time # from rest_framework_jwt.utils import jwt_encode_handler # from rest_framework_jwt.utils import jwt_decode_handler SECRET_KEY = 'test123' JWT_SECRET_KEY = SECRET_KEY # 解码模拟,其中JWT_VERIFY判断是否要校验时间,leeway用来做时间展开 token = '' test = jwt.decode(token, None, False) print(test) time_local = time.localtime(test["exp"]) exp = time.strftime("%Y-%m-%d %H:%M:%S", time_local) print(exp) JWT_VERIFY = True unverified_payload = jwt.decode(token, SECRET_KEY, JWT_VERIFY, leeway=111) print(unverified_payload)
# 编码 payload = { 'user_id': 24, 'username': "tch", 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=0), # 'exp': 1682508752 } # jwt编码在登录中 # jwt_value = jwt_encode_handler(payload) # 这里面代码会把exp转换成时间戳 jwt_value = jwt.encode( payload, SECRET_KEY, 'HS256' ).decode('utf-8') print(payload) print(jwt_value)
附录:base64编码和解码
# base64编码和解码 from jwt.utils import base64url_decode, base64url_encode import base64 # 本质是base64中的urlsafe_b64decode调用b64decode payload = '1111'.encode('utf-8') payload_encode = base64url_encode(payload).decode('utf-8') print(payload_encode) payload = '2222'.encode('utf-8') payload_encode = base64url_encode(payload).decode('utf-8') print(payload_encode) payload_decode = base64url_decode(a) print(payload_decode)