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

2023-10-27

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

AcApp 端使用 AcWing 一键授权登录的流程与之前网页端的流程一样,只有申请授权码这一步有一点细微的差别:

在这里插入图片描述

我们在打开 AcApp 应用之后会自动向 AcWing 请求账号登录,客户端会向后端服务器请求一些参数,然后后端服务器向 AcWing 请求授权码,然后 AcWing 在接到请求之后会询问用户是否要授权登录,如果用户同意了那么 AcWing 会给客户端发送一个授权码,客户端可以通过授权码加上自己的身份信息向 AcWing 服务器请求自己的授权令牌 access_token 和用户的 openid,最后客户端在拿到令牌和 ID 后即可向 AcWing 服务器请求用户的用户名和头像等信息。

在网页端授权登录时我们使用的方法是通过 URL 的方式重定向到某一个链接里申请授权码,而这次的 AcApp 不是通过链接,而是通过 AcWing 的一个 API 申请,请求授权码的 API:

AcWingOS.api.oauth2.authorize(appid, redirect_uri, scope, state, callback);

参数说明:

  • 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 中,设置两小时有效期;
  • callbackredirect_uri 返回后的回调函数,即接受 receive_code 函数向前端返回的信息。

用户同意授权后,会将 codestate 传递给 redirect_uri

如果用户拒绝授权,则将会收到如下错误码:

{
    errcode: "40010"
    errmsg: "user reject"
}

我们在 game/views/settings/acwing/acapp 目录中将之前网页端的 apply_code.pyreceive_code.py 复制过来,然后对 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/acapp/receive_code/')
    scope = 'userinfo'
    state = get_state()
    cache.set(state, True, 7200)  # 有效期2小时

    # 需要返回四个参数
    return JsonResponse({
        'result': 'success',
        'appid': appid,
        'redirect_uri': redirect_uri,
        'scope': scope,
        'state': state,
    })

进入 game/urls/settings/acwing 修改一下路由:

from django.urls import path
from game.views.settings.acwing.web.apply_code import apply_code as web_apply_code
from game.views.settings.acwing.web.receive_code import receive_code as web_receive_code
from game.views.settings.acwing.acapp.apply_code import apply_code as acapp_apply_code
from game.views.settings.acwing.acapp.receive_code import receive_code as acapp_receive_code

urlpatterns = [
    path('web/apply_code/', web_apply_code, name='settings_acwing_web_apply_code'),
    path('web/receive_code/', web_receive_code, name='settings_acwing_web_receive_code'),
    path('acapp/apply_code/', acapp_apply_code, name='settings_acwing_acapp_apply_code'),
    path('acapp/receive_code/', acapp_receive_code, name='settings_acwing_acapp_receive_code'),
]

现在访问 https://app4007.acapp.acwing.com.cn/settings/acwing/acapp/apply_code/ 即可看到返回内容。

然后我们修改一下 receive_code.py

from django.http import JsonResponse
from django.core.cache import cache
from django.contrib.auth.models import User
from game.models.player.player import Player
from random import randint
import requests

def receive_code(request):
    data = request.GET

    if 'errcode' in data:
        return JsonResponse({
            'result': 'apply failed',
            'errcode': data['errcode'],
            'errmsg': data['errmsg'],
        })

    code = data.get('code')
    state = data.get('state')

    if not cache.has_key(state):
        return JsonResponse({
            'result': 'state not exist',
        })
    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']

    players = Player.objects.filter(openid=openid)  # filter不管存不存在都会返回一个列表,get如果不存在会报异常
    if players.exists():  # 用户如果已存在就直接返回用户
        player = players[0]
        return JsonResponse({
            'result': 'success',
            'username': player.user.username,
            'avatar': player.avatar,
        })

    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)

    return JsonResponse({
        'result': 'success',
        'username': player.user.username,
        'avatar': player.avatar,
    })

接着我们修改前端文件,也就是 game/static/js/src/settings 目录中的 Settings 类:

class Settings {
    constructor(root) {
        this.root = root;
        this.platform = 'WEB';  // 默认为Web前端
        if (this.root.acwingos) this.platform = 'ACAPP';
        this.username = '';  // 初始用户信息为空
        this.avatar = '';

        this.$settings = $(`
            ...
        `);

        ...

        this.start();
    }

    start() {  // 在初始化时需要从服务器端获取用户信息
        if (this.platform === 'WEB') {
            this.getinfo_web();
            this.add_listening_events();
        } else {
            this.getinfo_acapp();
        }
    }

    add_listening_events() {  // 绑定监听函数
        ...
    }

    add_listening_events_login() {
        ...
    }

    add_listening_events_register() {
        ...
    }

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

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

    acwing_login() {
        ...
    }

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

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

    getinfo_web() {  // 此处将之前的getinfo函数名进行了修改用来区分
        let outer = this;
        $.ajax({
            url: 'https://app4007.acapp.acwing.com.cn/settings/getinfo/',  // 用AcWing部署
            // url: 'http://8.130.54.44:8000/settings/getinfo/',  // 用云服务器部署
            type: 'GET',
            data: {
                platform: outer.platform,
            },
            success: function(resp) {  // 调用成功的回调函数,返回的Json字典会传给resp
                console.log(resp);  // 控制台输出查看结果
                if (resp.result === 'success') {
                    outer.username = resp.username;
                    outer.avatar = resp.avatar;
                    outer.hide();
                    outer.root.menu.show();
                } else {  // 如果未登录则需要弹出登录界面
                    outer.login();
                }
            }
        });
    }

    acapp_login(appid, redirect_uri, scope, state) {
        let outer = this;
        // resp是redirect_uri的返回值,此处为用户名和头像
        this.root.acwingos.api.oauth2.authorize(appid, redirect_uri, scope, state, function(resp) {
            console.log(resp);
            if (resp.result === 'success') {
                outer.username = resp.username;
                outer.avatar = resp.avatar;
                outer.hide();
                outer.root.menu.show();
            }
        });
    }

    getinfo_acapp() {
        let outer = this;
        $.ajax({
            url: 'https://app4007.acapp.acwing.com.cn/settings/acwing/acapp/apply_code/',
            type: 'GET',
            success: function(resp) {
                if (resp.result === 'success') {
                    outer.acapp_login(resp.appid, resp.redirect_uri, resp.scope, resp.state);
                }
            }
        });
    }

    hide() {
        this.$settings.hide();
    }

    show() {
        this.$settings.show();
    }
}

注意,如果遇到跨域问题:Access to XMLHttpRequest at 'XXX',大概率是某个文件的内容写错了,可以检查 uWSGI 启动后的报错内容修改代码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Django学习笔记-AcApp端授权AcWing一键登录 的相关文章

  • 使用ideone时如何传入命令行参数?

    我正在使用 ideone 在线解释器 http ideone com http ideone com 来测试一些 C 和 Python 程序 如何指定命令行参数而不是使用 STDIN 输入 看起来你不能 但是快速破解应该做的伎俩 stati
  • pandas read_csv 之前预处理数据文件

    我使用 SAP 的数据输出 但它既不是 CSV 因为它不引用包含其分隔符的字符串 也不是固定宽度 因为它具有多字节字符 它是一种 固定宽度 字符 为了将其放入 pandas 我当前读取文件 获取分隔符位置 对分隔符周围的每一行进行切片 然后
  • 在Python中将大文件(25k条目)加载到dict中很慢?

    我有一个大约有 25000 行的文件 它是 s19 格式的文件 每行就像 S214780010 00802000000010000000000A508CC78C 像这样的事情怎么样 我做了一个测试文件 只有一行S21478001000802
  • matplotlib:渲染到缓冲区/访问像素数据

    我想使用 matplotlib 生成的图作为 OpenGL 中的纹理 到目前为止 我遇到的 matplotlib 的 OpenGL 后端要么不成熟 要么已经停止使用 所以我想避免使用它们 我当前的方法是将图形保存到临时 png 文件中 并从
  • Python 中“is”运算符的语义是什么?

    如何is运算符确定两个对象是否相同 它是如何工作的 我找不到它的记录 来自文档 http docs python org reference datamodel html 每个对象都有一个身份 一个类型 和一个值 对象的身份 一旦发生就永远
  • 使用 python 从 CSV 创建字典

    我有一个 CSV 格式的文件 其中 A B 和 C 是标题 我如何以Python方式将此CSV转换为以下形式的字典 A 1 B 4 C 7 A 2 B 5 C 8 A 3 B 6 C 9 到目前为止我正在尝试以下代码 import csv
  • 尝试校准keras模型

    我正在尝试通过 Sklearn 实现来校准我的 CNN 模型CalibratedClassifierCV 尝试将其包装为KerasClassifier并覆盖预测功能但没有成功 有人可以说我做错了什么吗 这是模型代码 def create m
  • Python FTP下载550错误

    我编写了一个 ftp 爬虫来下载特定文件 它会一直工作 直到找到要下载的特定文件 然后抛出此错误 ftplib error perm 550 该文件存在于我的下载文件夹中 但文件大小为 0 kb 我需要转换某些内容才能下载吗 我可以访问 f
  • ImproperlyConfigured at / 不允许空静态前缀 - Django

    我正在使用 Django 上传 显示图像 该网站部署在 Heroku 上 下列的this https coderwall com p bz0sng教程我能够成功上传图像 但是 图像并未显示在模板中 然后我了解到我的 urls py 末尾应该
  • 为什么 pip 已经是最新的了却要求我升级?

    我全新安装了 python 3 7 1 64 位 并使用最新的 pyCharm 作为我的 IDE 我在这台机器上没有安装其他 python 我去安装 numpy 并收到以下消息 venv C Users John PycharmProjec
  • 使用 Python-VLC 的 PyInstaller:无属性“media_player_new”错误

    我使用 Python VLC 创建视频播放器 并使用 PyInstaller 在 Windows 10 计算机上生成可执行文件 最初 它给了我错误 Import Error Failed to load dynlib dll libvlc
  • 类变量:“类列表”与“类布尔值”[重复]

    这个问题在这里已经有答案了 我不明白以下示例的区别 一次类的实例可以更改另一个实例的类变量 而另一次则不能 示例1 class MyClass object mylist def add self self mylist append 1
  • Django 1.7:如何使用 html/css 文件作为模板发送电子邮件

    从 Django 1 7 开始 可以send email 使用新参数 html message 不幸的是 没有关于如何使用它的全面指南 新手友好 或者至少我找不到它 我需要使发送的电子邮件变得漂亮 因此 我试图弄清楚如何将我的消息包含到 h
  • 发布数据以同时创建相关的 Tastypie 资源?

    给定两个相关的 Django 模型A and B in a 一对多关系 模型 py class A models Model name models CharField max length 5 class B models Model n
  • 将 str.contains 映射到 pandas DataFrame

    python 初学者 我正在寻找创建字符串的字典映射以及关联的值 我有一个数据框 想要创建一个新列 如果字符串匹配 则会将该列标记为 x df pd DataFrame comp dell notebook dell notebook S3
  • Django 管理通过 ImageField 链接到图像

    我已经设置了一个活动的 Django 模型 其中包含以下字段 class Activity models Model thumbnail models ImageField upload to thumbs blank True null
  • 本地主机上的 Google App Engine GQL 查询

    我正在 Google App Engine Windows 上的 SDK 版本 1 7 0 上开发一个应用程序 我需要经常测试该应用程序 并且此测试涉及数据存储上的大量 GQL 查询 您可以在 App Engine 管理界面的浏览器中在线运
  • Django 中使用外键的抽象基类继承

    我正在尝试在 Django 支持的网站上进行模型继承 以遵守 DRY 我的目标是使用一个名为 BasicCompany 的抽象基类来为三个子类提供通用信息 Butcher Baker CandlestickMaker 它们位于各自的应用程序
  • 将函数按元素应用于两个 DataFrame

    如何应用函数z ij f x ij y ij 来自数据框X and Y相同大小并将结果保存到 DataFrameZ 这取决于你有什么样的功能 很多功能已经被矢量化为数据框 例如 等等 所以对于这些功能 你可以简单地做Z X Y or Z X
  • 如何在不同的目录中执行python脚本?

    Solved对于可能觉得这有帮助的人 请参阅下面我的答案 我有两个脚本 a py 和 b py 在我当前的目录 C Users MyName Desktop MAIN 中 我运行 gt python a py 第一个脚本 a py 在我当前

随机推荐

  • 摄像机跟随之第三人称视角(一)

    在我们开发游戏的时候 总避免不了对摄像机的设置 当然 这也因为每款游戏类型不一样 所以摄像机的跟随或者说放置位置不一样 这也要求我们写出合适的算法 本篇就主要是针对第三人称视角的摄像机 进行算法设计分析 首先来分析第三人称摄像机的特性 1
  • thinkphp6 入门(1)--安装、路由规则、多应用模式

    一 安装thinkphp6 具体参考官方文档 安装 ThinkPHP6 0完全开发手册 看云 下面仅列举重要步骤 ThinkPHP6 0的环境要求如下 PHP gt 7 2 5 1 安装Composer 2 安装稳定版thinkphp 如果
  • 浏览器显示无法解析服务器的DNS地址,搜狗浏览器无法解析服务器的DNS地址怎么解决...

    5 点击 使用下面的DNS 服务器地址 然后在下面填入 114 114 114 114 点击 确定 确定 保存设置即可解决问题 3 重置winsock 目录设置后等待提示已成功刷新DNS 解析缓存 继续输入 netsh winsock re
  • 【Kubernetes部署篇】Ansible自动化工具离线部署K8s 1.27版本

    一 前提须知 采用kubeadm方式 目前只支持 单Master 多Node部署架构 需要主机网络互通 没有网络限制 需要使用root用户权限进行部署 二 使用Ansible部署K8S集群步骤 第一步 获取离线安装包 百度网盘获取 MD5
  • L298N模块驱动电机(实现pwm调速)

    1 占空比是高电平所占周期时间与整个周期时间的比值 2 脉宽调制 PWM 基本原理 控制方式就是对逆变电路开关器件的通断进行控制 使输出端得到一系列幅值相等的脉冲 用这些脉冲来代替正弦波或所需要的波形 也就是在输出波形的半个周期中产生多个脉
  • Angular前端与springBoot后端的http请求交互

    前言 开发模式采用前后端分离 前端用angular 这里指的是angular2 不要和angularjs混淆了 后端采用springBoot 主要介绍下angular与后端接口调用问题 思路 其实官网都有调用 只是一些地方要注意到 而往往就
  • c51延时子程序delayms(uchar ms);

    void delayms unsigned char ms 延时子程序 unsigned char i while ms for i 0 i lt 109 i 晶振为11 0592M时i lt 109 若为12M时 i 需设为 lt 120
  • 最全java面试题及答案(208道)

    本文分为十九个模块 分别是 Java 基础 容器 多线程 反射 对象拷贝 Java Web 异常 网络 设计模式 Spring Spring MVC Spring Boot Spring Cloud Hibernate MyBatis Ra
  • 转: 边界值法:什么是上点,内点,离点

    以下转自 web http yzylion blog 163 com blog static 89406994200892784325898 什么是上点 内点 离点 2008 10 27 20 43 25 分类 默认分类 标签 it评论 字
  • 中文Python(3)Python语言中的占位神器pass语句

    中文Python 3 Python语言中的占位神器pass语句 Python作为一门高级计算机语言 其程序结构相对简单 同时又灵活多变 在编写程序时 我们经常需要考虑各种情况的处理 但有时候一些情况我们还没有想好如何去处理 这时候就可以借助
  • CUDA中的锁页内存 Page-Locked Memory详解

    Page Locked内存 运行时提供的函数允许使用锁页 也称为固定 主机内存 与 malloc 分配的常规可分页主机内存相反 cudaHostAlloc 和 cudaFreeHost 分配和释放锁页主机内存 cudaHostRegiste
  • maven使用及高级

    简介 maven工作全靠插件plugin maven的工作目标也是插件 管理插件 运行插件 安装 下载后 设置m2 home及path 使用mvn v命令检查安装是否成功 maven安装包非常小 因为maven被设计成将主要的指责委派给一组
  • PDF加密文件无法打印

    前几天遇到一个要打印的PDF可是打印的按钮就是无法使用 用wps打开后发现已被加密了 后在找解密方法时找到一个网站可以使用 这边暂且记着以后方便使用 http www pdfdo com pdf remove restriction asp
  • Truffle报'module'未定义错误解决方法

    在使用truffle compile命令时出现如下错误 只要把文件夹中的 truffle js 文件重命名为其他名字即可
  • ELK系列(八)、使用Filebeat+Redis+Logstash收集日志数据

    前面提到过 logstash占资源很大 filebeat更加轻量 一般都是组合使用 难免会有logstash宕掉的时候 这时候filebeat再往logstash里写数据就写不了了 这期间的日志信息可能就无法采集到了 因此一般都会采用red
  • uniapp实现村组数据多级联动

    选择了村的数据才会有组的数据 下面为接口的返回格式 首先是点击输入框 弹出u picker组件 下面是主要代码
  • 物联网概述

    1 1 物联网的定义和特征 1 物联网的定义 物联网的英文名称是 The Internet of things IoT 意即 物物相连的互联网 也就是说 物联网是在互联网基础上的延伸和扩展的网络 其用户端延伸和扩展到了任何物品与物品之间 进
  • SQL数据库自学

    SQL数据库 一 1 相关概念 数据库 数据管理系统 SQL 2 SQL通用语法 3 语句分类 1 DDL 数据定义语言 对数据库操作 对表操作 2 DML 对数据库中表的数据进行操作 添加数据 修改数据 删除数据 3 DQL 查询数据 基
  • sql注入时union出错(Illegal mix of collations for operation UNION)

    当union连接information schema时 就会出现Illegal mix of collations for operation UNION 经过一系列的检查发现之后 其实是union连接的字段的字符规则不一样 此方法只适用于
  • Django学习笔记-AcApp端授权AcWing一键登录

    笔记内容转载自 AcWing 的 Django 框架课讲义 课程链接 AcWing Django 框架课 AcApp 端使用 AcWing 一键授权登录的流程与之前网页端的流程一样 只有申请授权码这一步有一点细微的差别 我们在打开 AcAp