一篇文章讲清楚Token(Token)

1.初次认识Token

讲一下什么是token,这里的token默认说的是JWT(Json Web Token)

体验Json Web Token

在线生成JWT

token类型

传统的token

传统的token是某个用户登陆之后,服务器返回一个token给用户保存,这个token可能是随机几个字母的组合,并且服务器保留同一份token(比如用redis存储token)

当用户对其他的接口访问时,必须携带这个token,接着服务器判断这个token是否存在与redis中,来判断用户是否已经登陆或者是否有相应的权限

JWT(json web token)

与传统token不同的是,json web token是不用保留一份在服务器上的。

处理流程:

用户登陆之后,服务器通过计算,返回一个按照一定规则加密过的token,token里面包含用户的一些必要信息,比如用户的id、token过期时间

当用户访问其他的接口时,必须携带这个token,接着服务器通过密码来验证这个token

  • 验证token是否是服务器发送的token
  • 验证token过期时间

一切都正确之后,才认为用户是已经登陆、有相应权限的

什么地方会用到token?

在我们前后端分离的开发中,往往需要为多端服务提供认证,比如移动端、web端、小程序端,用户千千万个,接口千千万个

而我们后台怎么样才能保证每个接口只允许有权限的用户来访问呢?(比如只能管理员访问或者只允许登陆的用户访问)

处理办法就是可以返回一个JWT给用户了,然后再从这个token里面判断当前访问接口用户是否符合我们设定

2.Json Web Token的特点

优点

  • 不用储存用户的登录状态信息到服务器上面(比如redis)
  • 同一个Token在不同的服务器器、环境、甚至不同的业务服务都可以使用(只要配置好密钥之类的)
  • 可以手机端、web端、小程序端等等使用

缺点

当然坏处也是有的

比如生成和解析token都需要时间,对比于传统的token验证方式,JWT的生成与解析花的时间会比较多

比如不能取消已经发布存在的token(如果token泄露,在token有效期内,即使用户已经改了密码,攻击者仍然可以拿该tokne为所欲为)

3.Token的原理

如果你已经进入过在线生成JWT这个网站,那么你可以看到这个图

Json web token 包含三部分,头部、数据、签名

JWT结构

头部

头部可以放一些关于token的信息,比如加密算法的信息,创建token的时间,过期的时间,然后用base64进行加密

注意(base64加密后的数据是任何人都可以解密,相当于透明)

比如

{
  "typ": "jwt", # token类型
  "iat": 1603426198, # 生成时间
  "exp": 1603426298 # 过期时间
}

数据

数据这里可以放一些用户的id、权限等级之类的,不过请不要放隐秘的数据,比如(用户的密码、用户的真实姓名)

注意(因为头部和数据这两部分是任何人都可以使用base64解密的)

{
  "say": "我爱你",
  "from": "音宫",
  "to": "小姐姐",
  "uid": 1,
  "role": "admin"
}

签名

签名这部分才是JWT的精华之处,把头部和数据两段字符串进行哈希加密,而这个哈希加密跟base64加密不一样,哈希加密是需要密码的,这个密码不能透露给用户,也就是加密后的内容是不可逆的

比如加密方式如下

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

什么意思呢?

比如头部部分记录的是7点创建,8点过期,即有效期一个小时。

数据部分记录的是某个用户的id。

这个时候服务器通过密码加密之后,会形成一串字符串(只要头部和数据部分的数据不改变,那么每次加密都会形成一样的字符串)

你7-8点之间访问服务接口都可以正常使用

然而,9点钟的时候,黑客发现了你这个token,不过token已经过期,然后黑客改了头部的过期时间,然后再用这个token来访问服务接口

这时候就会出错,为什么呢?

因为头部的信息改动之后,签名就会改变,然而黑客没有生成token的密码,使用黑客不能构造正确的签名

后台验证token的时候,会把头部和数据部分进行用密码加密,生成一个签名,然后再与提交上来的token对比是否一致

4.生成token

这里使用python来演示生成token

首先我们要导入几个包

import time
import json
import base64
import hashlib
import hmac

分别是用于获取当前时间、生成和解析json格式、加密的工具

提前准备好两个参数

exp=60 # token有效期 秒

salt="xxx" # 加密的密码 不能让用户知道

处理头部header

    headers = {
        "typ": "ygt",
        "exp": int(time.time() + exp)  # 过期时间戳
    }

加密头部

first = base64.urlsafe_b64encode(json.dumps(headers, separators=(',', ':')).encode('utf-8').replace(b'=', b'')).decode(
    'utf-8').replace('=', '')

处理数据payload

这里payload存储的是一些用户数据(uid、角色、之类的),注意:不要把隐私的数据放这里(比如用户的密码等等)

payload = {
  "uid": 1,
  "author": "好音宫",
  "admin": 1
}

加密payload

second = base64.urlsafe_b64encode(json.dumps(payload, separators=(',', ':')).encode('utf-8').replace(b'=', b'')).decode(
    'utf-8').replace('=', '')

加密签名

第三部分是签名,签名需要用到salt,也就是加密的密码

把前两部分拼接之后,用哈希加密把签名两部分加密得到一个字符串,这个字符串就叫做签名啦

# 拼接前两部分
first_second = f"{first}.{second}"

# 对前面两部分签名呀
third = base64.urlsafe_b64encode(
    hmac.new(salt.encode('utf-8'), first_second.encode('utf-8'), hashlib.sha256).digest()).decode('utf-8').replace('=',
                                                                                                                   '')

# 拼接签名和前两部分,就叫做token啦
token = ".".join([first, second, third])
print(token)

去官网解析token信息

打印我们的token,复制token

eyJ0eXAiOiJ5Z3QiLCJleHAiOjE2MDM0Mjk4MDd9.eyJ1aWQiOjEsImF1dGhvciI6Ilx1NTk3ZFx1OTdmM1x1NWJhYiIsImFkbWluIjoxfQ.XT6h40ghE6aLxCisrLMbJG8stcP_ujMt1IpxVOMrO0w
去官网解析token信息

发现数据跟我们定义的都一样,证明前面两部分正常,接下来就是验证token了

解析token提取数据

尝试解析token的并且提取数据

参考JWT的代码

# 提取出header、payload、sign
headers = token.split(".")[0]
payload = token.split(".")[1]
sign = token.split(".")[2]

判断这个token是不是有效的,用同样的方法对header和payload加密,看看得到的签名跟token附上来的sign是不是一样

headers_payload = f"{headers}.{payload}"

new_sign = base64.urlsafe_b64encode(
  hmac.new(salt.encode('utf-8'), headers_payload.encode('utf-8'), hashlib.sha256).digest()).decode(
  'utf-8').replace(
  '=',
  '')
print(new_sign == sign)
# if new_sign == sign: 判断是否相等

如果是一样的,证明token有效,再去获取header或者payload信息

if isinstance(payload, str):
  payload = payload.encode('ascii') 

rem = len(payload) % 4 
if rem > 0:
  payload += b'=' * (4 - rem)
# 上面这一部分是解密的部分数据补全格式

payload_data = base64.urlsafe_b64decode(payload) # 解码
data = json.loads(payload_data) # 加载payload信息为可以通过get方法获取里面的值
  
print(data.get("uid")) # 打印token里面的payload部分中的uid数据信息
print(data.get("author")) # 打印token里面的payload部分中的uid数据信息

到这一步

我们就成功创建了token并且可以从token里面提取出来值了

接下来就是根据我们自己的需求封装成一个类了

5.参考文章

jwt.io官网说明文档

Python JWT使用,by dawsonenjoy

JSON Web Token 入门教程,by 阮一峰

欢迎来赞我的GitHub

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353