DjangoRestFramework 使用 django-oauth-toolkit 使网站成为 OAuth Provider(提供方)

Django集成oauth2实现第三方授权登录        最近在项目中需要用到oauth2来进行第三方的授权服务,在网上了解到django-oauth-toolkit已经集成了oauth2方法,将自己心得记录下来.        首先要学习什么是oauth2,阮老师的介绍应该算是很详细了,网址为:理解oauth2.        我用到的是oauth2中的密码模式 resource owner password credentials来进行... 阅读详情

本文为 django-oauth-toolkit 使用记录。(官方文档)

参考阅读:OAuth 2.0 的四种方式

1. 安装

pip install django-oauth-toolkit djangorestframework

2. 使用

创建 Django 项目,目录树如下图: 

│  manage.py
├─provider
│      asgi.py
│      settings.py
│      urls.py
│      wsgi.py
│      __init__.py
└─users
    │  admin.py
    │  apps.py
    │  models.py
    │  tests.py
    │  views.py
    └─__init__.py

并进行相关配置:

# provider/settings.py

INSTALLED_APPS = [
    ...
    'oauth2_provider',    # add
    'users',              # add                
    'rest_framework',     # add
]

LOGIN_URL = '/admin/login/'

OAUTH2_PROVIDER = {
    'SCOPES': {
        'read': 'Read scope',
        'write': 'Write scope',
        'test': 'test api',
        'introspection': 'Introspect token scope',  # 认证 和 资源服务器分离时使用
    }
}

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',  # 使用OAuth登录认证
    )
}

# provider/urls.py

urlpatterns = [
    ...
    path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),  # add
]

3. 编写 API 接口

本次项目中的 User 模型使用 Django 默认的模型。 

# users/views.py
from django.contrib.auth.models import User
from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope
from rest_framework import generics, permissions, serializers


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'email', "first_name", "last_name")


class UserList(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
    queryset = User.objects.all()
    serializer_class = UserSerializer
    required_scopes = ['test']


class UserDetails(generics.RetrieveAPIView):
    permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
    queryset = User.objects.all()
    serializer_class = UserSerializer
    required_scopes = ['test']


# provider/urls.py
from users.views import UserList    # add

urlpatterns = [
    ...
    path('users/', UserList.as_view()),          # add
    path('users/<pk>/', UserDetails.as_view()),  # add
]

 同步数据库、创建超级用户并运行项目:

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver 0.0.0.0:8000

4. 注册/添加 Application 

访问 http://127.0.0.1:8000/o/applications/ 注册/添加 Application 。

其中 client_id 和 client_secret 可以自定义,client_type 为 confidential,grant_type 为 password;添加后如下图所示。

5. 访问 API 接口 

 以下接口测试均使用 python httpie 工具。(pip install httpie)

5.1 获取 access_token (登录)

其中 client_id 和 client_secret 为注册 Application 时填写的内容;username 及 password 为 Django User 模型中的用户名及密码。

# 其中 client_id 和 client_secret 为注册 Application 时填写的内容
# username 及 password 为 Django User 模型中的用户名及密码
http -f POST http://127.0.0.1:8000/o/token/ client_id=self client_secret=self grant_type=password username=admin password=123456

# return
{
    "access_token": "LpyYoQvzPZc2wNGc2MPNM0wkYuXNZt",
    "expires_in": 36000,
    "refresh_token": "qFcUD4Jvj6k1R3xpLWdnrfMRuFfEom",
    "scope": "read write",
    "token_type": "Bearer"
}

 5.2 获取 users 列表数据

访问数据时需要将 access_token 添加到请求头中。 

# 其中 Bearer 后为 access_token
http -f POST http://127.0.0.1:8000/users/ Authorization:"Bearer LpyYoQvzPZc2wNGc2MPNM0wkYuXNZt"

# return
[
    {
        "email": "",
        "first_name": "",
        "last_name": "",
        "username": "admin"
    }
]

 5.3 刷新 access_token

其中 client_id 和 client_secret 为注册 Application 时填写的内容;

grant_type 类型为 refresh_token,意为刷新令牌;

refresh_token 即为 5.1步骤 获取到的刷新令牌。

# 其中 client_id 和 client_secret 为注册 Application 时填写的内容
# refresh_token 即为 5.1步骤 获取到的刷新令牌
http -f POST http://127.0.0.1:8000/o/token/ refresh_token=qFcUD4Jvj6k1R3xpLWdnrfMRuFfEom client_id=self client_secret=self grant_type=refresh_token

# return
{
    "access_token": "GJSkO5nRpfyOanF78MOVlcyGScIWNP",
    "expires_in": 36000,
    "refresh_token": "LfwrybnMAsAwdGRx0GBot6oMFZGBbS",
    "scope": "read write",
    "token_type": "Bearer"
}

5.4 注销/移除 access_token (退出登录)

 其中 client_id 和 client_secret 为注册 Application 时填写的内容;

grant_type 类型为 revoke_token,意为移除令牌;

token 为有效的 access_token。

# 其中 token 为有效的 access_token
http -f POST http://127.0.0.1:8000/o/revoke_token/ token=GJSkO5nRpfyOanF78MOVlcyGScIWNP grant_type=revoke_token client_id=self client_secret=self

# return 
HTTP/1.1 200 OK

6. Permissions 权限

6.1 Django-oauth-toolkit 默认提供了以下五个权限类:

  • TokenHasScope: 
    当 access_token 中的 scope 包含类视图函数中 required_scopes 列表所给范围:
    即  时才会允许访问该视图。  
  • TokenHasReadWriteScope: 
    当 access_token 中的 scope 包含类视图中 required_scopes 列表所给范围并且 scope 中含有 'read' 或者 'write':
    即   时才允许访问该视图的 GET 请求;
     时才允许访问该视图的 POST 请求。
  • TokenHasResourceScope: 
    即 时才允许访问该视图的 GET 请求;
     时才允许访问该视图的 POST 请求。
  • IsAuthenticatedOrTokenHasScope: 
    当当前请求满足 request.user.is_authenticated 或者 TokenHasScope 权限时允许访问该视图。
  • TokenMatchesOASRequirements: 
    该权限类根据请求的模式来匹配相应权限规则;
    若使用该类则应将类视图中 required_scopes 替换为 required_alternate_scopes,示例如下: 
    class SongView(views.APIView):
        authentication_classes = [OAuth2Authentication]
        permission_classes = [TokenMatchesOASRequirements]
        required_alternate_scopes = {
            "GET": [["read"]],
            "POST": [["create"], ["post", "widget"]],
            "PUT":  [["update"], ["put", "widget"]],
            "DELETE": [["delete"], ["scope2", "scope3"]],
        }

6.2 在登录时为 access_token 设置相应权限

在步骤2的配置一步中,

OAUTH2_PROVIDER = {
    'SCOPES': {
        'read': 'Read scope',
        'write': 'Write scope',
        'test': 'test api',
        'music:write': 'Write music',
        'music': 'Test music',
    }
}

即为可供资源拥有者(Django User)选择的 当第三方网站调用该网站资源时获取的权限范围。

逻辑则类似为下图中第三方网站将获得以下权限,资源拥有者可以勾选权限。

在获取 access_token 时若不指定 scope 参数,则默认为所有权限范围。

若要指定权限范围则需要传递 scope 参数(以空格隔开):

# 传递 scope 数据(以空格隔开)
http -f POST http://127.0.0.1:8000/o/token/ client_id=self client_secret=self grant_type=password username=admin password=123456 scope="music:write"

7. Signals 信号

Django-oauth-toolkit 提供了 app_authorized 信号,当用户认证时触发该信号。

from oauth2_provider.signals import app_authorized

def handle_app_authorized(sender, request, token, **kwargs):
    print('App {} was authorized'.format(token.application.name))

app_authorized.connect(handle_app_authorized)

8. Settings.py 配置

更多默认配置可以查看 oauth_provider/settings.py 文件。 

# settings.py
# 指定 oauth_provider 的 models
OAUTH2_PROVIDER_APPLICATION_MODEL = xxx
OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = xxx
OAUTH2_PROVIDER_ID_TOKEN_MODEL = xxx
OAUTH2_PROVIDER_GRANT_MODEL = xxx
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = xxx

OAUTH2_PROVIDER = {
    'SCOPES': {
        'read': 'Read scope',
        'write': 'Write scope',
    },
    'ACCESS_TOKEN_EXPIRE_SECONDS': 60*60*10,  # 访问令牌过期时间,默认为36000秒
    'ACCESS_TOKEN_GENERATOR': None,           # 生成访问令牌的可调用对象的导入路径 默认为None

    'ALLOWED_REDIRECT_URI_SCHEMES': ["http", "https"],
    'AUTHORIZATION_CODE_EXPIRE_SECONDS': 60,  # 建议为10分钟
    'REFRESH_TOKEN_EXPIRE_SECONDS': 60*2,     # 刷新令牌首次使用与过期之间的秒数,建议为2分钟
    'ROTATE_REFRESH_TOKEN': True,             # 当客户端刷新访问令牌时,将向客户端发出新的刷新令牌
    'REQUEST_APPROVAL_PROMPT': 'auto',        # Skip authorization form  Can be 'force' or 'auto'

    ...
}

8. OAuth 认证服务和资源服务分离

8.1 新建另一个 Django 项目: 

# 新建 django project
django-admin startproject resource
cd resource
# 创建 users app
python manage.py startapp users

 8.2 resource 的配置如下:

# resource/settings.py

INSTALLED_APPS = [
    ...
    'users',            # add
    'oauth2_provider',  # add
    'rest_framework',   # add
]

OAUTH2_PROVIDER = {
    'RESOURCE_SERVER_INTROSPECTION_URL': 'http://127.0.0.1:8000/o/introspect/',
    # 'RESOURCE_SERVER_AUTH_TOKEN': 'Lroj8VEAThXttbCiZPxWVlgooVgpGz',  # OR this but not both:
    'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': ('self', 'self'),  # ('client_id', 'client_secret') 
}

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
    )
}

 8.3 编写 API

将 provider 项目中的 views.py 中的代码复制到 resource 相应位置:

# resource/users/views.py
from django.contrib.auth.models import User
from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope
from rest_framework import generics, permissions, serializers


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'email', "first_name", "last_name")


class UserList(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
    queryset = User.objects.all()
    serializer_class = UserSerializer
    required_scopes = ['test']


# resource/urls.py
from users.views import UserList    # add

urlpatterns = [
    ...
    path('users/', UserList.as_view()),          # add
]

 8.4 同步数据库并访问 API

# 同步数据库
python manage.py migrate

# 启动 resource 服务
python manage.py runserver 0.0.0.0:8001

此时 provider 项目在端口 8000 运行,resource 项目在端口 8001 运行;

使用 provider 返回的 access_token 访问 resource 的 API 接口:

http http://127.0.0.1:8001/users/ Authorization:"Bearer Lroj8VEAThXttbCiZPxWVlgooVgpGz"

8.5 查看 resource admin

为 resource 项目创建一个超级用户(admin2),并登陆 /admin/ 后台查看数据:

可以看到 provider 项目上的 access_token 模型实例以被同步到 resource 中,但是 resource 上并没有 access_token 对应的 refresh_token, 同时 provider 项目上的 access_token 对应的 User 也被同步到 resource 中(未设置密码)。

Django初窥门径-oauth登录认证 本文探讨了如何在Django框架中创建和注册应用,自定义身份验证服务,配置`AUTHENTICATION_BACKENDS`,以及创建与API视图相关的登录、注销和用户授权视图。通过优化身份验证和授权流程,可以在Web应用程序中实现更强大的用户身份验证和安全性。文章还详细介绍了如何获取当前登录用户的信息,并提供了有用的资源,以帮助读者深入了解Django身份验证和OAuth。通过这些方法,开发者可以构建安全且可扩展的Web应用程序。 阅读详情

相关推荐

OAuth钓鱼攻击中链接验证失效机制与动态防御策略研究

OAuth 2.0作为现代应用间授权的核心协议,通过授权码模式在不暴露用户密码的前提下实现安全的数据访问。其安全基石在于客户端注册、重定向URI验证、state参数以及PKCE扩展等环节构成的完整验证链。然而,在实际工程实践中,验证逻辑的缺陷常被攻击者利用,例如重定向URI的宽松匹配、state参数缺失或PKCE未启用,导致钓鱼攻击得以实施,窃取授权码或访问令牌,进而引发身份冒充和数据泄露风险。本文聚焦于OAuth钓鱼攻击中链接验证失效的深层机制,探讨了回调URL劫持、开放重定向漏洞等常见手法,并提出了从客

weixin_30653023的博客 323

django-oauth-toolkit(1.2.0) 单点登陆测试

django-oauth-toolkit 可以让django作为认证中心,常用的有两个版本:1.2.0 支持传统的auth2认证.2.2.0 这是最新版 支持OAuth 2.0 扩展协议 PKCE 协议这里我们用1.2.0作为演示,因为我发现微信的单点登陆接口也没有使用PKCE. 为了降低难度(偷懒),那就用1.2.0做演示吧.

jiafeitutu的博客 1626

django-oauth-toolkit:Django开发者实现OAuth2的终极工具包

OAuth2协议,作为互联网上用于授权的一种事实标准,已经在云服务、移动应用和Web应用中得到了广泛应用。它允许用户提供一个安全、有效的授权机制,在不共享账号密码的前提下,授予第三方应用访问他们信息的能力。Django作为一个高级的Python Web框架,它鼓励快速开发和干净、实用的设计。Django遵循着MVC(Model-View-Controller)架构模式,但将其重新命名为MVT(Model-View-Template),以更准确地描述该框架的组件。模型(Model)

weixin_35753291的博客 823

Oauth2认证授权服务(django-oauth-toolkit)搭建及操作流程

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33725807的博客 2008

Python库 | django-oauth-toolkit-0.3.2.tar.gz

python库。 资源全名:django-oauth-toolkit-0.3.2.tar.gz

Stata极简生存分析

1. 导入数据 webuse drugtr,clear *webuse -- Use dataset from Stata website stset,clear *上一行命令导入进来的数据是“生存时间数据”,会比最原始的数据多四列,这一行命令的目的是重置为原始数据,之所以这样做,是为了显示数据处理的全部过程 codebook drug codebook studytime codebook died codebook age *codebook -- Describe data contents 2.

TOP生物信息 2335

BongoCat跨平台桌宠:现代桌面交互的智能伴侣解决方案

在数字工作环境中寻找一丝生动与乐趣?BongoCat跨平台桌宠应用通过创新的实时交互机制,为开发者、游戏玩家和日常用户提供了一个独特的桌面伴侣解决方案。这款基于Tauri框架构建的开源应用,不仅实现了Windows、macOS和Linux三大操作系统的原生兼容,更通过精细的输入设备响应算法,让桌面上的卡通猫咪能够实时模仿用户的键盘敲击、鼠标移动和游戏手柄操作。 ## 技术架构解析:现代化桌面应用

gitblog_00077的博客 669

拆解维修指南:当你的大扭矩电动扳手‘罢工’,如何自己动手排查行星齿轮与谐波传动故障?

本文详细解析了大扭矩电动扳手行星齿轮与谐波传动故障的排查与修复方法。从故障初判、拆解工艺到行星齿轮系统检测、谐波传动维护,提供了一套完整的维修指南,帮助用户快速诊断和解决常见问题,延长工具使用寿命。

weixin_42525582的博客 435

CentOS-WSL完全指南:如何在Windows系统中无缝运行CentOS发行版

CentOS-WSL项目提供了将CentOS QCOW2云镜像转换为适用于Windows Subsystem for Linux (WSL)的RootFS,让用户能够在Windows系统中高效运行CentOS发行版。本指南将详细介绍如何获取、安装和使用这一强大工具,帮助新手轻松上手。 ## 📋 支持的CentOS版本 该项目目前支持多个CentOS版本,满足不同用户的需求: - CentOS

gitblog_00069的博客 1224

金蝶云星空应用开发初级考试题目汇总

摘要:本文为金蝶云星空应用开发初级考试题目汇总

2401_86113757的博客 3799

基于IC555的PWM信号发生器设计:从原理到电机调速与LED调光应用

脉冲宽度调制(PWM)是一种通过调节数字脉冲宽度来控制模拟信号电平的核心技术,其原理在于固定频率下改变高电平占空比,从而等效调节平均电压或功率。这项技术因其高效率、低损耗的特点,在电机调速、LED调光、开关电源等工程领域具有重要价值。本文聚焦于利用经典IC555定时器芯片构建PWM信号发生器,通过分析其无稳态工作模式及改进型电路设计,实现了占空比独立可调且频率稳定的信号输出。文中结合电机驱动与LED调光等具体应用场景,详细阐述了电路参数计算、元件选型、PCB设计及常见问题排查等实践要点,为电子爱好者与工程师

weixin_30342363的博客 396

005-Django OAuth Toolkit 身份认证

Django OAuth Toolkit 身份认证1、最小化安装配置2、创建 API3、注册一个应用程序4、获取 token 并访问 API Django OAuth Toolkit + Django REST Framework 身份认证。 API 官方地址:https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/...

sone 的博客 1482

MacTeX+TeXstudio:Macbook配置本地Latex看这一篇就够了

这个博客记录本人在MacBook上配置本地Latex的流程,其中选择的编译环境为MacTeX,编辑器为TeXstudio。采用的原因主要两点:一个是本人配置的原因主要是用来撰写毕业论文,希望能够尽可能简化设置流程,专注内容表达;第二是由于本人还有一个windows系统的笔记本,想要编辑器在专注写作体验的同时在两个系统上能够都适配,从而减少切换效率,故选择此组合。这篇文章介绍并详细记录了本人的安装流程,有相同需求的同志也可借鉴。

Katherine-XY的博客 6978

Django 架设 Restful API(四)API开发:OAuth2.0 认证和鉴权

1.Django 架设 Restful API(三)

anbuqi的博客 3931

基于STM32设计的老人摔倒检测报警系统(OneNet+MQTT)(235)

基于STM32设计的老人摔倒检测报警系统,不仅能够实时监测老人的位置信息,方便在紧急情况下快速定位,还能通过陀螺仪模块精确检测老人的身体姿态,一旦判断为摔倒状态,系统将立即触发蜂鸣器报警,并通过4G网络向指定联系人发送短信警示,确保老人能够迅速得到援助。此外,系统还支持老人主动按下设备上的按键发送求助短信,增强了系统的灵活性和实用性。

4690

当启动容器的时候提示:exec format error?如何解决问题

Docker容器出现"exec format error"的主要原因是架构不兼容(如ARM设备运行x86镜像)。解决方案包括:1)匹配镜像与宿主机架构;2)构建时指定平台;3)安装QEMU模拟器;4)修复文件权限和格式;5)验证基础镜像兼容性。排查流程应从检查架构匹配开始,逐步验证权限、解释器和换行符等问题。建议优先使用多架构镜像,构建时明确指定平台,并使用buildx工具生成多平台镜像。调试时可进入容器手动验证文件格式和兼容性。

weixin_39291964的博客 2716

django-oauth-toolkit 授权码模式实现案例

django-oauth-toolkit authorization_code

weixin_43901010的博客 924

抓包工具:Fiddler下载、安装、使用 教程

文章目录抓包工具:Fiddler下载、安装、使用 教程一、Fiddler 下载二、Fiddler 安装三、Fiddler 使用3、Statistics 请求的性能数据分析4、Inspectors 查看数据内容5、AutoResponder 允许拦截指定规则的请求6、Composer 自定义请求发送服务器7、Filters 请求过滤规则8、Timeline 请求响应时间9、Fiddler 设置解密HTTPS的网络数据10、Fiddler 内置命令与断点 抓包工具:Fiddler下载、安装、使用 教程 Fidd

Sumarua的博客 17万+

【亲测免费】 Django OAuth Toolkit 使用教程

Django OAuth Toolkit 是一个为 Django 开发者提供的 OAuth2 工具包。它可以帮助你在 Django 应用中轻松实现 OAuth2 认证和授权服务。该工具包广泛使用了 OAuthLib,确保所有实现都符合 RFC 标准。 ### 主要功能 - 保护你的 Django 应用的 Web API - 实现 OAuth2 授权服务器,提供令牌管理 - 提供开箱即用的端点、数...

gitblog_00269的博客 1201

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

django-oauth-toolkit 隐性授权模式实现案例

django rest framework + django-oauth-toolkit 鉴权登录 implicit

weixin_43901010的博客 1360

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

【Vue+Django REST framework】前后端分离平台开发之【项目初始化】

项目初始化 系统环境 python 3.7.3 Django 2.2.4 DjangoRestFramework 3.10.2 第一步:启动虚拟化环境 virtualenv虚拟化环境搭建:https://www.cnblogs.com/bk770466199/p/11366374.html 第二步:安装相关依赖包 #查看当前安装依赖包列表 pi...

weixin_30642029的博客 332

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Django中的社交登录集成:OAuth与第三方认证的实践

接着,我们探讨了用户管理和个性化设置的重要性,并提供了创建个人资料页面、配置个人资料链接以及定制用户模型的方法。最后,我们提出了测试与调试的策略,如单元测试、集成测试、调试工具和日志调试,并强调了用户反馈与改进的重要性。通过不断地测试、调试、收集反馈和改进,我们可以确保社交登录功能在生产环境中稳定可靠,与用户的需求和期望保持一致,为用户提供更好的登录体验。通过允许用户使用他们在其他网站上拥有的账户来登录您的应用程序,社交登录不仅提供了方便,还可以增加用户体验和用户参与度。我们详细讨论了安装和配置。

一键难忘的博客 2852

django rest framework案例

项目环境和工具 Django==1.11 djangorestframework==3.8.2 macos sublime sqlite3 virtualenvwrapper python3.6 postman tip:sublime建立项目环境方法 1. 点击project,选择Add Foler to project 2. Save project as..(保存项目文件目录,建议保...

__sebass博客 1151

Django项目中使用JWT

1.requiremwnts: Django版本:2.2 python版本:3.6 djangorestframework版本:3.1 djangorestframework-jwt版本:1.11 MySQL版本:5.7 注意:使用Django 2.1以上的版本,MySQL数据库必须在5.5以上的版本。 2.新建项目 1)使用pycharm新建一个Django项目,我的项目名称叫:django_j...

weixin_42935779的博客 953

django框架向DRF框架演变过程详解

django框架向DRF框架演变过程详解

喵酱 3020

Django:DjangoRestFramework drf 开发1

1. Django REST framework

程序员五哥 471
上一篇: DjangoRestFramework 使用 simpleJWT 登陆认证
下一篇: DjangoRestFramework 使用 python-social-auth 实现第三方登录
PFFFei
博客等级 码龄9年 137粉丝 17原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值