Django Oauth 工具包:用户数据自省

问题描述 投票:0回答:2

当前情况:

我正在使用 Introspect 来验证身份验证服务器上的访问令牌。此调用仅从身份验证服务器返回用户的“用户名”并将其保存在资源服务器中。同一用户在认证服务器和资源服务器上的Id不一定相同。

期望的场景:

我想接收有关用户的更多数据(电子邮件、电话号码、地址等)并将其保存在资源服务器中。

到目前为止我所做的:

我修改了

django-oauth-toolkit/oauth2_provider/views/introspect.py/ get_token_response
以返回我需要的数据。

还剩什么:

如何将这些数据保存到资源服务器中?或者每当我需要用户数据时对身份验证服务器进行 api 调用会更好吗?

python django oauth-2.0 django-oauth django-oauth-toolkit
2个回答
3
投票

我通过修改 Auth-Server 中 IntrospectTokenView 中的 get_token_response 来实现这一点

def get_token_response(token_value=None):
        try:
            token = get_access_token_model().objects.select_related(
                "user", "application"
                ).get(token=token_value)
        except ObjectDoesNotExist:
            return HttpResponse(
                content=json.dumps({"active": False}),
                status=401,
                content_type="application/json"
            )
        else:
            if token.is_valid():
                data = {
                    "active": True,
                    "scope": token.scope,
                    "exp": int(calendar.timegm(token.expires.timetuple())),
                }
                if token.application:
                    data["client_id"] = token.application.client_id
                if token.user:
                    data["username"] = token.user.get_username()
# TODO: DANGER ZONE
# Pass extra parameters
# ------------------------------------------------------------------------------
                    data["email"] = token.user.email
                    data["phone_number"] = token.user.phone_number
                    data["is_company"] = token.user.is_company
                    customer = token.user.customer
                    data["designation"] = customer.designation
                    company = customer.company
                    data["company"] = company.company_name
# ------------------------------------------------------------------------------
                return HttpResponse(content=json.dumps(data), status=200, content_type="application/json")
            else:
                return HttpResponse(content=json.dumps({
                    "active": False,
                }), status=200, content_type="application/json")

以及资源服务器中 OAuth2Validator 中的 _get_token_from_authentication_server

def _get_token_from_authentication_server(
            self, token, introspection_url, introspection_token, introspection_credentials
    ):
        headers = None
        if introspection_token:
            headers = {"Authorization": "Bearer {}".format(introspection_token)}
        elif introspection_credentials:
            client_id = introspection_credentials[0].encode("utf-8")
            client_secret = introspection_credentials[1].encode("utf-8")
            basic_auth = base64.b64encode(client_id + b":" + client_secret)
            headers = {"Authorization": "Basic {}".format(basic_auth.decode("utf-8"))}

        try:
            response = requests.post(
                introspection_url,
                data={"token": token}, headers=headers
            )
        except requests.exceptions.RequestException:
            log.exception("Introspection: Failed POST to %r in token lookup", introspection_url)
            return None

        # Log an exception when response from auth server is not successful
        if response.status_code != http.client.OK:
            log.exception("Introspection: Failed to get a valid response "
                          "from authentication server. Status code: {}, "
                          "Reason: {}.".format(response.status_code,
                                               response.reason))
            return None

        try:
            content = response.json()
        except ValueError:
            log.exception("Introspection: Failed to parse response as json")
            return None

        if "active" in content and content["active"] is True:
            if "username" in content:
                user, _created = UserModel.objects.get_or_create(
                    **{UserModel.USERNAME_FIELD: content["username"]}
                )
# TODO: DANGER ZONE
# Adding extra data to user profile and create company
# ------------------------------------------------------------------------------
                user.email = content["email"]
                user.phone_number = content["phone_number"]
                user.is_company = content["is_company"]

                customer, _created_customer = CustomerModel.objects.get_or_create(
                    user = user
                )
                customer.designation = content["designation"]

                company, _created_company = CompanyModel.objects.get_or_create(
                    company_name = content["company"]
                )
                customer.company = company

                customer.save()
                user.save()
# ------------------------------------------------------------------------------
            else:
                user = None

            max_caching_time = datetime.now() + timedelta(
                seconds=oauth2_settings.RESOURCE_SERVER_TOKEN_CACHING_SECONDS
            )

            if "exp" in content:
                expires = datetime.utcfromtimestamp(content["exp"])
                if expires > max_caching_time:
                    expires = max_caching_time
            else:
                expires = max_caching_time

            scope = content.get("scope", "")
            expires = make_aware(expires)

            access_token, _created = AccessToken.objects.update_or_create(
                token=token,
                defaults={
                    "user": user,
                    "application": None,
                    "scope": scope,
                    "expires": expires,
                })

            return access_token

。 现在我想知道如何扩展类并添加额外的代码而不是直接修改源代码?感谢任何帮助。


0
投票

回答如何扩展

IntrospectTokenView
来覆盖
get_token_response
方法。在与其他 Django 应用程序文件夹相同的级别创建一个新的“oauth2”目录。添加新的空
__init__.py
views.py
文件。

oauth2/views.py:

from calendar import timegm
from django.core.exceptions import ObjectDoesNotExist
from django.http import JsonResponse
from oauth2_provider.views.introspect import IntrospectTokenView
from oauth2_provider.models import get_access_token_model


class CustomIntrospectView(IntrospectTokenView):

    @staticmethod
    def get_token_response(token_value=None):
        try:
            token = (
                get_access_token_model().objects.select_related("user", "application").get(token=token_value)
            )
        except ObjectDoesNotExist:
            return JsonResponse({"active": False}, status=401)
        else:
            if token.is_valid():
                data = {
                    "active": True,
                    "scope": token.scope,
                    "exp": int(timegm(token.expires.timetuple())),
                }
                if token.application:
                    data["client_id"] = token.application.client_id
                if token.user:
                    data["username"] = token.user.get_username()
                    # add extra key-value pairs here:
                    data["email"] = token.user.email
                    data["phone_number"] = token.user.phone_number
                    data["is_company"] = token.user.is_company
                    customer = token.user.customer
                    data["designation"] = customer.designation
                    company = customer.company
                    data["company"] = company.company_name
                return JsonResponse(data)
            else:
                return JsonResponse({"active": False})

根据您将

oauth2_views
包含到主项目
urls.py
的方式,您需要包含此修改后的视图类。如果您遵循 Django Oauth Toolkit 文档,则需要更改:

...

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

致:

from django.contrib import admin
from django.urls import path, include
from oauth2.views import CustomIntrospectView
from oauth2_provider import views as oauth2_views


oauth2_endpoint_views = [
    path('authorize/', oauth2_views.AuthorizationView.as_view(), name="authorize"),
    path('token/', oauth2_views.TokenView.as_view(), name="token"),
    path('revoke-token/', oauth2_views.RevokeTokenView.as_view(), name="revoke-token"),
    path('introspect/', CustomIntrospectView.as_view(), name="introspect"),
]

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

使用参考:https://django-oauth-toolkit.readthedocs.io/en/latest/advanced_topics.html#overriding-views

© www.soinside.com 2019 - 2024. All rights reserved.