JWT认证
什么是JWT认证:
Json web token(JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,
特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器
获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT构成和工作原理
JWT构成:由三段信息构成,分别是头部,荷载,和签证。像这样
用逗号分隔开的
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
header(一般放公司信息,项目组信息,token采用的加密方式信息)
jwt的头部承载两部分信息: 声明类型,这里是jwt 声明加密算法 通常直接使用HMAC SHA256 完整的头部长这样: { 'typ': 'JWT', 'alg': 'HS256' } 然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
payload(荷载:一般存放用户主键,用户名,签发时客户端信息(设备号,地址)、过期时间)
荷载是存放有效信息的地方。这些有效信息主要包括三个部分 标准中注册的声明(建议但不强制使用): iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: jwt的过期时间,这个过期时间必须要大于签发时间 nbf: 定义在什么时间之前,该jwt都是不可用的. iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避时序攻击。 公共的声明: 可以添加任何信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密 私有的声明: 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息 示例: { "sub": "1234567890", "name": "John Doe", "admin": true } 然后将其进行base64加密,得到jwt的第二部分 eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成: header(base64后的) payload(base64后的) secret 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。 // javascript var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 将这三部分用.链接成一个完整的字符串,构成了最终的jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
关于签发和核验JWT,我们可以使用Django REST framework JWT扩展来完成。
认证原理
JWT认证原理 -用户携带用户名,密码登录我的系统,校验通过,生成一个token(三部分),返回给用户---》登录功能完成 -访问需要登录的接口(用户中心),必须携带token过来,后端拿到token后,把header和payload截出来,再通过一样的加密方式和密码得到一个signature,
和该token的signature比较,如果一样,表示是正常的token,就可以继续往后访问
1 任何语言都有base64的加码和解码,转码方式(加密方式) 2 python中base64的加密与解密 import base64 import json dic_info={ "name": "lqz", "age": 18 } # 转成json格式字符串 dic_str=json.dumps(dic_info) print(dic_str) #eyJuYW1lIjogImxxeiIsICJhZ2UiOiAxOH0= #eyJuYW1lIjogImxxeiIsICJhZ2UiOiAxOH0= # 需要用bytes格式 # 加密 base64_str=base64.b64encode(dic_str.encode('utf-8')) print(base64_str) # 解密 res_bytes=base64.b64decode('eyJuYW1lIjogImxxeiIsICJhZ2UiOiAxOH0=') print(res_bytes)
1 drf中使用jwt,借助第三方https://github.com/jpadilla/django-rest-framework-jwt 2 pip3 install djangorestframework-jwt 3 快速使用(默认使用auth的user表) 1 再默认auth的user表中创建一个用户 2 在路由中配置 path('login/', obtain_jwt_token), 3 用postman向这个地址发送post请求,携带用户名,密码,登陆成功就会返回token 4 obtain_jwt_token本质也是一个视图类,继承了APIView -通过前端传入的用户名密码,校验用户,如果校验通过,生成token,返回 -如果校验失败,返回错误信息 4 用户登录以后才能访问某个接口 -jwt模块内置了认证类,拿过来局部配置就可以 -class OrderView(APIView): # 只配它不行,不管是否登录,都能范围,需要搭配一个内置权限类 authentication_classes = [JSONWebTokenAuthentication, ] permission_classes = [IsAuthenticated,] def get(self, request): print(request.user.username) return Response('订单的数据') 5 用户未登录,不能访问 -class OrderView(APIView): # 只配它不行,不管是否登录,都能范围,需要搭配一个内置权限类 authentication_classes = [JSONWebTokenAuthentication, ] def get(self, request): print(request.user.username) return Response('订单的数据') 6 如果用户携带了token,并且配置了JSONWebTokenAuthentication,从request.user就能拿到当前登录用户,如果没有携带,当前登录用户就是匿名用户 7 前端要发送请求,携带jwt,格式必须如下 -把token放到请求头中,key为:Authorization -value必须为:jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo1LCJ1c2VybmFtZSI6ImVnb24xIiwiZXhwIjoxNjA1MjQxMDQzLCJlbWFpbCI6IiJ9.7Y3PQM0imuSBc8CUe_h-Oj-2stdyzXb_U-TEw-F82WE
1 控制登录接口返回的数据格式如下 { code:100 msg:登录成功 token:asdfasfd username:egon } 2 写一个函数 from homework.serializer import UserReadOnlyModelSerializer def jwt_response_payload_handler(token, user=None, request=None): return {'code': 100, 'msg': '登录成功', 'token': token, 'user': UserReadOnlyModelSerializer(instance=user).data } 3 在setting.py中配置 import datetime JWT_AUTH = { 'JWT_RESPONSE_PAYLOAD_HANDLER': 'homework.utils.jwt_response_payload_handler', }
1 自己实现基于jwt的认证类,通过认证,才能继续访问,通不过认证就返回错误 2 代码如下 class JwtAuthentication(BaseJSONWebTokenAuthentication): def authenticate(self, request): # 认证逻辑() # token信息可以放在请求头中,请求地址中 # key值可以随意叫 # token=request.GET.get('token') token=request.META.get('HTTP_Authorization'.upper()) # 校验token是否合法 try: payload = jwt_decode_handler(token) except jwt.ExpiredSignature: raise AuthenticationFailed('过期了') except jwt.DecodeError: raise AuthenticationFailed('解码错误') except jwt.InvalidTokenError: raise AuthenticationFailed('不合法的token') user=self.authenticate_credentials(payload) return (user, token) 3 在视图类中配置 authentication_classes = [JwtAuthentication, ]
扩展:
1 什么是集群,什么是分布式 集群:就是具有完整功能的系统群集,一个集群节点坏了,其它节点能照常运行,这是不是像一个并联电路?一个并联电路节点坏了,其它节点还是能够正常工作的。 分布式:就是把一个大而复杂的业务计算分配到多个业务节点机器上,即多个节点机器构成一个完整的业务链。一个节点故障,则整个业务链中断奔溃,这就类似一个串联电路。而分布式一个比较突出的使用场景就是微服务中间件的系统架构模式。 2 什么是反向代理,什么是正向代理 有两个人分别是 甲和乙,甲身材矮小,乙身高马大,乙做的很多事情让甲不痛快,但是碍于打不过乙,只能忍耐。有一天乙做了一件事情实在惹怒了甲,于是,甲花钱请了一个打手,打了乙一顿。在这个过程中真正要打乙的是甲。但是乙不知道。打手在这个过程中充当了一个很重要的角色就是我们所说的代理,也可以说是正向代理。 乙被人打了于是拨打110总机报警,但是乙并不知道接电话的是谁。110总机在这里就是代理,也就是反向代理。 总结一下:正向代理隐藏的是发起请求的一端,也就是客户端,反向代理隐藏的处理请求的一端,也就是服务端。 一句话:正向代理隐藏真实的客户端,反向代理隐藏真实的服务器端。
(拓展)自己实现jwt的认证
-{}.{}.asfasdf 前两部分用base64加密,最后一部分用md5+密码----》token 串
-携带串过来 ,取出前俩部分,再用统一的md5+密码得到signature,跟携带过来的比较,通过(drf的认证类中)