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

news2024/11/13 9:40:16

笔记内容转载自 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 启动后的报错内容修改代码。

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

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

相关文章

Servlet的使用(JavaEE初阶系列17)

目录 前言: 1.Servlet API的使用 1.1HttpServlet 1.2HttpServletRequest 1.3HttpServletResponse 2.表白墙的更新 2.1表白墙存在的问题 2.2前后端交互接口 2.3环境准备 2.4代码的编写 2.5数据的持久化 2.5.1引入JDBC依赖 2.5.2创建数据库 2.5.3编写数…

云原生之使用Docker部署SSCMS内容管理系统

云原生之使用Docker部署SSCMS内容管理系统 一、SSCMS介绍二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载SSCMS镜像五、部署SSCMS内容管理系统5.1 创建SSCMS容器5.2 检查SSC…

LabVIEW开发检测肌肉疾病的新技术

LabVIEW开发检测肌肉疾病的新技术 肌电图(EMG)是一种生物信号,可检测骨骼肌在收缩过程中产生的电流,以量化神经肌肉活动。了解肌电图信号需要了解骨骼肌以及允许它们产生生物电信号的机制。它还考虑了影响信号的众多系统和事件。…

Django基础5——ORM中间程序

文章目录 一、基本了解二、ORM基本操作2.1 连接数据库2.1.1 使用sqlite数据库2.1.2 使用MySQL数据库 2.2 对数据库操作2.2.1 增(前端数据——>数据库)2.2.2 查(数据库——>前端展示)2.2.3 改(修改数据&#xff0…

【人脸考勤项目】

本项目主要是基于Opencv完成的人脸识别的考勤系统 人脸检测器的5种实现方法 方法一:haar方法进行实现(以下是基于notebook进行编码) # 步骤 # 1、读取包含人脸的图片 # 2.使用haar模型识别人脸 # 3.将识别结果用矩形框画出来# 导入相关包 …

自动驾驶感知传感器标定安装说明

1. 概述 本标定程序为整合现开发的高速车所有标定模块,可实现相机内参标定和激光、相机、前向毫米波 至车辆后轴中心标定,标定参数串联传递并提供可视化工具验证各个模块标定精度。整体标定流程如下,标定顺序为下图前标0-->1-->2-->3,相同编号标定顺序没有强制要求…

疲劳检测-闭眼检测(详细代码教程)

简介 瞌睡经常发生在汽车行驶的过程中,该行为害人害己,如果有一套能识别瞌睡的系统,那么无疑该系统意义重大! 实现步骤 思路:疲劳驾驶的司机大部分都有打瞌睡的情形,所以我们根据驾驶员眼睛闭合的频率和…

微服务鉴权中心之网关配置SpringSecurity+oauth2

微服务鉴权中心流程如下: 1. 网关配置oauth2之TokenStore存储方式,此处用RedisTokenStore Configurationpublic class TokenConfig {Autowiredprivate RedisConnectionFactory redisConnectionFactory;Beanpublic TokenStore tokenStore() {return new …

生信分析Python实战练习 2 | 视频20

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在:https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

如何做好微信号标签管理?

微信除了生活外,也越来越多企业用微信来联系维护客户和发展自己的私域流量池,微信好友越加越多。 为了提高微信的管理效率,针对不同的微信好友群体进行群发,但每次都要手动打标签很费时间,那么有没有什么工具可以批量打…

Java项目01——项目配置

1. 前置知识: 1.把项目提交到本地仓库 2. gitee新建仓库,idea推送 3. 新建数据库,直接用navicate导入sql语句即可 4. 前后端联调,先编译,然后运行 5. 前端发送的请求,是如何请求到后端服务的&#xff1f…

【提升接口响应能力的最佳实践】常规操作篇

文章目录 1. 并行处理简要说明CompletableFuture是银弹吗?测试案例测试结论半异步,半同步总结 2. 最小化事务范围简要说明编程式事务模板 3. 缓存简要说明 4. 合理使用线程池简要说明使用场景线程池的创建参数的配置建议 线程池的监控线程池的资源隔离 5…

买卖股票的最佳时机 II【贪心策略】

买卖股票的最佳时机 II 给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。 在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。 返回 你能获得的…

用wireshark流量分析的四个案例

目录 第一题 1 2 3 4 第二题 1 2 3. 第三题 1 2 第四题 1 2 3 第一题 题目: 1.黑客攻击的第一个受害主机的网卡IP地址 2.黑客对URL的哪一个参数实施了SQL注入 3.第一个受害主机网站数据库的表前缀(加上下划线例如abc) 4.…

【力扣】盛最多水的容器

目录 题目 题目初步解析 水桶效应 代码实现逻辑 第一步 第二步 第三步 代码具体实现 注意 添加容器元素的函数 计算迭代并且判断面积是否是最大值 总代码 运行结果 总结 题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是…

GB28181学习(一)——总述

概念 GB28181全称是“公共安全视频监控联网系统信息传输、交换、控制技术要求”,它定义了视频监控设备之间的联网通信协议,旨在实现视频监控系统的互联互通和统一管理。 架构 GB28181协议的基本架构包括设备端和平台端。 设备端:包括视频监…

广告行业小程序搭建教程,零基础也能轻松上手

随着移动互联网的发展和智能手机的普及,小程序成为了各行业推广和服务的利器。对于广告行业来说,拥有一个专属的小程序不仅能提升企业形象,还可以方便用户查看广告、咨询服务等。那么,如何简单操作一键搭建广告行业小程序呢&#…

小区物业业主管理信息系统设计的设计与实现(论文+源码)_kaic

摘 要 随着互联网的发展,网络技术的发展变得极其重要,所以依靠计算机处理业务成为了一种社会普遍的现状。管理方式也自然而然的向着现代化技术方向而改变,所以纯人工管理方式在越来越完善的现代化管理技术的比较之下也就显得过于繁琐&#x…

SpringCloud超详细教程

1.认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢? 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构:将业务的所有…

WEBRTC 的RTP/RTCP的 NACK, PLI,SLI,FIR

1,概述 在网络环境不是太好的情况下,比如网络拥塞比较严重,丢包率可能比较高,简单实用NACK重传的机制,这样就会有大量的RTCP NACK报文,发送端收到相应的报文,又会发送大量指定的RTP报文&#xf…