Django学习笔记-Web端授权AcWing一键登录

这篇具有很好参考价值的文章主要介绍了Django学习笔记-Web端授权AcWing一键登录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

笔记内容转载自 AcWing 的 Django 框架课讲义,课程链接:AcWing Django 框架课。

1. 在Django中集成Redis

Redis 为内存数据库,目前我们使用的是 Django 自带的数据库 SQLite,且能够很容易地迁移到 MySQL,这些数据库的效率不如 Redis,其特点为:

  • Redis 存的内容为 key, value 对,而其它数据库存的是若干张表,每张表为若干条目;
  • Redis 为单线程的,不会出现读写冲突。

首先我们需要先安装 Redis:

sudo apt-get update
sudo apt-get install redis-server
sudo service redis-server status  # 查看redis-server状态
pip install django_redis

接着配置一下 Django 的缓存机制,将下面这段代码复制到 settings.py 中:

CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        },
    },
}
USER_AGENTS_CACHE = 'default'

然后启动 redis-server,启动后可以使用 top 命令查看 redis-server 是否运行:

sudo redis-server /etc/redis/redis.conf

此时在项目根目录执行 python3 manage.py shell 进入 IPython 交互界面,输入以下代码测试一下 Redis:

In [1]: from django.core.cache import cache

In [2]: cache.keys('*')  # 查找当前数据库的所有关键字,支持正则匹配
Out[2]: []

In [3]: cache.set('yyj', 1, 5)  # 设置关键字,第三个参数表示多长时间过期,单位是秒,None表示不会过期
Out[3]: True

In [4]: cache.set('abc', 2, None)
Out[4]: True

In [5]: cache.has_key('abc')  # 查询某个键是否存在
Out[5]: True

In [6]: cache.has_key('yyj')
Out[6]: False

In [7]: cache.get('abc')  # 查询某个键的值
Out[7]: 2

In [8]: cache.delete('abc')  # 删除某个关键字
Out[8]: True

注意如果出现报错:ConnectionError: Error 111 connecting to 127.0.0.1:6379. Connection refused. 说明 redis-server 没有启动。

2. 申请授权码

一键授权登录的流程是用户点击一键登录后弹出确认授权的页面,用户确认后就自动创建一个新的账号,且登录到网站里。

具体交互流程如下图所示,用户点击按钮后向网站服务器端(Web)发起申请,请求用 AcWing 账号登录,然后 Web 将自己的 AppID 报给 AcWing,AcWing 给用户返回一个页面询问用户是否要授权给刚刚的网站,如果用户同意,那么 AcWing 会将一个授权码 code(两小时有效期)发给 Web,Web 接到授权码后再加上自己的身份信息 AppSecret 以及 AppID 向 AcWing 申请一个授权令牌 access-token(两小时有效期)和用户的 openid(唯一辨别用户),Web 拿到令牌和用户 ID 后即可向 AcWing 申请用户的用户名和头像。

Django学习笔记-Web端授权AcWing一键登录

由于我们需要记录每个用户的 openid,因此我们需要在数据库(game/models/player/)的 Player 类中添加一个信息:

from django.db import models
from django.contrib.auth.models import User

# Player有两个关键字,user表示是和哪个User对应的,avatar表示头像
class Player(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)  # 当删除User时也将其关联的Player一块删掉
    avatar = models.URLField(max_length=256, blank=True)  # 头像用链接存
    openid = models.CharField(default='', max_length=50, blank=True, null=True)

    def __str__(self):  # 显示每个Player的数据
        return str(self.user)

在根目录下更新一下数据库:

python3 manage.py makemigrations
python3 manage.py migrate

申请授权码的请求地址:https://www.acwing.com/third_party/api/oauth2/web/authorize/

请求方法为 GET,参考示例:

https://www.acwing.com/third_party/api/oauth2/web/authorize/?appid=APPID&redirect_uri=REDIRECT_URI&scope=SCOPE&state=STATE

参数说明:

  • appid:应用的唯一 ID,可以在 AcWing 编辑 AcApp 的界面里看到;
  • redirect_uri:接收授权码的地址,表示 AcWing 端要将授权码返回到哪个链接,需要对链接进行编码:Python3 中使用 urllib.parse.quote;Java 中使用 URLEncoder.encode
  • scope:申请授权的范围,目前只需填 userinfo
  • state:用于判断请求和回调的一致性,授权成功后原样返回该参数值,即接收授权码的地址需要判断是否是 AcWing 发来的请求(判断收到的 state 与发送出去的 state 是否相同),如果不是直接 Pass。该参数可用于防止 CSRF 攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数(如果是将第三方授权登录绑定到现有账号上,那么推荐用 随机数 + user_id 作为 state 的值,可以有效防止 CSRF 攻击)。此处 state 可以存到 Redis 中,设置两小时有效期。

用户同意授权后会重定向到 redirect_uri,返回参数为 codestate。链接格式如下:

redirect_uri?code=CODE&state=STATE

如果用户拒绝授权,则不会发生重定向。

我们在 game/views/settings 目录中创建一个 acwing 目录表示 AcWing 授权登录,然后在该目录中先创建一个 __init__.py,再创建两个子目录 webacapp 分别表示 Web 端的 AcWing 一键登录以及 AcApp 端的 AcWing 一键登录,最后同样在这两个子目录中创建 __init__.py 文件。

web 目录下创建申请授权码的 API apply_code.py

from django.http import JsonResponse

def apply_code(request):
    appid = '4007'
    return JsonResponse({
        'result': 'success',
    })

接着创建接收授权码的 API receive_code.py

from django.shortcuts import redirect

def receive_code(request):
    return redirect('index')  # 重定向回index

然后在 game/urls/settings 目录中也创建一个 acwing 目录,在该目录中创建 __init__.pyindex.pyindex.py 内容如下:

from django.urls import path
from game.views.settings.acwing.web.apply_code import apply_code
from game.views.settings.acwing.web.receive_code import receive_code

urlpatterns = [
    path('web/apply_code/', apply_code, name='settings_acwing_web_apply_code'),
    path('web/receive_code/', receive_code, name='settings_acwing_web_receive_code'),
]

然后需要将该目录 includesettings 目录的 index.py 中:

from django.urls import path, include
from game.views.settings.getinfo import getinfo
from game.views.settings.login import mylogin
from game.views.settings.logout import mylogout
from game.views.settings.register import register

urlpatterns = [
    path('getinfo/', getinfo, name='settings_getinfo'),
    path('login/', mylogin, name='settings_login'),
    path('logout/', mylogout, name='settings_logout'),
    path('register/', register, name='settings_register'),
    path('acwing/', include('game.urls.settings.acwing.index')),
]

重启项目后即可访问 https://<公网IP>/settings/acwing/web/apply_code/ 以及 https://<公网IP>/settings/acwing/web/receive_code/ 测试效果。

现在来看看 urllib.parse.quote 的作用,它能够将链接重新编码,替换掉原本的部分特殊字符防止出现 BUG:

In [1]: from urllib.parse import quote

In [2]: url = 'https://app4007.acapp.acwing.com.cn/settings/acwing/web/receive_code/?args=yyj'

In [3]: quote(url)
Out[3]: 'https%3A//app4007.acapp.acwing.com.cn/settings/acwing/web/receive_code/%3Fargs%3Dyyj'

现在我们完善一下 apply_code.py 的内容:

from django.http import JsonResponse
from django.core.cache import cache
from urllib.parse import quote
from random import randint

def get_state():  # 获得8位长度的随机数
    res = ''
    for i in range(8):
        res += str(randint(0, 9))
    return res

def apply_code(request):
    appid = '4007'
    redirect_uri = quote('https://app4007.acapp.acwing.com.cn/settings/acwing/web/receive_code/')
    scope = 'userinfo'
    state = get_state()
    cache.set(state, True, 7200)  # 有效期2小时
    apply_code_url = 'https://www.acwing.com/third_party/api/oauth2/web/authorize/'
    return JsonResponse({
        'result': 'success',
        'apply_code_url': apply_code_url + '?appid=%s&redirect_uri=%s&scope=%s&state=%s' % (appid, redirect_uri, scope, state)
    })

然后修改一下前端代码(Settings 类):

class Settings {
    constructor(root) {
        ...

        this.$acwingoption = this.$login.find('.ac_game_settings_acwingoption img');

        ...
    }

    start() {  // 在初始化时需要从服务器端获取用户信息
        ...
    }

    add_listening_events() {  // 绑定监听函数
        this.add_listening_events_login();
        this.add_listening_events_register();
    }

    add_listening_events_login() {
        let outer = this;
        this.$login_register.click(function() {
            outer.register();
        });
        this.$login_submit.click(function() {
            outer.login_on_remote();
        });
        this.$acwingoption.click(function() {
            outer.acwing_login();
        });
    }

    add_listening_events_register() {
        ...
    }

    login_on_remote() {  // 在远程服务器上登录
        ...
    }

    register_on_remote() {  // 在远程服务器上注册
        ...
    }

    logout_on_remote() {  // 在远程服务器上登出
        ...
    }

    acwing_login() {
        $.ajax({
            url: 'https://app4007.acapp.acwing.com.cn/settings/acwing/web/apply_code/',
            type: 'GET',
            success: function(resp) {
                console.log(resp);
                if (resp.result === 'success') {
                    window.location.replace(resp.apply_code_url);  // 将当前页面重定向
                }
            }
        })
    }

    register() {  // 打开注册界面
        ...
    }

    login() {  // 打开登录界面
        ...
    }

    getinfo() {
        ...
    }

    hide() {
        ...
    }

    show() {
        ...
    }
}

3. 申请授权令牌和用户ID

请求地址:https://www.acwing.com/third_party/api/oauth2/access_token/

请求方法为 GET,参考示例:

https://www.acwing.com/third_party/api/oauth2/access_token/?appid=APPID&secret=APPSECRET&code=CODE

参数说明:

  • appid:应用的唯一 ID,可以在 AcWing 编辑 AcApp 的界面里看到;
  • secret:应用的秘钥,可以在 AcWing 编辑 AcApp 的界面里看到;
  • code:上一步中获取的授权码。

申请成功的返回内容示例:

{ 
    "access_token": "ACCESS_TOKEN", 
    "expires_in": 7200, 
    "refresh_token": "REFRESH_TOKEN",
    "openid": "OPENID", 
    "scope": "SCOPE",
}

申请失败的返回内容示例:

{
    "errcode": 40001,
    "errmsg": "code expired",  # 授权码过期
}

返回参数说明:

  • access_token:授权令牌,有效期2小时;
  • expires_in:授权令牌还有多久过期,单位(秒);
  • refresh_token:用于刷新 access_token 的令牌,有效期30天;
  • openid:用户的 ID。每个 AcWing 用户在每个 AcApp 中授权的 openid 是唯一的,可用于识别用户;
  • scope:用户授权的范围。目前范围为 userinfo,包括用户名、头像。

现在我们点击 AcWing 一键登录按钮即可跳出请求授权的页面,AcWing 会向 receive_code 函数发送 code 以及 state,现在我们完善 receive_code.py 接到授权后的操作:

from django.shortcuts import redirect
from django.core.cache import cache
import requests

def receive_code(request):
    data = request.GET
    code = data.get('code')
    state = data.get('state')

    if not cache.has_key(state):
        return redirect('index')
    cache.delete(state)

    apply_access_token_url = 'https://www.acwing.com/third_party/api/oauth2/access_token/'
    params = {
        'appid': '4007',
        'secret': '0edf233ee876407ea3542220e2a8d83e',
        'code': code
    }

    access_token_res = requests.get(apply_access_token_url, params=params).json()  # 申请授权令牌
    print(access_token_res)

    return redirect('index')  # 重定向回index

现在我们点击一键登录按钮即可在后台看到接收到的授权令牌内容。

4. 申请用户信息

请求地址:https://www.acwing.com/third_party/api/meta/identity/getinfo/

请求方法为 GET,参考示例:

https://www.acwing.com/third_party/api/meta/identity/getinfo/?access_token=ACCESS_TOKEN&openid=OPENID

参数说明:

  • access_token:上一步中获取的授权令牌;
  • openid:上一步中获取的用户 openid

申请成功的返回内容示例:

{
    'username': "USERNAME",
    'photo': "https:cdn.acwing.com/xxxxx"
}

申请失败的返回内容示例:

{
    'errcode': "40004",
    'errmsg': "access_token expired"  # 授权令牌过期
}

现在我们用授权令牌向 AcWing 申请用户的用户名和头像,进一步完善 receive_code.py

from django.shortcuts import redirect
from django.core.cache import cache
from django.contrib.auth.models import User
from django.contrib.auth import login
from game.models.player.player import Player
from random import randint
import requests

def receive_code(request):
    data = request.GET
    code = data.get('code')
    state = data.get('state')

    if not cache.has_key(state):
        return redirect('index')
    cache.delete(state)

    apply_access_token_url = 'https://www.acwing.com/third_party/api/oauth2/access_token/'
    params = {
        'appid': '4007',
        'secret': '0edf233ee876407ea3542220e2a8d83e',
        'code': code
    }

    access_token_res = requests.get(apply_access_token_url, params=params).json()  # 申请授权令牌
    access_token = access_token_res['access_token']
    openid = access_token_res['openid']

    player = Player.objects.filter(openid=openid)  # 查看当前用户的openid是否已经存在
    if player.exists():
        login(request, player[0].user)  # 如果用户的账号已经存在直接登录即可
        return redirect('index')

    get_userinfo_url = 'https://www.acwing.com/third_party/api/meta/identity/getinfo/'
    params = {
        'access_token': access_token,
        'openid': openid
    }

    get_userinfo_res = requests.get(get_userinfo_url, params=params).json()  # 申请获取用户信息
    username = get_userinfo_res['username']
    avatar = get_userinfo_res['photo']

    while User.objects.filter(username=username).exists():  # 如果当前用户的用户名已经存在则在其后面添加若干位随机数
        username += str(randint(0, 9))

    user = User.objects.create(username=username)  # 创建该用户,没有密码
    player = Player.objects.create(user=user, avatar=avatar, openid=openid)
    login(request, user)  # 创建好后直接登录

    return redirect('index')  # 重定向回index

至此 Web 端的 AcWing 一键登录已经实现。文章来源地址https://www.toymoban.com/news/detail-507484.html

到了这里,关于Django学习笔记-Web端授权AcWing一键登录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Django的Rest framework搭建自定义授权登录

    提示:阅读本章之前,请先阅读目录 之前的文章有写过通过jwt认证的文章,今天这一篇是通过自定义用户认证的; 使用场景:有些API需要用户登录成功之后,才能访问;有些无需登录就能访问 解决方法:创建两张表,一张用户表,一张token表,保存用户登录成功后生产的t

    2024年02月15日
    浏览(27)
  • mpVue 微信小程序授权登录流程(即登录鉴权流程)及获取手机号一键登录教程(getPhoneNumber使用)

    微信小程序登录 鉴权流程 如下: 因 wx.getUserProfile 与 wx.getUserInfo 接口被收回了,都不能弹出授权窗口,只能使用头像昵称填写能力去获取微信用户信息。 在鉴权页面如下操作 : 1、在 onShow 中调用微信登录 wx.login 获取到唯一的code(用来获取 openid ); 2、根据 wx.login 获取的c

    2024年02月12日
    浏览(49)
  • uniapp小程序 手机号授权一键登录 小程序接口调用getPhoneNumber java+uniapp

    uniapp 前端 uniapp登录按钮: (在button加上这俩个参数) open-type=\\\"getPhoneNumber\\\" @getphonenumber=\\\"getPhoneNumber\\\" methods方法区 getPhoneNumbe(e) 这个 e 就是登录用户 小程序的code值 code值后续用于请求 小程序官方接口 来获取用户手机号 后端调用逻辑  大致逻辑分为以下几个步骤            

    2024年02月02日
    浏览(44)
  • H5 及 web 页面微信授权登录流程

    注意:我们要求调试微信网页授权,必须开发者微信号与公众号建立绑定关系 需求背景:将H5页面分享到微信,页面中的功能需要登录,所以再进行功能操作前需要调起登录 1、微信登录的几种情况: PC端: PC端微信浏览器 - 网页内嵌二维码方式(需要扫码,使用微信服务号

    2024年02月12日
    浏览(26)
  • 利用Django搭建python web项目(简单登录)

    目前市面上web项目大多数是由java语言开发(结合spring框架),但这并不意味着只有java语言能够开发web项目,python语言、go语言同样可以做到。本文将利用Django框架(由python语言开发的web框架)来搭建一个简单的web项目,并实现简单登录功能。 2.1.1 Django框架优点 Django遵循MVC(

    2024年01月23日
    浏览(29)
  • 蓝桥杯AcWing学习笔记 8-1数论的学习(上)

    我的AcWing 题目及图片来自蓝桥杯C++ AB组辅导课 蓝桥杯省赛中考的数论不是很多,这里讲几个蓝桥杯常考的知识点。 欧几里得算法代码: 就是因式分解的定理,所有的整数都可以唯一分解成若干个质因子乘积的形式: N = P 1 α 1 × P 2 α 2 × . . . × P k α k N=P_{1}^{α_{1}}×P_{2}^{α

    2024年01月16日
    浏览(71)
  • uniapp(vue3) - 详解微信小程序平台用户授权登录全流程,uniapp v3版本中小程序端开发下用户点击登录后获取手机号/昵称/性别/头像等信息完成登录(提供完整示例代码,一键复制开箱即用)

    在uniapp(v3)微信小程序端开发中,超详细实现用户授权登录完整功能源码,用户授权后获取手机号/昵称/头像/性别等,提供完整思路流程及逻辑讲解。 你也可以直接复制粘贴,然后改下参数放到你的项目中去就行。 做功能之前,先

    2024年02月05日
    浏览(45)
  • Web实战丨基于Django与HTML的用户登录验证系统

    本期内容:基于Django与HTML的简单登录验证系统。 项目需求:Python+Django 项目下载地址:https://download.csdn.net/download/m0_68111267/88727183 登录系统在我们的日常生活中随处可见,比如当我们使用QQ、微信等社交软件时,都需要先输入自己的账号密码进行登录,本文博主将带着大家用

    2024年01月25日
    浏览(37)
  • Django笔记三十二之session登录验证操作

    本文首发于公众号:Hunter后端 原文链接:Django笔记三十二之session登录验证操作 这一篇笔记将介绍 session 相关的内容,包括如何在系统中使用 session,以及利用 session 实现登录认证的功能。 这篇笔记将分为以下几个内容: session 的使用流程 session 的配置和相关方法 users 模块的

    2024年02月01日
    浏览(52)
  • 【Django学习】(十五)API接口文档平台_项目流程分析_日志器_认证_授权

    使用API接口文档不经可以很好的的维护接口数据,还给测试人员的接口测试工作带来了便利; 我们可以在全局配置文件中添加路由路径生成接口文档 1.1在全局配置文件里指定用于支持coreapi的Schema 1.2在全局路由表中添加路径   页面效果: 2.1 一定要先在配置表中注册drf_yas

    2024年02月15日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包