django 开发网站-(App)user 使用第三方登陆

一个网站的用户系统可谓是核心系统,这里我放在最前面来创建。我们将会用到githu的第三方登陆依赖的还是django的allauth。并添加模板函数,来获取用户的信息,并对前端代码进行处理,作选择判断。

主要还是介绍在win10上面的操作过程

主要用的技术:python3.8;django2
编辑器/ide:vscode、sublime——text、powershell、web
githu项目地址:https://github.com/Freen247/django_blog 老爷们,给个star吧
网站前端的初始化静态文件存在我的github上哦:https://github.com/Freen247/django_blog/bl...

python开发,虚拟环境第一步。

    PS F:\demo> .\env\Scripts\activate
    (env) PS F:\demo>

django的第三方登陆

Django-allauth,它不仅包含一整套的本地注册、登录、管理的解决方案,还支持GitHub、Twitter、微博、微信甚至百度等几十种第三方登录方式

下载包:

(env) PS F:\demo\django_blog> pip install django-allauth
(env) PS F:\demo\django_blog> pip install django-crispy-forms

在settings中修改注册的app。
注释掉原来的登陆方式

    INSTALLED_APPS = [
        'django.contrib.admin',
        # 'django.contrib.auth',# 原来的登陆方式可以删除了。
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        #apps
        'apps.index',
        #lib
        'imagekit',  # 注册 imagekit处理压缩图片
        'mdeditor',#django mdeditor富文本编辑器
        'uuslug',#将中文转化成拼音 slug 的插件
        'markdown',#python自带的md翻译工具
         # allauth需要注册的应用
        'crispy_forms',
        'django.contrib.auth',
        'django.contrib.sites',
        'allauth',
        'allauth.account',
        'allauth.socialaccount',
         # github 登陆 
        'allauth.socialaccount.providers.github',
    ]

在settings.py中配置登陆的回调路由:

# 在继承user时要重载AUTH_USER_MODEL
AUTH_USER_MODEL = 'user.Ouser'
# 登陆成功后的回调路由
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/'
SOCIAL_AUTH_REDIRECT_IS_HTTPS = True
AUTHENTICATION_BACKENDS = (
    # auth 身份验证 与 allauth 无关
    'django.contrib.auth.backends.ModelBackend',
    # allauth 身份验证 
    'allauth.account.auth_backends.AuthenticationBackend',
)

# Email setting
# SMTP服务器,我使用的是sendclound的服务
# 是否使用了SSL 或者TLS
#EMAIL_USE_SSL = True
#EMAIL_USE_TLS = True
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.outlook.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'aboyinsky@outlook.com'
EMAIL_HOST_PASSWORD = '1026shenyang'
DEFAULT_FROM_EMAIL = 'aboyinsky@outlook.com'

# 这里是随便写的一个 也可以是 /accounts/logout/ 测试比较随便
LOGIN_REDIRECT_URL = '/'
# 要求用户注册时必须填写email
ACCOUNT_EMAIL_REQUIRED = True
# 注册中邮件验证方法:“强制(mandatory)”,“可选(optional)【默认】”或“否(none)”之一。
# 开启邮箱验证的话,如果邮箱配置不可用会报错,所以默认关闭,根据需要自行开启
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
# 作用于第三方账号的注册
# SOCIALACCOUNT_EMAIL_VERIFICATION = 'optional' | 'mandatory' | 'none'
# 邮件发送后的冷却时间(以秒为单位)
ACCOUNT_EMAIL_CONFIRMATION_COOLDOWN = 10
# # 邮箱确认邮件的截止日期(天数)
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 1

# # 指定要使用的登录方法(用户名、电子邮件地址或两者之一)"username" | "email" | "username_email"
ACCOUNT_AUTHENTICATION_METHOD="username_email"
# # 登录尝试失败的次数
# ACCOUNT_LOGIN_ATTEMPTS_LIMIT(=5)
# # 从上次失败的登录尝试,用户被禁止尝试登录的持续时间
# ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT(=300)
# # 更改为True,用户一旦确认他们的电子邮件地址,就会自动登录
ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True

# # 更改或设置密码后是否自动退出
# ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE(=False)
# 更改为True,用户将在重置密码后自动登录

修改settings.py中的teplates:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                # allauth 需要 django 提供这个处理器
                'django.template.context_processors.request',
            ],
        },
    },
]

创建user(app)并进行重构。

创建app,user

python manage.py startapp user

将user文件夹移动到apps中
添加forms.py文件:

from django import forms
from .models import Ouser

class UserForm(forms.Form):
    username = forms.CharField(max_length=30)
    password = forms.CharField(max_length=50)
    password2 = forms.CharField(max_length=50)
    email = forms.CharField(max_length=50)

class loginForm(forms.Form):
    username = forms.CharField(max_length=30)
    password = forms.CharField(max_length=50)

class ProfileForm(forms.ModelForm):
    class Meta:
        model = Ouser
        fields = ['link', 'avatar']

重新继承原始登陆的user类但是进行重构:
models.py:

from django.db import models
from django.contrib.auth.models import AbstractUser
from imagekit.models import ProcessedImageField
from imagekit.processors import ResizeToFill
from django.conf import settings
from django.shortcuts import reverse
from uuslug import slugify

class Ouser(AbstractUser):
    # 扩展用户个人网站字段
    link = models.URLField('个人网址', blank=True, help_text='提示:网址必须填写以http开头的完整形式')
    # contact = models.ManyToManyField(Contacts, verbose_name='通讯录',default='1')
    # 扩展用户头像字段
    avatar = ProcessedImageField(
        upload_to='avatar/%Y%m%d',
        default='avatar/default.png',
        verbose_name='头像',
        processors=[ResizeToFill(80, 80)],
        blank=True
    )

    class Meta:
        verbose_name = '用户'  # 定义网站管理后台表名
        verbose_name_plural = verbose_name
        ordering = ['id']

    def __str__(self):
        return self.username

    def save(self, *args, **kwargs):
        super(Ouser, self).save(*args, **kwargs)

    def db_delete_user(self):
        deleteResult = Ouser.objects.filter(username=self.username).delete()
        if deleteResult:
            return 1

在admin中注册user,admin.py:

from django.contrib import admin
from .models import Ouser

@admin.register(Ouser)
class OuserAdmin(admin.ModelAdmin):
    list_display = ('username', 'email', 'is_staff', 'is_active', 'date_joined')
    fieldsets = (
        ('基础信息', {'fields': (('username', 'email'), ('link','contact', 'password'))}),
        ('权限信息', {'fields': (('is_active', 'is_staff', 'is_superuser'),
                             'groups', 'user_permissions')}),
        ('重要日期', {'fields': (('last_login', 'date_joined'),)}),
    )
    filter_horizontal = ('groups', 'user_permissions',)
    list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
    search_fields = ('username', 'email')

url.py定向于个人信息和修改个人信息界面:

# -*- coding: utf-8 -*-
from django.conf.urls import url
from .views import profile_view, change_profile_view

app_name='accounts'
urlpatterns = [
url(r'^profile/$',profile_view,name='profile'),
url(r'^profile/change/$',change_profile_view,name='change_profile'),
]

迁移数据库:

(env) PS F:\demo\django_blog> python .\manage.py makemigrations
No changes detected

访问127.0.0.1:8000,没问题。

接下来实现在web上的登陆:
首先获取accounts的登陆url,在主urls.py,django/urls.py中添加两条数据:

urlpatterns = [
    path('admin/', admin.site.urls),
    # index
    path('', include('apps.index.urls'), name='index'),
    #用户
    path('accounts/', include('allauth.urls'), name='accounts'),
    path('accounts/', include('apps.user.urls'), name='accounts'),
]

运行项目。打开本地8000端口。我们试着访问登陆的url http://127.0.0.1:8000/accounts/login/?next=/
成功访问,但是还没有界面。

在web上修改前端界面,实现登陆

先对我们原来的最底层的base.html进行代码修改:
头部注册user的标签函数

<!DOCTYPE html>
{% load static user_tags%}
<html lang="en-US">


修改成:

{% if user.is_authenticated %}
            <ul class="navbar-nav">
                <div class="dropdown for-notification">
                    <button class="btn btn-secondary dropdown-toggle" type="button" id="notification" data-toggle="dropdown"
                        aria-haspopup="true" aria-expanded="false">
                        <i class="fa fa-bell"></i>
                        <span class="count bg-danger">test</span>
                    </button>
                    <div class="dropdown-menu dropdown-menu-right mt-0 rounded-0 border-0" aria-labelledby="notification">
                        <a class="dropdown-item media" href="">
                            <i class="fa fa-bullhorn mx-1"></i>
                            <p>未读消息|管理消息</p>
                        </a>
                        <a class="dropdown-item media" href="">
                            <i class="fa fa-warning"></i>
                            <p>{{ each.create_p }}回复你:{{ each.comment.content|truncatechars:15|safe }}
                            </p>
                        </a>
                    </div>
                </div>
                <div class="dropdown for-message">
                    <button class="btn btn-secondary dropdown-toggle" type="button" id="message" data-toggle="dropdown"
                        aria-haspopup="true" aria-expanded="false">
                        <i class="fa fa-envelope"></i>
                        <span class="count bg-danger">3</span>
                    </button>
                    <div class="dropdown-menu dropdown-menu-right mt-0 rounded-0 border-0" aria-labelledby="message">
                        <p class="red">You have 3 Mails</p><a class="dropdown-item media" href="#">
                            <span class="photo media-left"><img alt="avatar" src="#" alt="。"></span>
                            <div class="message media-body">
                                <span class="name float-left">Jonathan Smith</span>
                                <span class="time float-right">Just now</span>
                                <p>Hello, this is an example msg</p>
                            </div>
                        </a>
                        <a class="dropdown-item media" href="#">
                            <span class="photo media-left"><img alt="avatar" src="#"></span>
                            <div class="message media-body">
                                <span class="name float-left">Jack Sanders</span>
                                <span class="time float-right">5 minutes ago</span>
                                <p>Lorem ipsum dolor sit amet, consectetur</p>
                            </div>
                        </a>
                        <a class="dropdown-item media" href="#">
                            <span class="photo media-left"><img alt="avatar" src="#" alt="。"></span>
                            <div class="message media-body">
                                <span class="name float-left">Cheryl Wheeler</span>
                                <span class="time float-right">10 minutes ago</span>
                                <p>Hello, this is an example msg</p>
                            </div>
                        </a>

                    </div>
                </div>
                <li class="nav-item dropdown">
                    <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
                        aria-haspopup="true" aria-expanded="false" title="{{ user },欢迎回来!">
                        <img class="avatar" src="{{ user.avatar.url }}" alt="Freen247">
                    </a>
                    <div class="dropdown-menu dropdown-menu-right mt-0 rounded-0 border-0" aria-labelledby="navbarDropdown">
                        <a class="dropdown-item pl-3" href="/accounts/profile/"><i
                                class="fa fa-fw fa-user text-info mr-2"></i>个人资料</a>
                        <a class="dropdown-item pl-3" href="/accounts/logout/"><i
                                class="fa fa-fw fa-sign-out text-info mr-2"></i>退出</a>
                    </div>
                </li>
            </ul>
            {% else %}
            <ul class="navbar-nav">
                <li class="nav-item mr-2">
                    <a class="nav-link py-md-3" href="/accounts/login/?next=/">登录</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link py-md-3" href="/accounts/signup/?next=/">注册</a>
                </li>
            </ul>
            {% endif %}

我们打开env\Lib\site-packages\allauth\templates\account中的所有这些文件移动到user/templates中:

对其中base.html进行重构:
注意下面中所继承的base.html就是继承的index中的base.html。请不要弄混了。

{% extends "base.html" %}
{% load static user_tags %}

{% block metas %}
    <meta name="description" content="用户账号管理,使用django-allauth社交用户系统,支持微博、Github等社交账号登录,加入{{ site_end_title }},查看更多信息。">
    <meta name="keywords" content="django-allauth,社交用户系统,OAuth 2.0">
{% endblock %}

{% block top_file %}
<link href="{% static 'account/account.css' %}?v=20171229.019906" rel="stylesheet">
{% endblock %}

<div class="freen-author-box freen-widget">
{% block mdeditor_contain %}
<div class="container">
    <div class="row">
        <div class="col-12 col-sm-8 col-md-6 offset-sm-2 offset-md-3 px-xl-5">
            <div class="card rounded-0 px-3 px-lg-4">
                <div class="card-header text-center bg-white py-2">
                    <h3 class="my-1 text-info">{% block user_title %}账号管理{% endblock %}</h3>
                </div>
                <div class="card-body card-login">{% block content %}{% endblock %}</div>
                <div class="text-center mb-5" id="social-login">
                    <div class="login-title">
                        <span>快速登录</span>
                    </div>
                    <div class="login-link">
                        {% get_request_param request 'next' as next_url %}

                        <a class="mx-4" href="/accounts/github/login/?next={{ next_url }}" title="社交账号登录有点慢,请耐心等候!"><i class="fa fa-github fa-2x"></i></a>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}
</div>
{% block end_file %}
{% block extra_body %}
{% endblock %}
{% endblock %}

前端加载用户头像

修改settings中的配置文件,添加:

# 媒体文件地址
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

MEDIA_ROOT和MEDIA_URL是用户上传文件保存、访问的位置:

  • upload_to。有了这个参数,文件上传后将自动保存到项目根目录的media文件夹中。 os.path.join(MEDIA_ROOT, 'media/')指定了media文件夹的位置。

  • MEDIA_URL代表用户通过URL来访问这个本地地址的URL。设置好这个参数后,用户就可以通过解析url,很方便的获取文件的地址。这样做的好处是避免的硬编码,让代码更容易维护。

Django框架擅长的是对逻辑的处理,而对图片这类文件的处理则非常的耗时。因此在实际的生产环境中(即产品上线之后),通常是有专门的Web服务器来处理文件的访问。

而在开发阶段我们不会在意效率问题,所以Django也提供了方法,让开发服务器能够处理图片。

与此我们已经将用户的头像添加到了指定的文件中,但是还没法访问。
在django_blog/urls.py中添加这行:

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

这样子前端通过调用用户的avatar属性就可以调用到用户的头像url。

创建文件夹templatetags,编写获取user信息的tags文件user_tags.py,

在上面的base文件中已经进行载入:

# 创建了新的tags标签文件后必须重启服务器

from django import template
from ..models import Ouser
from django.db.models.aggregates import Count
register = template.Library()

@register.simple_tag
def get_user_avatar_tag(user):
    '''返回用户的头像,是一个img标签'''
    return {'user':user}

@register.simple_tag
def get_users_list():
    '''返回全部用户字典'''
    return Ouser.objects.all()

@register.simple_tag
def get_users_num():
    '''返回全部用户的数量'''
    return Ouser.objects.count()

@register.simple_tag
def get_users_bynameid(the_name,the_id):
    '''按照昵称和id获取用户'''
    return Ouser.objects.filter(username=the_name,id=the_id)

@register.simple_tag
def get_request_param(request, param, default=None):
    """获取请求的参数"""
    return request.POST.get(param) or request.GET.get(param, default)

我们访问网站,http://127.0.0.1:8000/accounts/login/?next=/
顺利的登陆,

因为刚才在settings中添加的配置文件中,表示登陆的用户需要邮箱验证,所有会发送一封邮件。
邮件的内容可以在password_reset_key_message.txt中进行修改:

到这里我们就实现了对django中user的重构。

加载github第三方登陆。

另外需要注意的是django-allauth所注册的账号与django内置的本地账号是通用的,也就是说通过内置User创建的账号,是可以通过django-allauth登录的。

有了django-allauth,
在settings中我们需要在入app,注意哦,在之前的操作中我们已经载入了。

 # github 登陆 
    'allauth.socialaccount.providers.github',

我们需要在github中申请key

注册登陆的api,https://github.com/settings/applications/n...

callbackd地址:http://127.0.0.1:8000/accounts/github/logi...

看看github给的api文档:

The flow to authorize users for your app is:

  1. Users are redirected to request their GitHub identity
  2. Users are redirected back to your site by GitHub
  3. Your app accesses the API with the user's access token

点击确认后,获取KEY

Client ID
ce721====78bf
Client Secret
971687ce3c2381=====15e9d514935d0b6862

进入后台界面,添加site:

添加 social application

尝试第三方登陆
成功:

其实第三方登陆和api一样不是很难,只需要key和url定向就可以实现,微博、微信的登录方式大致都遵循这个流程;本章虽然加载了微博的接口,但是限于篇幅并没有配置,请读者查阅官方文档去实现。

用户登陆退出消息提醒

 <!--消息块-->
                {% if messages %}
                <div class="container">
                    {% for message in messages %}
                    <div class="alert {% if message.tags %}alert-{{ message.tags }}{% else %}alert-secondary{% endif %} alert-dismissible rounded-0 fade show"
                        role="alert">
                        {{ message }}
                        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    {% endfor %}
                </div>
                {% endif %}

原址:https://boywithacoin.cn/
github:https://github.com/Freen247/django_blog

本作品采用《CC 协议》,转载必须注明作者和本文链接
文章!!首发于我的博客Stray_Camel(^U^)ノ~YO
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!