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

本文为 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_idclient_secret 可以自定义,client_type 为 confidential,grant_type 为 password;添加后如下图所示。

5. 访问 API 接口 

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

5.1 获取 access_token (登录)

其中 client_idclient_secret 为注册 Application 时填写的内容;usernamepassword 为 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_idclient_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_idclient_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-toolkit是一个用于处理OAuth 2.0协议的第三库,它简化了在Django应用中实现OAuth认证流程的过程。配置一个基本的授权页面主要包括以下几个步骤: 1. **安装**: 首先需要在你的Django项目中安装`django-oauth-toolkit`,可以使用pip命令: ``` pip install djangorestframework-django-oauth-toolkit ``` 2. **添加依赖到settings.py**: 将`rest_framework`和`oauth2_provider`添加到你的INSTALLED_APPS列表中。 3. **设置授权端点**: 在`urls.py`文件中,通常会包含OAuth2Provider的应用URL模式,比如: ```python from rest_framework import routers from oauth2_provider import views as o2p_views router = routers.DefaultRouter() router.register(r'authorize', o2p_views.AuthorizationView) router.register(r'token', o2p_views.TokenView) urlpatterns = [ ..., path('oauth/', include(router.urls)), ] ``` 4. **配置授权视图**: 需要创建一个模板文件,如`templates/oauth/authorize.html`,这个模板将展示给用户进行授权的表单。 5. **启动认证服务**: 在视图函数中,你可以选择启动一个本地服务器让用户访问授权页面,通常是通过`o2p_views.start_authorization_server()`。 6. **触发授权流程**: 用户在浏览器中打开授权页面后,需要同意应用程序的权限请求。完成授权后,会返回一个授权码或者直接生成一个access token。 7. **客户端配置**: 如果你有前端应用,需要注册客户端并获取客户端ID和密钥,以便在授权成功后进行后续的token请求。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值