在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证,我们不再shiyongsession认证机制,而使用Json Web Token认证机制。
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
流程上是这样的:
这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)
策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *
。
那么我们现在回到JWT的主题上。
JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.
链接一起就构成了Jwt字符串。就像这样:
ewogICd0eXAnOiAnSldUJywKICAnYWxnJzogJ0JBU0U2NCcKfQ==.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAi5bC85Y+k5ouJ5pav6LW15ZubIiwKICAiYWRtaW4iOiBmYWxzZQp9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分我们称它为头部(header),第二部分我们称其为荷载(payload,承载的数据),第三部分是签证(signature)。
完整的头部就像下面这样的JSON
{
'typ': 'JWT',
'alg': 'BASE64'
}
然后将头部进行base64加密(加密是对称的,可加可解),构成了第一部分
ewogICd0eXAnOiAnSldUJywKICAnYWxnJzogJ0JBU0U2NCcKfQ==
这里是存放有效信息的地方,有效信息包含三个部分
标准中的注册声明(建议但不强制使用)
公共的声明
这里可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加铭感信息,因为该部分在客户端可解密
私有的声明
是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归为明文信息。
{
"sub": "1234567890",
"name": "尼古拉斯赵四",
"admin": false
}
然后将其进行base64加密,得到jwt的第二部分
ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAi5bC85Y+k5ouJ5pav6LW15ZubIiwKICAiYWRtaW4iOiBmYWxzZQp9
JWT的第三部分是一个签证信息,这个签证信息由三部分组成:
这个部分需要base64加密后的header和base64加密后的payload使用.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt的第三部分。
将这三部分用.
连接成一个完整的字符串,构成了最终的jwt:
ewogICd0eXAnOiAnSldUJywKICAnYWxnJzogJ0JBU0U2NCcKfQ==.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAi5bC85Y+k5ouJ5pav6LW15ZubIiwKICAiYWRtaW4iOiBmYWxzZQp9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
关于签发和核验JWT,我们可以使用Django REST framework JWT扩展来完成。
文档网站:http://getblimp.github.io/django-rest-framework-jwt/
{
"company": "公司信息",
...
}
{
"user_id": 1,
...
}
{
"head": "头的加密字符串",
"payload": "体的加密字符串",
"secret_key": "安全码"
}
账号密码就能根据User表得到user对象,形成的三段字符串用 . 拼接成token返回给前台
注意:登录接口需要做 认证 + 权限 两个局部禁用
import base64 # 导入模块
import json # 序列化
dic = {
"key": "what a beautiful day!",
"value": "but I still have to bring an umbrella"
}
# 序列化并指定编码格式
byte_dic = json.dumps(dic).encode('utf-8')
# 编码
dic_basesf = base64.b64encode(byte_dic)
print(dic_basesf)
# # b'eyJrZXkiOiAid2hhdCBhIGJlYXV0aWZ1bCBkYXlcdWZmMDEiLCAidmFsdWUiOiAiYnV0IEkgc3RpbGwgaGF2ZSB0byBicmluZyBhbiB1bWJyZWxsYSJ9'
# 解码
dic_str = base64.b64decode(dic_basesf)
print(dic_str)
# b'{"key": "what a beautiful day\\uff01", "value": "but I still have to bring an umbrella"}'
pip install djangorestframework-jwt
默认使用auth的user表中创建的用户
# urls.py
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^login/', obtain_jwt_token),
]
postman测试
向后端接口发送post请求,携带用户名密码,即可看到生成的token,携带用户名密码,登陆成功返回token
-通过前端传入的用户名密码,校验用户,如果校验通过,生成token,返回
-如果校验失败,返回错误信息
我们先写了一个视图并配置好了路由
# urls.py
url(r'^text/(?P\d+)' , views.TextAPI.as_view()),
# views.py
class TextAPI(GenericAPIView):
queryset = models.Book.objects
serializer_class = serializer.BookSerializer
def get(self, request, *args, **kwargs):
book = self.get_object()
ser = self.get_serializer(book)
return utils.APIResponse(data=ser.data)
这个时候未登录 没有token也是可以访问的,我们要想加个验证就需要使用jwt
内部的认证类,拿过来局部配置就可以了
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
,但是只配置他是不行的,是没有用的,我们仍然可以访问到具体数据,我们还需要搭配一个权限类来使用from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
class TextAPI(GenericAPIView):
queryset = models.Book.objects
serializer_class = serializer.BookSerializer
# 只配置他是不行的,不管是否登录,都能访问,他需要搭配一个权限类
authentication_classes = [JSONWebTokenAuthentication, ]
permission_classes = [IsAuthenticated, ]
def get(self, request, *args, **kwargs):
book = self.get_object()
ser = self.get_serializer(book)
return utils.APIResponse(data=ser.data)
这是最简单的使用,注意
下面图片中Vaule一定要带 jwt + 一个空格 +token
JSONWebTokenAuthentication
,从request.user就能拿到当前登录用户,如果没有携带,当前登录用户就是匿名用户注意:如果不加DRF内置的IsAuthenticated
那他登录也可以访问,没登录也可以访问
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, ]
同一个业务,部署在多个服务器上
一个业务分拆多个子业务,部署在不同的服务器上
小饭店原来只有一个厨师,切菜洗菜备料炒菜全干。后来客人多了,厨房一个厨师忙不过来,又请了个厨师,两个厨师都能炒一样的菜,这两个厨师的关系是集群。为了让厨师专心炒菜,把菜做到极致,又请了个配菜师负责切菜,备菜,备料,厨师和配菜师的关系是分布式,一个配菜师也忙不过来了,又请了个配菜师,两个配菜师关系是集群
是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
它隐藏了真实的请求客户端, 服务端不知道真实的客户端是谁, 客户端清求的服务都被代理服务器代替来请求。
指的就是加、解密使用的同是一串密钥,所以被称做对称加密。对称加密只有一个密钥作为私钥。常见的对称加密算法:DES,AES等。
对称加密相比非对称加密算法来说,加解密的效率要高得多、加密速度快。但是缺陷在于对于密钥的管理和分发上比较困难,不是非常安全,密钥管理负担很重。
指的是加、解密使用不同的密钥,一把作为公开的公钥,另一把作为私钥。公钥加密的信息,只有私钥才能解密。反之,私钥加密的信息,只有公钥才能解密。
安全性更高,公钥是公开的,密钥是自己保存的,不需要将私钥给别人。缺点:加密和解密花费时间长、速度慢,只适合对少量数据进行加密。