You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中非User类型客户端的权限授予方案咨询

问题描述

我有一个基于Django Rest Framework的应用,使用django-oauth-toolkit实现OIDC认证,适配用户交互场景运行良好。现在需要添加服务账号,供外部自动化程序发起请求并创建对象。根据django-oauth-toolkit文档,Client Credential grant适用于机器对机器认证。我已实现该授权类型并通过以下脚本测试:

client_id='axXSSBVuvOyGVzh4PurvKaq5MHXMm7FtrHgDMi4u'
secret='hun***2'
credential="${client_id}:${secret}"
CREDENTIAL=$(printf '%s' "$credential" | base64)
export CREDENTIAL
(
set -x
r=$(\ncurl "http://[::1]:8080/o/token/" \
     -H "Authorization: Basic ${CREDENTIAL}" \
     -H "Cache-Control: no-cache" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -X POST \
     -d "grant_type=client_credentials" \
     -d 'user=2'
)
access_token=$(jq -r '.access_token' <<< "$r")
curl -H "Authorization: Bearer ${access_token}" -H "Cache-Control: no-cache" 'http://[::1]:8080/notify/riskimpacts/'
)

但返回HTTP 403错误:

{"detail":"You do not have permission to perform this action."}

认证成功但无法授权访问视图,因为该凭证未关联User。实际上django-oauth-toolkit会从请求中移除User,因此该授权生成的access_token无法关联User。然而,除了最开放的(近乎匿名)权限外,Django Rest Framework的所有权限都要求存在User。

我了解到授权系统中的"principal"概念,它可以是User,也可以是其他拥有权限和凭证的实体。多数客户端凭证相关文档将该流程描述为代表服务而非用户,但这意味着服务本身是一个principal,应向服务主体授予权限而非用户主体。

我有以下疑问:

  1. 当需要两个服务使用共享密钥进行服务间认证时,客户端凭证授权是否是合适的方案?
  2. 如果合适,在django-oauth-toolkit/Django Rest Framework中,是否必须创建自定义权限类(例如基于scope)才能让客户端凭证访问资源,还是有原生的非匿名方式实现?
  3. 如果不合适,客户端凭证授权的适用场景是什么,更合适的服务间认证方案是什么?
解答

1. 客户端凭证授权是否适合服务间认证?

完全适合。客户端凭证授权就是为机器对机器(M2M)场景设计的,服务之间通过客户端ID和密钥完成认证,不需要关联具体用户,完全符合你所说的"服务作为principal"的需求。你遇到的403问题不是方案选错了,而是权限适配的问题。

2. DRF/django-oauth-toolkit中如何实现非匿名的客户端凭证权限控制?

不需要完全从零编写自定义权限,有两种原生/半原生的实现方式:

  • 基于Scope的权限控制:django-oauth-toolkit自带TokenHasScope和TokenHasReadWriteScope权限类,可在视图中配置允许的scope。你需要先在OAuth应用中设置对应的scope,然后在视图的permission_classes里添加这些类,同时指定required_scopes属性。示例代码:
    from rest_framework.views import APIView
    from oauth2_provider.contrib.rest_framework import TokenHasScope
    
    class RiskImpactView(APIView):
        permission_classes = [TokenHasScope]
        required_scopes = ['risk_impact:write']
    
    只要客户端凭证请求的token包含指定scope,就能通过权限校验,无需关联User。
  • 关联虚拟用户:如果现有权限体系严重依赖User模型,可以给每个服务客户端创建对应的"虚拟用户",然后在django-oauth-toolkit配置里开启OAUTH2_PROVIDER_CLIENT_CREDENTIALS_USER_MODEL,让客户端凭证认证后自动关联这个虚拟用户。之后就能复用现有的基于User的权限体系(比如Django的ModelPermissions)。不过这种方式相当于把服务主体映射到User实体,不如基于Scope的方式纯粹。

另外注意:你测试脚本里的-d 'user=2'是无效的,客户端凭证授权流程不接受user参数,django-oauth-toolkit会忽略该参数,无需传递。

3. 客户端凭证的适用场景及替代方案

客户端凭证的核心适用场景就是:无用户参与的M2M交互,比如定时任务、服务间数据同步、自动化脚本调用等,这些场景下服务本身就是权限主体。

如果非要替代,可选方案有:

  • API密钥认证:直接在请求头传递预生成的密钥,实现简单,但没有token过期、scope控制等特性,安全性和灵活性不如OAuth2客户端凭证。
  • JWT直接认证:服务之间直接交换签名的JWT,不需要请求token端点,但需要自行管理密钥和签名逻辑,复杂度较高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 06:53:23