关于jwt的三个部分,我这里不介绍了,我们看看JWT的使用方式:
首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT。形成的JWT就是一个形同lll.zzz.xxx的字符串。
后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。
前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
5. 后端检查JWT是否存在,及其他,例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选),如果签名验证通过,我们也可以获得此JWT所属的用户。
准备工作
1.安装
pip install djangorestframework-jwt
2.settings.py配置:我这里没有配置全局的权限与认证,等会在单独的接口单独配置;另外配置JWT时效与JWT字符串的前缀;最后设置验证类,主要是验证类中的authenticate方法。
REST_FRAMEWORK = { # 设置所有接口都需要被验证 ‘DEFAULT_PERMISSION_CLASSES‘: ( #’rest_framework.permissions.IsAuthenticatedOrReadOnly’, ), # 用户登陆认证方式 ‘DEFAULT_AUTHENTICATION_CLASSES‘: ( # ‘rest_framework_jwt.authentication.JSONWebTokenAuthentication‘, #’rest_framework.authentication.SessionAuthentication’, #’rest_framework.authentication.BasicAuthentication’, ), } # jwt载荷中的有效期设置 JWT_AUTH = { #token 有效期 ‘JWT_EXPIRATION_DELTA‘: datetime.timedelta(seconds=1800), ‘JWT_AUTH_HEADER_PREFIX‘: ‘JWT‘, } AUTHENTICATION_BACKENDS = ( #选择django自己的验证类 ‘django.contrib.auth.backends.ModelBackend‘, )
urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path(‘admin/‘, admin.site.urls),
#登录成功自动签发token
url(r‘^login/‘, obtain_jwt_token)
]
这里面主要是obtain_jwt_token,实践调用ObtainJSONWebToken.as_view(),ObtainJSONWebToken类下面,有如下serializer_class = JSONWebTokenSerializer
而JSONWebTokenSerializer就是最重要的类,后面如果你的登录不只有username,password时,需要传入更多的参数,需要更改这两个方法。
POSTMAN测试:注意首先确保你的这个用户已经存在
views.py
from rest_framework.views import APIView from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework import authentication from rest_framework.response import Response class IndexViewset(APIView): #单独给这个接口增加JWT认证 authentication_classes = (JSONWebTokenAuthentication, authentication.SessionAuthentication) def get(self, request, *args, **kwargs): return Response(‘POST请求,响应内容‘) def post(self, request, *args, **kwargs): return Response(‘POST请求,响应内容‘)
上面代码中,通过验证则代表JWT有效,那么如何获取到此token所属于的用户,看如下代码:
from rest_framework.views import APIView from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework import authentication from rest_framework.response import Response from rest_framework_jwt.utils import jwt_decode_handler class IndexViewset(APIView): authentication_classes = (JSONWebTokenAuthentication, authentication.SessionAuthentication) def get(self, request, *args, **kwargs): print("验证后的token:",bytes.decode(request.auth)) token_user = jwt_decode_handler(bytes.decode(request.auth)) print(token_user[‘user_id‘]) return Response(‘POST请求,响应内容‘) def post(self, request, *args, **kwargs): return Response(‘POST请求,响应内容‘)
1.如果你想扩展django user字段,新建UserProfile类,继承AbstractUser,settings.py里指定user model,并迁移数据库,请查看
2.如果用户登录中不仅有用户名,密码,还有验证码等,就需要重写验证类,新建CustomBackend类,继承ModelBackend,实现authenticate方法,settings.py里指定
AUTHENTICATION_BACKENDS = (
#选择django自己的验证类
users.views.CustomBackend‘,
#选择django自己的验证类
#‘django.contrib.auth.backends.ModelBackend‘,
)
def authenticate(request=None, **credentials): """ If the given credentials are valid, return a User object. """ for backend, backend_path in _get_backends(return_tuples=True): try: inspect.getcallargs(backend.authenticate, request, **credentials) except TypeError: args = () credentials.pop(‘request‘, None) # Does the backend accept a request keyword argument? try: inspect.getcallargs(backend.authenticate, request=request, **credentials) except TypeError: # Does the backend accept credentials without request? try: inspect.getcallargs(backend.authenticate, **credentials) except TypeError: # This backend doesn‘t accept these credentials as arguments. Try the next one. return None else: warnings.warn( "Update %s.authenticate() to accept a positional " "`request` argument." % backend_path ) else: credentials[‘request‘] = request warnings.warn( "In %s.authenticate(), move the `request` keyword argument " "to the first positional argument." % backend_path ) # Annotate the user object with the path of the backend. return backend.authenticate(*args, **credentials) # The credentials supplied are invalid to all backends, fire signal user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials), request=request)
原文:https://www.cnblogs.com/-wenli/p/13696989.html