博客网站上线一段时间了,发现注册用户很少,大家对于注册账号这种事都不太感兴趣,因此决定开发第三方账号登录功能,参考了相关文档实现了该功能,对整个过程以及其中遇到的问题还有解决办法做了详细的总结,避免大家踩坑。

一、什么是OAuth

1. 概念

OAuth的英文全称是Open Authorization,它是一种开放授权协议。OAuth目前共有2个版本,2007年12月的1.0版(之后有一个修正版1.0a)和2010年4月的2.0版,1.0版本存在严重安全漏洞,而2.0版解决了该问题。 OAuth简单说就是一种授权的协议,只要授权方和被授权方遵守这个协议去写代码提供服务,那双方就是实现了OAuth模式。

2. 应用场景

用户想要在某个网站应用登录使用时,又不想通过繁琐的步骤注册账号,此时就可以使用OAuth协议,调用第三方平台(如qq 微博等),完成第三方平台登录后,实现自动创建用户并登录的过程。

3. 过程解析

以博客网站使用QQ实现认证登录过程为例,详细分析具体流程

image.png

  • 第一步:点击qq登录图标

image.png 此时f12打开调试工具,发现network发送的具体请求地址是 https://www.cuiliangblog.cn/login/login/qq 

image.png Django服务器会响应一个重定向地址,指向qq授权登录页。https://graph.qq.com/oauth2.0/authorize?client_id=101921043&client_id=https://www.cuiliangblog.cn/login/complete/qq&state=uu25k4sabcActZYDUB2B1He8BrYS0MYu&response_type=code image.png 该请求中携带了client_id、state等信息,QQ服务器收到请求后判断来源请求认证网站信息的正确性,确认无误后跳转至登录授权页面 image.png

  • 第二步:qq登录页面输入用户名密码,然后点授权并登录

image.png 用户输入账号密码点击授权并登录按钮后,访问qq服务器中校验用户名密码的方法,进行用户名密码登录校验

image.png 若校验成功,该方法会响应浏览器一个重定向地址,并附上一个code(授权码)和state。

image.png

  • 第三步:跳回到博客页面,成功登录

Django服务器拿到的token后,获取根据openid获取用户信息。最后将用户信息储存起来,返回给浏览器其首页的视图。到此OAuth2.0授权结束。

image.png

二、使用OAuth

1. django项目使用OAuth

了解上述过程后,如何在自己的项目中使用OAuth认证呢?

  • 方式一:参考文档,发送request请求api接口,完成认证授权。参考文档:https://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0
  • 方式二:使用Django库,在此推荐两个库①django-allauth(大而全,从用户注册到发送邮件重置密码全过程、以及第三方登录都功能都有)②social_django(Django项目第三方登录模块)

由于我的项目已经开发了用户认证全部流程,所有在这采用social_django库实现用户第三方登录。参考文档:https://python-social-auth.readthedocs.io/en/latest/index.html

2.准备工作

使用QQ、微博的第三方登录时,需要开发者实名认证,QQ调用网站还需要进行备案。微信不支持个人身份开发者注册,需要填写企业信息。

  • QQ申请地址:https://connect.qq.com/,如果遇到无法填写信息的话,可以登陆开放平台http://wiki.open.qq.com/ 后,点管理中心-账号资料再点编辑就可以上传修改信息,提交审核即可。(QQ互联资质和腾讯开放平台资质是共用的)

image.png

image.png

image.png

  • 安装social_django库

pip install social-auth-app-django 

  • 修改setting.py配置
INSTALLED_APPS = [
    ……
    'social_django',  # 第三方登录
    ……
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                ………
                # 第三方账号登录
                'social_django.context_processors.backends',
                'social_django.context_processors.login_redirect',
                ……
            ],
        },
    },
]

# 支持邮箱登录
AUTHENTICATION_BACKENDS = (
                           'django.contrib.auth.backends.ModelBackend',  # 指定Django的modelbackend类
                           )
# 第三方登录
SOCIAL_AUTH_URL_NAMESPACE = 'social'

# 登陆成功后的回调路由
SOCIAL_AUTH_LOGIN_REDIRECT_URL = 'https://www.cuiliangblog.cn/account/'
# 使用https代理
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
  • 配置url
urlpatterns = [
    ……
    path('login/', include('social_django.urls', namespace='social')),
    # 第三方账户登录
    ……
]
  • 创建数据库相关表

python manage.py migrate 

生成如下五张表

image.png

3. github

  • setting文件添加配置
# 登录认证后端
AUTHENTICATION_BACKENDS = (……
                           'social_core.backends.github.GithubOAuth2',  # github登录
                            ……
                           )

# 第三方Github KEY和SECRET
SOCIAL_AUTH_GITHUB_KEY = '***********'
SOCIAL_AUTH_GITHUB_SECRET = '***************'
# 使用昵称作为用户名
SOCIAL_AUTH_GITHUB_USE_OPENID_AS_USERNAME = True
  • 前端页面添加github登录跳转按钮
<button type="button" onclick="window.location.href = '{% url 'social:begin' 'github' %}'" class="layui-btn layui-btn-normal"><i class="fab fa-github fa-lg"></i></button>
  • github应用settings配置,Homepage URL和Authorization callback URL都填写域名即可

image.png

  • 登录测试(如果出现redirect_uri错误提示,参考后面的注意事项)

4. 微博

  • setting文件添加配置
# 登录认证后端
AUTHENTICATION_BACKENDS = (……
                           'social_core.backends.weibo.WeiboOAuth2',  # 新浪微博登录
                            ……
                           )

# 第三方新浪微博KEY和SECRET
SOCIAL_AUTH_WEIBO_KEY = '**********'
SOCIAL_AUTH_WEIBO_SECRET = '***************'
  • 前端页面添加github登录跳转按钮
<button type="button" onclick="window.location.href = '{% url 'social:begin' 'weibo' %}'" class="layui-btn layui-btn-normal"><i class="layui-icon layui-icon-login-weibo"></i></button>
  • 微博开放平台,填写应用授权回调页

image.png

  • 登录测试(如果出现redirect_uri错误提示,参考后面的注意事项)

5. QQ

  • setting文件添加配置
# 登录认证后端
AUTHENTICATION_BACKENDS = (……
                           'social_core.backends.qq.QQOAuth2',  # qq登录
                            ……
                           )

# 第三方QQ KEY和SECRET
SOCIAL_AUTH_QQ_KEY = '***************'
SOCIAL_AUTH_QQ_SECRET = '*********'
  • 前端页面添加github登录跳转按钮
<button type="button" onclick="window.location.href = '{% url 'social:begin' 'qq' %}'" class="layui-btn layui-btn-normal"><i class="layui-icon layui-icon-login-qq"></i></button>
  • QQ开放平台,填写应用授权回调页

image.png

  • 登录测试(如果出现redirect_uri错误提示,参考后面的注意事项)

三、其他功能实现

使用默认配置不能获取用户头像,性别信息,微博和QQ的用户名是用户的UID,为了提高用户体验,需要在创建用户时,修改一部分代码逻辑,social_django库支持可扩展的管道机制,开发人员可以在身份验证、登录和注销期间自定义开发其他功能。参考文档:https://python-social-auth.readthedocs.io/en/latest/pipeline.html#authentication-pipeline 安装官方文档的模板开发功能,还是比较麻烦的,后期库版本更新了还得调试代码。所有我换了个思路,研究social_django库代码发现,用户其他信息都存放在AbstractUserSocialAuth模型的extra_data字段中

1. 调试测试

  • 模型文件路径:social_django > migrations > models.py
class AbstractUserSocialAuth(models.Model, DjangoUserMixin):
    """Abstract Social Auth association model"""
    ……
    extra_data = JSONField()
    ……

    def __str__(self):
        return str(self.user)

    class Meta:
        app_label = "social_django"
        abstract = True
    ……


class UserSocialAuth(AbstractUserSocialAuth):
    """Social Auth association model"""

    class Meta:
        """Meta data"""
        app_label = "social_django"
        unique_together = ('provider', 'uid')
        db_table = 'social_auth_usersocialauth'
  • 数据库查看extra_data字段信息

image.png

  • python shell 调试获取用户详细信息
[root@0da62d4c0833 myblog]# python3.9 manage.py shell
Python 3.9.0 (default, Dec 12 2020, 15:10:05) 
[GCC 8.3.1 20191121 (Red Hat 8.3.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from social_django.models import UserSocialAuth
>>> user1 = UserSocialAuth.objects.get(user_id=62)                
>>> user1.extra_data['username']
'ζั͡ޓއއ陌影'
>>> user1.extra_data['profile_image_url']
'http://thirdqq.qlogo.cn/g?b=oidb&k=B2EsdNd78qoVu4616YoNVg&s=40&t=1581912070'
>>> user1.extra_data['gender']
'女'
>>> 

调试没问题,下面开始写这部分业务逻辑的代码。

2. 获取用户头像、用户名、性别

网站的数据库设计时,有一个userinfo表,专门存放用户详细信息,整体逻辑流程就是获取UserSocialAuth当前登录用户的extra_data相关值,保存到userinfo相关字段即可

# userinfo模型详细信息
class UserInfo(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, unique=True)
    # 与自带用户表一对一
    phone = models.CharField(verbose_name='手机号', max_length=20, default="保密")
    sex_choice = [('1', '男'), ('2', '女')]
    sex = models.CharField(verbose_name='性别', max_length=1, choices=sex_choice, default=1)
    web = models.CharField(verbose_name='个人网站', max_length=50, blank=True, null=True)
    aboutme = models.TextField(verbose_name='个性签名', max_length=200, default="这个人很懒,什么都没留下!")
    photo = models.ImageField(upload_to='photo/', verbose_name='头像', default='photo/default.jpg')

    class Meta:
        verbose_name = '用户详细信息'
        verbose_name_plural = verbose_name

    def __str__(self):
        return "user:{}".format(self.user.username)

主要解决的问题有:

  • 用户头像和性别只有QQ、微博这两个平台可以授权获取,github获取不到
  • 用户名github默认获取的昵称作为用户名,而QQ和微博使用UID编码作为用户名

用户授权完成后跳转到个人中心页view部分代码

# 用户个人中心页面相关view处理逻辑
def personalCenter(request):
    try:
        userinfo = UserInfo.objects.get(user_id=request.user.id)
        # print("已注册用户登录")
    except Exception as e:
        print(e)
        # print("第三方用户第一次登录")
        social_user = UserSocialAuth.objects.get(user_id=request.user.id)
        if social_user.provider == 'github':
            # print("github登录")
            UserInfo.objects.create(user_id=request.user.id)
            user = User.objects.get(id=request.user.id)
        else:
            # print("qq或微博登录")
            username = social_user.extra_data['username'].replace(" ", "")
            photo_url = social_user.extra_data['profile_image_url']
            username = social_user.extra_data['username'].replace(" ", "")
            photo_url = social_user.extra_data['profile_image_url']
            sex = social_user.extra_data['gender']
            # QQ用户性别存储值为男、女,微博用户性别存储值为f、m
            if sex == '女' or sex == 'f':
                sex_id = 2
            else:
                sex_id = 1
            user_info = UserInfo()
            user_info.user_id = request.user.id
            user_info.photo = downloadPhoto(photo_url)
            user_info.sex = sex_id
            user_info.save()
            new_user = User.objects.get(id=request.user.id)
            new_user.username = username
            new_user.save()
            user = new_user
    ………………
    return render(request, 'layui-mini/account/index.html', locals())

四、注意事项

1. 授权回调页地址填写

  • github授权回调页填写域名即可,例如https://www.cuiliangblog.cn/
  • qq和微博的授权回调页打开浏览器调试工具,查看redirect_uri地址填写

image.png

image.png

2. qq回调地址问题

social_django库默认回调地址为****/login/complete/qq/,不符合qq回调页地址要求。

image.png 此时可以修改setting配置,取消默认回调页uri后面的/

# 回调页uri后面不加/
SOCIAL_AUTH_TRAILING_SLASH = False

3. from表单redirect_uri地址异常问题

正确的form表单redirect_uri地址如下图所示

image.png

  • redirect_uri地址为http://127.0.0.1:8000/***情况解决办法

主要是被代理服务器接收到的header信息不对,修改nginx配置,添加proxy_set_header 配置即可

server {
        …………
        location / {
                proxy_pass http://127.0.0.1:8000;
                proxy_set_header  Host $host;
                proxy_set_header  X-Real-IP $remote_addr;
                proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto  $scheme;
        }
                …………
    }
  • redirect_uri地址为http://,而不是https://情况解决办法

修改setting配置,强制将所有http的重定向请求转为https

# 将所有非SSL请求永久重定向到SSL
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

五、效果演示

1. 登录页

image.png

2. 用户个人中心页

image.png

3. 后台管理用户列表

image.png

0 子项 · 1 次浏览 · 2026-09-24