Django初窥门径-oauth登录认证

news2024/11/26 16:25:40

引言

在现代Web应用程序中,用户身份验证和授权是至关重要的组成部分。Django,一个流行的Python Web框架,为用户身份验证提供了内置支持。本文将探讨如何创建和注册Django应用,自定义身份验证服务,配置AUTHENTICATION_BACKENDS,创建视图以及如何使用API视图获取当前登录用户的信息。

Django框架和OAuth身份验证

Django是一个高度可定制的Web框架,它提供了强大的工具和库,用于快速构建Web应用程序。OAuth是一种开放标准,用于安全地授权访问资源,通常用于身份验证。结合Django和OAuth,您可以构建安全和可扩展的用户身份验证和授权系统。

创建应用和注册应用

要创建一个Django应用,您可以使用以下命令:

python manage.py startapp oauth

然后,在项目的settings.py文件中,将应用注册到INSTALLED_APPS列表中,如下所示:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'drf_yasg2',
    'django_filters',
    'account.apps.AccountConfig',
    'oauth'
]

身份验证服务

创建自定义身份验证服务

#!/usr/bin/python
# -*- coding: utf-8 -*-
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.core.cache import cache
from django.db.models import Q
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils import timezone
from rest_framework import exceptions
from rest_framework.authentication import TokenAuthentication as BaseTokenAuthentication

User = get_user_model()


def get_ip(request):
    """获取当前请求ip
    :param request: 当前请求上下文
    :return: ip地址
    """
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip


class EmailOrUsernameModelBackend(ModelBackend):
    """自定义用户验证
    允许用户使用用户名或者邮箱登录
    允许使用密码或者邮箱验证码校验
    """

    def authenticate(self, request, username=None, password=None, login_type='account', **kwargs):
        try:
            user = User.objects.get(Q(username=username) | Q(email=username), is_active=True)
            if login_type == 'account' and user.check_password(password):
                user.last_login = timezone.now()
                user.last_login_ip = get_ip(request)
                user.save()
                return user
            else:
                # 邮箱验证码校验
                code = cache.get('account:email:{0}:login:code'.format(user.email))
                if password == code:
                    return user
                return None

        except User.DoesNotExist:
            return None


class TokenAuthentication(BaseTokenAuthentication):
    """token认证"""
    keyword = 'bearer'

    def authenticate_credentials(self, key):
        token_user_cache_key = f'oauth:token:{key}:user:id'
        if cache.ttl(token_user_cache_key) == 0:
            raise exceptions.AuthenticationFailed('token无效')
        try:
            user_id = cache.get(token_user_cache_key)
            user_token_cache_key = f'oauth:user:id:{user_id}:token'
            if cache.ttl(user_token_cache_key) != 0 and cache.get(user_token_cache_key) == key:
                user = get_object_or_404(User, id=user_id, is_active=True)
                return user, key
            raise exceptions.AuthenticationFailed('token错误')
        except Http404:
            raise exceptions.AuthenticationFailed('token无效')

在自定义身份验证服务中,我们创建了EmailOrUsernameModelBackendTokenAuthentication类,以改进用户身份验证和安全性。EmailOrUsernameModelBackend允许用户使用用户名或邮箱登录,还支持邮箱验证码校验。TokenAuthentication用于验证用户的令牌。这些自定义服务增强了用户身份验证的功能和灵活性。

配置`AUTHENTICATION_BACKENDS

为了使用我们的自定义身份验证服务,我们需要配置AUTHENTICATION_BACKENDS,如下所示:

AUTHENTICATION_BACKENDS = (
    'oauth.authentication.EmailOrUsernameModelBackend',
)

创建视图

登录表单

#!/usr/bin/python
# -*- coding: utf-8 -*-
from django.contrib.auth.forms import AuthenticationForm
from django.forms import widgets


class LoginForm(AuthenticationForm):
    """登录表单"""
    def __init__(self, *args, **kwargs):
        super(LoginForm, self).__init__(*args, **kwargs)
        self.fields['username'].widget = widgets.TextInput(attrs={
            'id': 'username',
            'class': 'form-control',
            'aria-errormessage': 'usernameError',
            'placeholder': '用户名'
        })
        self.fields['password'].widget = widgets.PasswordInput(attrs={
            'id': 'password',
            'class': 'form-control',
            'aria-errormessage': 'passwordError',
            'placeholder': '密码'
        })

登录视图

class LoginView(FormView):
    """登录视图"""
    form_class = LoginForm
    template_name = 'oauth/login.html'

    @method_decorator(sensitive_post_parameters('password'))
    @method_decorator(csrf_protect)
    @method_decorator(never_cache)
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView, self).dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        kwargs['redirect_to'] = get_redirect_uri(self.request)
        return super(LoginView, self).get_context_data(**kwargs)

    def post(self, request, *args, **kwargs):
        form = LoginForm(data=self.request.POST, request=self.request)
        if form.is_valid():
            auth.login(self.request, form.get_user())
            return super(LoginView, self).form_valid(form)
        return self.render_to_response({
            'form': form
        })

    def get_success_url(self):
        authorize_uri = reverse('authorize', request=self.request, kwargs={
            'authorize_type': 'account'
        })
        data = parse.urlencode({
            'response_type': 'token',
            'redirect_uri': get_redirect_uri(self.request)
        })
        return f'{authorize_uri}?{data}'

注销视图

class LogoutView(RedirectView):
    """退出登录"""

    @method_decorator(never_cache)
    def dispatch(self, request, *args, **kwargs):
        return super(LogoutView, self).dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        user = request.user
        user_token_cache_key = f'oauth:user:id:{user.id}:token'
        if cache.ttl(user_token_cache_key) != 0:
            token = cache.get(user_token_cache_key)
            cache.delete(user_token_cache_key)
            token_user_cache_key = f'oauth:token:{token}:user:id'
            if cache.ttl(token_user_cache_key) != 0:
                cache.delete(token_user_cache_key)
        logout(request)
        return super(LogoutView, self).get(request, *args, **kwargs)

    def get_redirect_url(self, *args, **kwargs):
        return get_redirect_uri(self.request)

用户授权视图

class AuthorizeView(RedirectView):
    """用户授权"""

    @method_decorator(never_cache)
    def dispatch(self, request, *args, **kwargs):
        return super(AuthorizeView, self).dispatch(request, *args, **kwargs)

    def get_redirect_url(self, authorize_type, *args, **kwargs):
        request = self.request
        user = request.user
        token = None
        if authorize_type == 'account':
            if user.is_authenticated:
                token = generate_token()
                token_user_cache_key = f'oauth:token:{token}:user:id'
                user_token_cache_key = f'oauth:user:id:{user.id}:token'
                cache.set(token_user_cache_key, user.id, timeout=60 * 60 * 24)
                cache.set(user_token_cache_key, token, timeout=None)
        if token:
            data = parse.urlencode({
                'access_token': token,
                'token_type': 'bearer'
            })
            return f'{get_redirect_uri(request)}#{data}'
        return reverse('login', request=request)

我们创建了登录表单登录视图注销视图用户授权视图。这些视图提供了用户身份验证和授权的功能。登录表单具有定制的字段,登录视图支持密码和邮箱验证码校验,而注销视图用于用户退出登录。用户授权视图负责用户身份验证后的授权操作。

API视图获取当前登录用户信息

定义用户模型序列化器

#!/usr/bin/python
# -*- coding: utf-8 -*-
from django.contrib.auth import get_user_model
from rest_framework import serializers

User = get_user_model()


class UserSerializer(serializers.ModelSerializer):
    """用户模型列化"""

    class Meta:
        model = User
        exclude = ('password',)
        read_only_fields = ('avatar', 'last_login', 'last_login_ip', 'active')

定义视图

from django.shortcuts import render
from rest_framework import permissions
from rest_framework.generics import GenericAPIView

from CodeVoyager import mixins
from .serializers import UserSerializer


# Create your views here.
class UserProfileAPIView(mixins.UpdateModelMixin, mixins.RetrieveModelMixin, GenericAPIView):
    """用户信息api视图"""
    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = UserSerializer

    def get_object(self):
        user = self.request.user
        if user.is_anonymous or not user.is_authenticated:
            return None
        return self.request.user

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

我们定义了API视图,用于获取当前登录用户的信息。这对于构建需要用户信息的应用程序非常有用。我们还提供了用户信息的序列化过程,以及如何使用API视图来检索和更新用户信息。

使用swagger测试

在这里插入图片描述

结语

用户身份验证和授权是任何Web应用程序的核心。通过结合Django和OAuth,您可以创建一个安全和可扩展的身份验证系统,满足不同项目的需求。希望本文能帮助您更好地理解和应用这些概念,从而构建强大的Web应用程序。

拓展和学习

如果您想进一步了解Django身份验证和OAuth,以下是一些有用的资源:

  • Django官方文档: 深入了解Django框架的官方文档。
  • OAuth 2.0官方文档: 了解OAuth 2.0标准的官方文档。
  • Django REST framework: 用于构建RESTful API的Django库的官方网站。

通过这些资源,您可以进一步扩展您的知识和技能,以构建更复杂和安全的Web应用程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1179019.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络数据包传感器简化流量监控

数据包捕获基于数据包镜像的概念,可用于深度数据包检查、测量应用程序的响应时间以及监视服务器、网络和用户行为,该技术还可用于对需要特定信息的某些区域进行广泛分析,尽管它有效,但并非在所有情况下都是必要的。要分析和管理流…

【MogDB/openGauss误删未归档的xlog日志如何解决】

在使用MogDB/openGauss数据库的过程中,有时候大量业务,或者导数据会导致pg_xlog下的日志数量持续增长,此时如果xlog的产生频率太快,而来不及自动清理,极有可能造成pg_xlog目录的打满。如果对数据库的xlog不太了解的时候…

HTTP 协议详解-上(Fiddler 抓包演示)

文章目录 HTTP 协议HTTP 协议的工作过程HTTP 请求 (Request)认识URL关于 URL encode认识 "方法" (method)GET 方法POST 方法其他方法请求 "报头" (header)请求 "正文" (body) HTTP 响应详解状态码响应 "报头" (header) HTTP 协议 HTT…

仪表盘 gauge

option {tooltip: {formatter: {a} <br/>{b} : {c}%},series: [{name: Pressure,type: gauge,startAngle: 225, // 起始角度&#xff0c;同极坐标endAngle: -45, // 终止角度&#xff0c;同极坐标// axisLine: {// //坐标轴轴线// show: false// },// splitLine: {//…

RT-Thread 10. 使用keil4编译GD32F450

1. 修改keil路径 2.增加MCU型号宏定义 3. 在ENV界面输入 scons -c scons --targetmdk44. 编译 scons --verbose提示错误 Warning: L6310W: Unable to find ARM libraries. Error: L6411E: No compatible library exists with a definition of startup symbol __main. Finish…

Pytorch 里面torch.no_grad 和model.eval(), model.train() 的作用

torch.no_grad: 影响模型的自微分器&#xff0c;使得其停止工作&#xff1b;这样的话&#xff0c;数据计算的数据就会变快&#xff0c;内存占用也会变小&#xff0c;因为没有了反向梯度计算&#xff0c;当然&#xff0c;我哦们也无法做反向传播。 model.eval() 和model.train()…

C/C++数的输入输出 2021年6月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C数的输入输出 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C数的输入输出 2021年6月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 输入一个整数和双精度浮点数&#xff0c;先将浮…

Sui发布RPC2.0 Beta,拥抱GraphQL并计划弃用JSON-RPC

为了解决现有RPC存在的许多已知问题&#xff0c;Sui正在准备推出一个基于GraphQL的新RPC服务&#xff0c;名为Sui RPC 2.0。GraphQL是一种开源数据查询和操作语言&#xff0c;旨在简化需要复杂数据查询的API和服务。 用户目前可以访问Sui主网和测试网网络的Beta版本的只读快照…

如何以电商“API”接口的形式帮助电商商家解决货源及运营难题?

如何帮助电商商家解决货源及运营难题&#xff1f;API 接口创建供应链的突出优势应该就是“API”接口的开发与应用了&#xff0c;通过API技术接口&#xff0c;来帮助商家快速实现货源采购及上架等需要。那么具体来说&#xff0c;创胜货源供应链是如何以“API”的形式来帮助电商商…

[第二章—Spring MVC的高级技术] 2.2 置multipart解析器

使用Servlet 3.0解析multipart请求 兼容Servlet 3.0的StandardServletMultipartResolver没有构 造器参数&#xff0c;也没有要设置的属性。 这样&#xff0c;在Spring应用上下文中&#xff0c;将 其声明为bean就会非常简单&#xff0c;如下所示&#xff1a; ● 既然这个Bean方…

计算机毕业设计java+vue+springboot的论坛信息网站

项目介绍 本论文系统地描绘了整个网上论坛管理系统的设计与实现&#xff0c;主要实现的功能有以下几点&#xff1a;管理员&#xff1b;首页、个人中心、用户管理、公告管理、公告类型管理、热门帖子管理、帖子分类管理、留言板管理、论坛新天地、我的收藏管理、系统管理&#…

[C/C++]数据结构 链表OJ题: 反转链表

描述: 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表 示例: 方法一: 让链表指向反向 如图所示: 代码思路: struct ListNode* reverseList(struct ListNode* head) {struct ListNode* n1NULL;struct ListNode* n2head;struct ListNode*…

webgoat-Broken Access ControlI 访问控制失效

Insecure Direct Object References 直接对象引用 直接对象引用是指应用程序使用客户端提供的输入来访问数据和对象。 例子 使用 GET 方法的直接对象引用示例可能如下所示 https://some.company.tld/dor?id12345 https://some.company.tld/images?img12345 https://some.…

【C++】map set

map & set 一、关联式容器二、键值对三、树形结构的关联式容器1. set&#xff08;1&#xff09;set 的介绍&#xff08;2&#xff09;set 的使用 2. multiset3. map&#xff08;1&#xff09;map 的介绍&#xff08;2&#xff09;map 的使用 4. multimap 四、map 和 set 的…

零基础编程入门教程软件推荐,零基础编程自学

零基础编程入门教程软件推荐&#xff0c;零基础编程自学 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#xff0c;而且可以开发大型的软件&#xff0c;象如图这个实例就是用…

freertos多任务

以前我们都是一个任务&#xff0c;假设现在我们创建三个任务,项目工程在上一节网盘 #include "stm32f10x.h" // Device header #include "freertos.h" #include "task.h" #include "usart.h"TaskHandle_t myTaskHan…

什么是商业?什么是企业?什么又是竞争呢?

博主从商了&#xff0c;读了一些关于商业企业跟竞争的关系的书跟文章&#xff0c;突然就有点感受想分享一下&#xff0c;请诸位佬讨论一下&#xff0c;听取一下各位佬的意见&#xff0c;学习一下各位的宝贵经验。 百度百科是这样解释商业企业跟竞争的 商业是以买卖方式使商品流…

Java中的垃圾回收机制

Java中的垃圾回收机制 一、Java垃圾回收机制面试题二、哪些对象应该被回收三、什么时候进行垃圾回收四、怎么清除垃圾对象4.1 标记-清除算法4.2 复制算法4.3 整理算法4.4 分代收集算法&#xff08;主流&#xff09;4.4.1 新生代4.4.2 老年代4.4.3 永久代 五、垃圾收集器六、注意…

zotero跨Windows设备数据同步(利用OneDrive、百度云同步空间等云服务)

zotero跨Windows设备数据同步&#xff08;利用OneDrive、百度云同步空间等云服务&#xff09; 特别注意 不能在多个设备同步使用zotero&#xff0c;否则会导致同步出现异常。 基本原理 同步zotero的数据&#xff0c;即同步全部的文献和笔记、高亮标记信息。而这两类数据分别…

JPCC:燃料电池中用于高效氧还原阴极的拓扑铋(1ī0)面

质子交换膜燃料电池的性能在很大程度上取决于电化学氧还原反应&#xff08;ORR&#xff09;的效率。在这里&#xff0c;宾夕法尼亚大学Andrew M. Rappe等人报道了二维&#xff08;4单层&#xff0c;ML&#xff09;和三维Bi&#xff08;16ML&#xff09;&#xff08;1ī0&#x…