Django框架课-web端AcWing一键登录

web端AcWing一键登录

Django中集成Redis

什么是Redis?

什么是Redis-菜鸟教程

安装Django_Redis

pip install django_redis

配置settings.py

配置django的缓存机制。将下面的内容复制入acapp/acapp/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

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

使用top来查看是否启动成功:
image

一键登录的过程

client:用户
web:网站端(后台)
acwing:acwing端(后台)

一键登录的过程:

1.用户点击一键登录按钮,client向web发送一个请求
(接下来是oauth2授权模式过程)
2.web端向acwing端发送请求,发送的请求会携带一些data,比如自己的acappid等
3.acwing端向client发送信息,端询问用户是否授权
4.client向acwing端发送请求,用户同意授权
5.acwing端向web端发送信息,发送授权码
6.web端向acwing端发送请求,携带授权码、appid、自己的app秘钥(secret)发送给acwing,向acwing请求一个授权令牌
7.acwing端想web端发送信息,发送acess_token授权令牌和openid(用来识别用户)给web端
8.web端向acwing端发送请求,携带acess_token授权令牌和openid向acwing请求openid对应的用户用户信息,比如用户名和头像。
9.acwing端向web端返回请求的用户信息

image

详情:web端acwing一键登录

openid:可以用来识别用户,有的时候某个用户在acwing端里改了昵称,acapp从acwing中获得的是改之后的昵称,想要识别出来还是这个用户,就需要有一个用户始终变不了的东西,就是openid

实现

修改player表结构

第三方登录用户是openid来标记的,先去给player加一个openid。(目前一个用户只有用户名和头像两个属性)

对数据库的表修改,进入到models/player/player.py里去修改

class Player(models.Model):
    user = models.OneToOneField(User,on_delete=models.CASCADE)
    photo = models.URLField(max_length = 256,blank = True)
    openid = models.CharField(default="", max_length=50, blank=True, null=True)

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

更新数据库:
python3 manage.py makemigrations
python3 manage.py migrate

然后重新打包,重启服务

访问管理员界面发现player多了一个openid属性

申请授权码code函数

这一步实现的是,用户点击一键登录,向web端发送ajax请求(get请求https://app4346.acapp.acwing.com.cn/settings/acwinglogin/web/apply_code/这个url),web端返回一个封装好的链接(带有appid等参数)给前端了,前端再重定向到了这个链接。

后端中封装好一个链接,这个链接带有appid,redirect_uri,scope,state这些参数,把这个封装好的链接给前端,让前端去对这个链接发送get请求

后端将参数拼接封装好后,应该返回给前端一个JsonResponse。

这里要写两个函数apply_codereceive_code函数,一个用来封装链接给前端去想acwing请求,一个是后端用来接收acwing端返回的授权码code的函数

写views函数、urls、js (写这些文件新建文件夹的时候记得新建一个__init__.py文件,这样路径会被django识别到)

game/views/settings/acwinglogin/web里新建apply_code.pyreceive_code.py两个视图函数

game/urls/settings/acwinglogin/里新建index.py
index.py:

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

urlpatterns = [
    path("web/apply_code/",apply_code,name="settings_acwinglogin_web_apply_code"),
    paht("web/receive_code/",receive_code,name="settings_acwinglogin_web_receive_code"),
]

写完这个路由,修改一下settings/index.py的路由,将刚刚写的路由include进来。

接下来接续补充两个视图函数:
apply_code.py是用来向封装链接返回给前端的,要返回一个JsonResponse对象

receive_code.py接收到授权码之后需要做重定向工作

apply_code.py:

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

def get_state(): // 返回随机状态码
    res = ""
    for i in range(8):
        res += str(randint(0,9))
    return res

def apply_code(request):
    appid = "4346"
    redirect_uri = ("https://app4346.acapp.acwing.com.cn/settings/acwinglogin/web/receive_code/")
    scope = "userinfo"
    state = get_state()

    cache.set(state,True,7200) # 有效期2h

    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)
    })

image

可以发现,这个地址已经有后端返回来封装好的链接了,这个链接想试试对不对,直接访问就好

接下来写前端js部分
需要实现,点击一键登录之后,前端通过ajax请求从apply_code_url拿到封装好的链接,然后重定向到这个链接

给一键登录这个按钮添加一个事件

acwing_login(){
        $.ajax({
            url: "https://app4346.acapp.acwing.com.cn/settings/acwinglogin/web/apply_code/",
            type: "GET",
            success: function(resp){
                console.log(resp);
                if(resp.result === "success"){
                  window.location.replace(resp.apply_code_url);
                }
            }
        });
    }

重新打包重启服务,现在点击一键登录就能跳转到封装好的链接页面(acwing授权登录页面)

用户点击一键登录,向web端发送ajax请求(get请求https://app4346.acapp.acwing.com.cn/settings/acwinglogin/web/apply_code/这个url),web端返回一个封装好的链接(带有appid等参数)给前端了,前端再重定向到了这个链接(这个链接就是acwing确认是否授权登录的页面)

当用户同意后,acwing端向redirect_uri的url携带code和state参数发起请求(这个url是在之前封装好的链接里作为参数发送给acwing端的)

receive_code.py:

from django.shortcuts import redirect

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

    return redirect("index")

现在,就拿到了授权码code了。

申请授权令牌access_token和用户的openid

现在有授权码,再加上appid(应用唯一id)和secret(应用秘钥)去向acwing申请授权令牌和用户openid

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': "写自己应用的appid",
        'secret': "写自己应用的秘钥",
        'code': code    
	}

现在就拿到了acwing的授权令牌了

申请用户信息

拿到了access_token授权令牌就可以去申请用户信息了

这时我们有access_token和openid,先特判断该用户是否在数据库中存在,如果这个openid的用户已经存在,就直接让这个用户登录就行

如果这个用户是个新用户,并不存在,就去携带access_token和openid向acwing端请求用户名和头像

请求到的用户名可能已经存在,我们往用户名后面多添加一位随机0~9的数字。
不要从1开始枚举添加,这样时间复杂度比较大。重名就添加数字,直到没有出现过这个用户名为止

然后将这个用户添加到user表和player表中后登陆这个用户就可以了

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)
    if players.exists(): # 如果该用户信息已经存在,就直接登录
        login(request,players[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
    }
    userinfo_res = requests.get(get_userinfo_url,params=params).json()
    username = userinfo_res['username']
    photo = 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,photo=photo,openid=openid)

    login(request,user)

    return redirect("index")

然后重启uwsgi服务就可以啦,web端acwing一键登录实现完毕!!!

posted @ 2023-03-01 20:43  r涤生  阅读(67)  评论(0编辑  收藏  举报