You need to enable JavaScript to run this app. Django:HttpOnly Cookie存储的SimpleJWT如何续签访问令牌
优惠活动
大模型
产品
解决方案
定价
更多

Django:HttpOnly Cookie存储的SimpleJWT如何续签访问令牌

结合HttpOnly Cookie的令牌续签实现逻辑

你之前在React里用setTimeout做续签的方案,核心是靠前端JS读取令牌值、判断有效期后主动调用刷新接口——但HttpOnly Cookie对JS完全不可见,这套逻辑根本没法跑,续签逻辑必须全部挪到后端的请求流程里处理,前端全程无感知,反而比前端定时器的方案更可靠,不会出现页面切后台后定时器冻结、用户回来时令牌已过期未续签的问题。

你现有中间件的适用性

你写的这个负责把Cookie内的令牌注入Authorization头的中间件,完全可以承载全链路的续签逻辑,甚至不需要额外开发独立的刷新接口。你目前的中间件只实现了令牌提取、头注入的动作,缺了三个核心环节:

  • 对提取到的access令牌做有效性校验
  • 识别到access过期时,自动读取Cookie里的refresh令牌生成新的有效access
  • 将新生成的access以HttpOnly Cookie的形式下发,同时替换当前请求中的旧令牌值,保证本次请求能正常通过权限校验

具体改造步骤

  1. 提前划定排除范围
    把不需要走令牌校验、续签逻辑的接口加入EXCLUDE_FROM_MIDDLEWARE列表,比如登录接口、登出接口、原有的令牌刷新接口、静态资源路径、健康检查接口。如果不排除这些路径,很容易出现无限递归调用,把服务端调用栈打满。
    注意:所有续签逻辑要写在__call__方法里,不要放到process_view钩子中——process_view是路由匹配完成、即将执行视图函数时才触发,这个时候再替换令牌、设置响应Cookie已经来不及了,必须在请求进入视图逻辑之前就完成所有校验、续签动作。
  2. 分层校验access令牌
    拿到Cookie里的access值后,用SimpleJWT自带的工具类校验,要区分两类异常场景:
    • 遇到令牌被篡改、签名不匹配这类安全级错误,直接放行让后续权限校验模块抛403,绝不触发续签逻辑
    • 只有识别到TokenExpired(令牌过期)异常时,才进入续签流程
  3. 自动完成令牌替换
    触发续签后,从Cookie中提取refresh令牌,用SimpleJWT的RefreshToken类实例化,调用access_token属性就能拿到全新的有效access;之后必须完成两个关键动作:
    • 把新的access值同步更新到当前请求的两个位置:一是直接修改request.COOKIES['access'],二是重新赋值request.META['HTTP_AUTHORIZATION']为新的Bearer {新access}——不然本次请求还是会用旧的过期令牌走后续校验,依旧会报401
    • 等本次请求处理完返回响应时,把新生成的access写入HttpOnly Cookie,所有配置要和你原来存储令牌的参数完全对齐:开启httponly、生产环境开启secure、设置适配站点的same_site、路径设为根路径、有效期和原access的配置保持一致
  4. 兜底异常处理
    如果取到的refresh令牌也过期/无效,就直接在响应中清除access、refresh两个Cookie,返回401状态码,前端收到401统一跳转登录页即可。

改造后的中间件参考代码

from rest_framework_simplejwt.exceptions import TokenError, TokenExpired
from rest_framework_simplejwt.tokens import AccessToken, RefreshToken
from django.conf import settings

class AuthorizationHeaderMiddleware:
    def __init__(self, get_response=None):
        self.get_response = get_response

    def __call__(self, request):
        access_token = request.COOKIES.get('access')
        refresh_token = request.COOKIES.get('refresh')
        new_access_token = None

        # 命中排除路径直接放行
        current_view = request.resolver_match.view_name if request.resolver_match else ""
        if current_view in EXCLUDE_FROM_MIDDLEWARE:
            return self.get_response(request)

        # 校验现有access有效性
        if access_token:
            try:
                AccessToken(access_token)
                # 校验通过,直接注入请求头
                request.META['HTTP_AUTHORIZATION'] = f'Bearer {access_token}'
            except TokenExpired:
                # access过期,尝试用refresh换新
                if not refresh_token:
                    return self.get_response(request)
                try:
                    refresh = RefreshToken(refresh_token)
                    new_access_token = str(refresh.access_token)
                    # 替换当前请求中的令牌,保证本次请求可通过权限校验
                    request.COOKIES['access'] = new_access_token
                    request.META['HTTP_AUTHORIZATION'] = f'Bearer {new_access_token}'
                except TokenError:
                    # refresh也无效,放行让后续校验抛401
                    return self.get_response(request)

        # 处理响应,同步新的access到Cookie
        response = self.get_response(request)
        if new_access_token:
            response.set_cookie(
                key='access',
                value=new_access_token,
                httponly=settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],
                secure=settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
                samesite=settings.SIMPLE_JWT['AUTH_COOKIE_SAMESITE'],
                path=settings.SIMPLE_JWT['AUTH_COOKIE_PATH'],
                max_age=settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'].total_seconds()
            )
        return response

常见踩坑提醒

  • 完全不需要为了续签保留前端定时器调用刷新接口的逻辑,HttpOnly Cookie的内容JS读不到,这类依赖前端触发的请求反而会引入CSRF相关风险,后端中间件自动续签的方案不需要前端介入,还能避免页面休眠导致定时器冻结的问题
  • 续签逻辑只响应令牌过期的异常,遇到签名不匹配、令牌被篡改的场景直接拦截即可,不要拿refresh换新,防止恶意请求绕过校验
  • 写入新的access Cookie时,一定要核对所有安全配置,如果漏设httponly=True,之前把令牌存在HttpOnly Cookie的安全优势就完全失效了

内容的提问来源于stack exchange,提问作者forest

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.03 12:45:08