Django框架课-web端AcWing一键登录
web端AcWing一键登录
Django中集成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
来查看是否启动成功:
一键登录的过程
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端返回请求的用户信息
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_code
、receive_code
函数,一个用来封装链接给前端去想acwing请求,一个是后端用来接收acwing端返回的授权码code的函数
写views函数、urls、js (写这些文件新建文件夹的时候记得新建一个__init__.py
文件,这样路径会被django识别到)
在game/views/settings/acwinglogin/web
里新建apply_code.py
和receive_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)
})
可以发现,这个地址已经有后端返回来封装好的链接了,这个链接想试试对不对,直接访问就好
接下来写前端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一键登录实现完毕!!!