【Django3.0功能开发】apps篇:基于API的JWT的全部配置及前后端使用方法

内容介绍

JSON Web Token(JWT)是目前 Token 鉴权机制下最流行的方案,鉴于网上 JWT的介绍众多且不完整,本文将我在项目中Django 如何利用 JWT 实现对 API 的认证鉴权全部流程内容进行梳理。

实现非认证用户不能访问指定内容的功能

传统的 session 和 JWT 的区别

我们以一个用户,获取用户资料的例子

session 流程

  1. 浏览器发起登陆请求。
  2. 服务端验证身份并生成验证信息,存储在服务端,并写入浏览器 Cookie。
  3. 浏览器发起请求获取用户资料,此时Cookie内容也跟随这发送到服务器。
  4. 服务器发现 Cookie 中有身份信息,验明正身。
  5. 服务器返回该用户的用户资料。

JWT 流程

  1. 浏浏览器发起登陆请求。
  2. 服务端验证身份,基于算法将用户标识符打包生成 token,返回给浏览器。
  3. 浏览器发起请求获取用户资料,将刚获取的token一起发送给服务器。
  4. 服务器发现数据中有 token,验明正身。
  5. 服务器返回该用户的用户资料。

你发现了吗?好些并没有什么区别,除了session需要服务端存储一份,而 JWT不需要.

  1. session 存储在服务端占用服务器资源,而 JWT 存储在客户端。
  2. session 存储在 Cookie 中,存在伪造跨站请求伪造攻击的风险。
  3. session 只存在一台服务器上,那么下次请求就必须请求这台服务器,不利于分布式应用。
  4. 存储在客户端的 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>

猜你喜欢

转载自blog.csdn.net/qq_20288327/article/details/112799232
今日推荐