文章目录
内容介绍
JSON Web Token(JWT)是目前 Token 鉴权机制下最流行的方案,鉴于网上 JWT的介绍众多且不完整,本文将我在项目中Django 如何利用 JWT 实现对 API 的认证鉴权全部流程内容进行梳理。
实现非认证用户不能访问指定内容的功能
传统的 session 和 JWT 的区别
我们以一个用户,获取用户资料的例子
session 流程
- 浏览器发起登陆请求。
- 服务端验证身份并生成验证信息,存储在服务端,并写入浏览器 Cookie。
- 浏览器发起请求获取用户资料,此时Cookie内容也跟随这发送到服务器。
- 服务器发现 Cookie 中有身份信息,验明正身。
- 服务器返回该用户的用户资料。
JWT 流程
- 浏浏览器发起登陆请求。
- 服务端验证身份,基于算法将用户标识符打包生成 token,返回给浏览器。
- 浏览器发起请求获取用户资料,将刚获取的token一起发送给服务器。
- 服务器发现数据中有 token,验明正身。
- 服务器返回该用户的用户资料。
你发现了吗?好些并没有什么区别,除了session需要服务端存储一份,而 JWT不需要.
- session 存储在服务端占用服务器资源,而 JWT 存储在客户端。
- session 存储在 Cookie 中,存在伪造跨站请求伪造攻击的风险。
- session 只存在一台服务器上,那么下次请求就必须请求这台服务器,不利于分布式应用。
- 存储在客户端的 JWT 比存储在服务端的 session 更具有扩展性。
JWT的工作原理
服务端配置流程
安装
pip install djangorestframework-jwt
settings.py配置
根据实际情况进行选择
# token rest framework 配置实现
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
# 'rest_framework.permissions.IsAuthenticated', # IsAuthenticated 仅通过认证的用户
# 'rest_framework.permissions.AllowAny', # AllowAny 允许所有用户
# 'rest_framework.permissions.IsAdminUser', # IsAdminUser 仅管理员用户
# 'rest_framework.permissions.IsAuthenticatedOrReadOnly', # IsAuthenticatedOrReadOnly 认证的用户可以完全操作,否则只能get读取
),
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # 在 DRF中配置JWT认证
'rest_framework.authentication.BasicAuthentication', # 在 DRF中基础认证信息,特定认证修改使用
# 'rest_framework.authentication.TokenAuthentication', # 全局Token认证
# 'rest_framework.authentication.SessionAuthentication', # 全局Session认证
# 'article.auth.MyTokenAuthentication', # 自定义的带过期的认证
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
),
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
# 新版drf schema_class默认用的是rest_framework.schemas.openapi.AutoSchema
}
# 配置jwt载荷中的有效期设置
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # 设置Token有效期
'JWT_AUTH_HEADER_PREFIX': 'JWT', # token前缀:headers中 Authorization 值的前缀
'JWT_ALLOW_REFRESH': True, # 刷新token:允许使用旧的token换新token
# 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(hours=24),# token有效期:token在24小时内过期, 可续期token
'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler', # 5.自定义JWT载荷信息:自定义返回格式,需要手工创建 utils.py的路径和其中方法
}
user/urls.py
from django.urls import include, path
from .views import *
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
# 自动生成路由方法
router = DefaultRouter()
......
urlpatterns = [
# path('user_token/',CreateUserTokenViewSet.as_view(),name="user_token"),# 用户Token
path('token_login/', obtain_jwt_token), # 获取token,登录视图
path('token_refresh/', refresh_jwt_token), # 刷新token
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), # 认证地址
]
urlpatterns += router.urls # 模块地址
项目/urls.py
urlpatterns = [
......
# include 各个模块的内容
path('UserSettings/', include("users.urls")), # apps.users里面的内容
]
urlpatterns += router.urls
user/utils.py
该代码修改源码里的内容,return中返回的内容可以自行修改
def jwt_response_payload_handler(token, user=None, request=None, role=None):
"""
自定义jwt认证成功返回数据
:token 返回的jwt
:user 当前登录的用户信息[对象]
:request 当前本次客户端提交过来的数据
:role 角色
"""
if user.first_name:
name = user.first_name
else:
name = user.username
return {
'authenticated': 'true',
'id': user.id,
"role": role,
'name': name,
'username': user.username,
'email': user.email,
'token': token,
}
应用中设置验证权限
from rest_framework.permissions import IsAuthenticated, BasePermission
from rest_framework_jwt.authentication import JSONWebTokenAuthentication # jwt用户认证
# JWT 权限设置
class MyPermission(BasePermission):
message = '自定义的返回信息'
def has_permission(self, request, view): # 列表数据
# # 这个函数返回True或者False,True表示有权限,False表示没有权限,这个函数同时有三个参数,最后一个是view, 这个是在源码中规定的
# if request.user.id == 0:
# return False
# else:
return True
def has_object_permission(self, request, view, obj): # 对象数据
"""用户是否有权限访问添加了权限控制类的数据对象"""
# 需求:用户能够访问id为1,3的对象,其他的不能够访问
if request.user.is_active == 1:
return True
else:
return False
# 内容详情视图,只有验证通过的才能访问文章正文信息
class ArticleDetailListViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = (IsAuthenticated, MyPermission) # 设置用户认证
authentication_classes = (JSONWebTokenAuthentication,) # 只接受 jwt token认证
......
测试JWT
获取JWT token
JWT token访问
前端使用JWT信息
这里举例将JWT保存到浏览器本地
Javascript和HTML部分
【Django3.0功能开发】JavaScript篇:结合JWT Token使用JS用户登陆、登出功能
Javascript 访问API数据
const token = localStorage.getItem('jwt_token'); //获取本地存储的token
console.log(token);
const info_slug = location.href.split("=")[1]
console.log(info_slut)
function VisitDetail() {
$.ajax({
type: 'get',
dataType: 'json',
contentType: 'application/json',
headers: {
'Content-Type': 'application/json',
'Authorization': 'JWT ' + token, //这里有个空格
},
url: 'http://127.0.0.1:8000/ArticleSettings/ArticleDetail/' + info_slug,
success: function (result) {
console.log(result) // 显示返回API数据结果
$("#article_detail").html(result.detail_content); //在html页面id=test的标签里显示html内容
},
error: function (response, ajaxOptions, thrownError) {
$("#article_detail").html("登陆后方可浏览");
}
})
}
HTML详情页部分
<div class="main" id="article_detail">
{
{ detail_content |safe }}
</div>