更多内容请点击 我的博客 查看,欢迎来访。
官方文档: https://pyjwt.readthedocs.io/en/latest/installation.html
Json web token (JWT)
, 根据官网的定义,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
[外链图片转存失败(img-qli33Ad0-1562813971810)(https://www.starmeow.cn/media/blog/images/2019/07/BLOG_20190711_105308_70.png “博客图集BLOG_20190711_105308_70.png”)]
与之相反这些正是JWT的优势所在:
pip install pyjwt
Django的 Python Console中测试
>>> from django.conf import settings
>>> import jwt
>>> key = settings.SECRET_KEY # 这个可以自定义密钥,为了方便直接使用Django的密钥
# 加密
>>> encoded = jwt.encode({'username': 'admin', 'site': 'http://testdomain.starmeow.cn'}, key, algorithm='HS256')
# 可以查看生成的Token
>>> encoded
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwic2l0ZSI6Imh0dHA6Ly9zdWliaWFuLnN0YXJtZW93LmNuIn0.5CzNoZ2aWyNYYnImrJvGJuLZlJbUeCVTg-_PFq_XF7o'
# 生成的是byte类型,可转换为字符串
>>> encoded.decode('utf-8')
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwic2l0ZSI6Imh0dHA6Ly9zdWliaWFuLnN0YXJtZW93LmNuIn0.5CzNoZ2aWyNYYnImrJvGJuLZlJbUeCVTg-_PFq_XF7o'
{'username': 'admin', 'site': 'http://testdomain.starmeow.cn'}, key, algorithm='HS256'
三个参数:
SECRET_KEY
配置变量,这个秘钥主要用在下文Signature签名中,服务端用来校验Token合法性,这个秘钥只有服务端知道,不能泄露;payload,这是认证的依据,也是后续解析token后定位用户的依据,需要包含特定用户的特定信息,如下面示例中注册了data
声明,data声明中用户的username
、nickname
等,在“用户鉴权”方法中,解析token完成后要利用这个username
来查找并返回用户信息给用户。这里的data声明是我们自己加的,pyjwt内置注册了以下几个声明(建议但不强制使用):
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwic2l0ZSI6Imh0dHA6Ly9zdWliaWFuLnN0YXJtZW93LmNuIn0.5CzNoZ2aWyNYYnImrJvGJuLZlJbUeCVTg-_PFq_XF7o
生成的Token:
Header
头部,Payload负载,Signature签名:Header.Payload.Signature
Header
和第二部分Payload
只是对原始输入的信息转成了base64编码,第三部分Signature
是用header+payload+secret_key
进行加密的结果解码第一部分Header,包括类别(typ)、加密算法(alg):通常直接使用 HMAC SHA256
>>> import base64
>>> base64.b64decode("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9")
b'{"typ":"JWT","alg":"HS256"}'
解码第二部分Payload(加==
:如果要编码的二进制数据不是3的倍数,最后会剩下1个或2个字节怎么办?此时,需在原数据后面添加1个或2个零值字节,使其字节数是3的倍数。然后,在编码后的字符串后面添加1个或2个等号“=”,表示所添加的零值字节数。解码的时候,会自动去掉。)
第二部分主要存放有效信息,由于这里用的是可逆的base64 编码,所以第二部分的数据实际上是明文的。应该避免在这里存放不能公开的隐私信息。
>>> base64.b64decode("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwic2l0ZSI6Imh0dHA6Ly9zdWliaWFuLnN0YXJtZW93LmNuIn0==")
b'{"typ":"JWT","alg":"HS256"}{"username":"admin","site":"http://testdomain.starmeow.cn"}'
三部分一起解码,可以看到最后一部分被加密了无法查看
>>> base64.b64decode("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwic2l0ZSI6Imh0dHA6Ly9zdWliaWFuLnN0YXJtZW93LmNuIn0.5CzNoZ2aWyNYYnImrJvGJuLZlJbUeCVTg-_PFq_XF7o==")
b'{"typ":"JWT","alg":"HS256"}{"username":"admin","site":"http://testdomain.starmeow.cn"}9\x0b3hgf\x96\xc8\xd6\x18\x9c\x89\xab&\xf1\x89\xb8\xb6e%\xb5\x1e\tT\xe0
# 解密Token
>>> decoded = jwt.decode(encoded, key, algorithms='HS256')
>>> decoded
{'username': 'admin', 'site': 'http://testdomain.starmeow.cn'}
到的payload可以跟原来生成payload进行比较来验证token的有效性。
将自定义的数据放在data
中。
# 设置payload保存的信息
payload = {
'exp': timezone.now() + datetime.timedelta(minutes=3), # 设置过期时间:https://pyjwt.readthedocs.io/en/latest/usage.html#expiration-time-claim-exp
'iat': timezone.now(), # 设置发布时间:https://pyjwt.readthedocs.io/en/latest/usage.html#issued-at-claim-iat
'data': {'username': username, 'nickname': nickname} # 添加自定义的数据,保存在data中
}
# 加密
encoded = jwt.encode(payload, key, algorithm='HS256')
# 解密
decoded = jwt.decode(encoded, key, algorithms='HS256')
子域名 testdomain.starmeow.cn 想要请求 www.starmeow.cn/login/ 认证用于登录,涉及到跨域请求,可以把 testdomain.starmeow.cn 当作一个独立的应用。
这里直接修改的普通视图,先判断session,当用户在这个域没登陆时(因为没在session没在这个域中对这个键赋值,一般是没有值的)。
再来尝试获取Token,由于前端通过Authorization
、Token
请求会传过来Token的值,或者直接获取COOKIEs
中的Token值,解析里面的用户名,解码成功后返回用户名。
def get_request_username(request):
"""
先从session中获取登录用户名,如果没获取到,再尝试从Token中解码用户名
:param request:
:return:
"""
username = request.session.get('session_simple_user_name', '')
if not username:
http_authorization = request.META.get('HTTP_AUTHORIZATION')
# print('超文本传输协议:', http_authorization) # 如果是Token认证,就是:Token code???????code
if http_authorization:
# 尝试token认证登录
method, code = http_authorization.split(' ') # 空格分割字符串
print('从HTTP_AUTHORIZATION中获取Token')
else:
code = request.COOKIES.get('simpleauth_token', '')
print('从COOKES中获取Token')
# print(code)
try:
import jwt
from django.conf import settings
decoded = jwt.decode(code, settings.SECRET_KEY, algorithm='HS256')
username = decoded.get('data').get('username') # 获取用户名:model中生成Token时放在payload的data中的数据
except:
pass
return username
class PhaseGroupBuy(View):
def get(self, request, phase_id):
print('团购登记主页')
editable = False # 可编辑
simpleauth_url = get_simpleauth_url(request) # 获取登录地址的url函数,相当于只返回一个域名
# 获取当前登录用户
# simple_user = SimpleUser.objects.get(username=request.session.get('session_simple_user_name', ''))
request_username = get_request_username(request)
if request_username == '':
# 如果没有获取到请求的用户,将cookie中的内容删除
response = render(request, 'group_buy_v0_3/group-buy.html', locals())
response.delete_cookie('simpleauth_nickname', path='/')
response.delete_cookie('simpleauth_token', path='/')
response.delete_cookie('simpleauth_username', path='/')
return response
print('获取到用户名:', request_username)
# 其他代码
如果cookie中没有simpleauth_username
值,则强制显示模态框,要求用户登录
//登录相关
let simpleauth_token = $.cookie('simpleauth_token');
function show_login_modal() {
$('#tokenAuthLoginModal').modal({
show: true,
backdrop: "static",//点击空白处不关闭对话框
keyboard: false //esc键盘不关闭.
})
}
//console.log(simpleauth_token);
//进入页面如果没获取到token就登录显示
if (simpleauth_token === undefined || simpleauth_token === '') {
show_login_modal();
}
模态框代码如下
<div class="modal fade" id="tokenAuthLoginModal" tabindex="-1" role="dialog" aria-labelledby="tokenAuthLoginModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content modal-login-bg">
<div class="modal-header">
div>
<div class="modal-body">
<div class="row w-100">
<div class="col-lg-4 mx-auto">
<div class="auth-form-light text-left p-5">
<div class="brand-logo">
<img src="{% static 'group_buy_v0_3/images/logo.png' %}" style="max-width: 120px">
div>
<h4><i class="mdi mdi-heart text-danger">i>你好,感谢支持<i class="mdi mdi-heart text-danger">i>h4>
<h6 class="font-weight-light" id="id-login-info">请输入帐密登录h6>
<form class="pt-3" id="id-login-form" autocomplete="off">
<div class="form-group">
<input type="text" class="form-control form-control-lg" name="username" placeholder="用户名">
div>
<div class="form-group">
<input type="password" class="form-control form-control-lg" name="password" placeholder="密码" autocomplete="off">
div>
<div class="mt-3">
<a class="btn btn-block btn-gradient-primary btn-lg font-weight-medium auth-form-btn" href="javascript:void(0)" id="id-login-button">登录a>
div>
{% csrf_token %}
<div class="my-2 d-flex justify-content-between align-items-center">
<div class="form-check">
div>
<a href="//{{ simpleauth_url }}/simpleauth/modify/?next=//{{ request.get_host }}{{ request.get_full_path }}" class="auth-link text-black">修改密码a>
div>
<div class="text-center mt-4 font-weight-light">
没有账户? <a href="//{{ simpleauth_url }}/simpleauth/register/?next=//{{ request.get_host }}{{ request.get_full_path }}" class="text-primary">创建a>
div>
form>
div>
div>
<p style="position:absolute; bottom:0; padding: 0; margin:0; text-align: center;width: 100%">
<small>Copyright © 2020 <a href="//{{ simpleauth_url }}/simpleauth/login/">吾星喵认证系统a> All Rights Reserved.
small>
p>
div>
div>
<div class="modal-footer">
div>
div>
div>
div>
[外链图片转存失败(img-Be8ijwak-1562813971812)(https://blog.starmeow.cn/media/blog/images/2019/07/BLOG_20190711_105256_93.png “博客图集BLOG_20190711_105256_93.png”)]
该模态框限制不可退出。
点击登录按钮,获取表单,进行ajax提交
$('#id-login-button').click(function () {
//console.log($("#id-login-form").serialize());
//$.ajaxSettings.async = false;
//let token = '';
//$.get('//{{ simpleauth_url }}/simpleauth/token/', function (data) {
// token = data.token; //simpleauth中获取token,不使用
//});
$.ajax({
url: '//{{ simpleauth_url }}/simpleauth/login/',
type: 'POST',
cache: false,
//dataType: "json",
//headers: {"X-CSRFToken": $.cookie('csrftoken')},
data: $("#id-login-form").serialize(),
async: true,
beforeSend: function (xhr, settings) {
//xhr.setRequestHeader("X-CSRFToken", token);
xhr.setRequestHeader("X-Requested-With", 'XMLHttpRequest'); //指定为ajax访问
},
success: function (data) {
console.log(data);
if (data.state_value === 1) {
//设置cookie中的值
let expiresDate = new Date();
expiresDate.setTime(expiresDate.getTime() + (30 * 60 * 1000));
$.cookie('simpleauth_username', data.data.username, {expires: expiresDate, path: '/'});
$.cookie('simpleauth_nickname', data.data.nickname, {expires: expiresDate, path: '/'});
$.cookie('simpleauth_token', data.data.token, {expires: expiresDate, path: '/'});
//填充用户名
$('#id-login-nickname').html(data.data.nickname);
//隐藏模态框
$('#tokenAuthLoginModal').modal('hide');
setTimeout(function () {//1秒后刷新页面
window.location.reload();
}, 500)
} else {
$('#id-login-info').html(data.msg); //登陆出错,填充提示信息
setTimeout(function () {//3秒后跳转
$('#id-login-info').html('请重新输入');
}, 3000)
}
},
})
});
定义def _generate_jwt_token(self)
方法用于生成token
class SimpleUser(models.Model):
username = models.CharField(max_length=30, verbose_name='用户名')
nickname = models.CharField(max_length=30, null=True, blank=True, verbose_name='昵称')
email = models.EmailField(null=True, blank=True, verbose_name='邮箱')
password = models.CharField(max_length=200, verbose_name='密码')
group = models.ManyToManyField(SimpleGroup, blank=True, related_name='users', verbose_name='用户组')
permission = models.ManyToManyField(SimplePermission, blank=True, related_name='users', verbose_name='权限')
# 生成jwt的token
def _generate_jwt_token(self):
import datetime
import jwt
from django.conf import settings
from django.utils import timezone
payload = {
'exp': timezone.now() + datetime.timedelta(minutes=3), # 设置过期时间:https://pyjwt.readthedocs.io/en/latest/usage.html#expiration-time-claim-exp
'iat': timezone.now(), # 设置发布时间:https://pyjwt.readthedocs.io/en/latest/usage.html#issued-at-claim-iat
'data': {'username': self.username, 'nickname': self.nickname} # 添加自定义的数据,为一个包含用户名和昵称的字典
}
secret = settings.SECRET_KEY # 密钥字符串
algorithm = 'HS256' # 指定签名算法
encoded = jwt.encode(payload, secret, algorithm) # 生成byte类型的token
token = encoded.decode('utf-8') # 转为字符串
return token
@property # 将方法转为属性来使用,直接使用 obj.token 就可以获取值
def token(self):
return self._generate_jwt_token()
class Meta:
verbose_name_plural = verbose_name = '简单用户'
def __str__(self):
return '【{}】 昵称:{}'.format(self.username, self.nickname)
在model中编写完生成Token的方法后,需要重启Django Console才可进行下面的测试,否则obj.token
无法使用
>>> from simpleauth.models import SimpleUser
>>> user = SimpleUser.objects.get(username='admin')
user.token
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI0NDM5ODcsImlhdCI6MTU2MjQ0MzgwNywiZGF0YSI6eyJ1c2VybmFtZSI6ImFkbWluIiwiZW1haWwiOiJhZG1pbkBhZG1pbi5jb20ifX0.Ry5BHjIdCuqhAyC-Wo558q4JQI9Qaf4NgTIm034cumg'
关闭csrf验证
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
class SimpleUserLoginView(View):
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request):
print('用户登录是否是Ajax请求:', request.is_ajax())
next_url = request.GET.get('next', '')
user_login_form = SimpleUserLoginForm()
return render(request, 'simpleauth/auth_v0_2/simple-auth-login.html', locals())
def post(self, request):
print('用户登录是否是Ajax请求:', request.is_ajax())
next_url = request.POST.get('next') # 获取隐藏表单的next
username = request.POST.get('username')
password = request.POST.get('password')
user_login_form = SimpleUserLoginForm(request.POST)
print(user_login_form.errors)
if user_login_form.is_valid():
user = SimpleUser.objects.get(Q(username=username) | Q(email=username))
# print(user)
if check_password(password, user.password):
# 【ajax请求】帐密校验通过
if request.is_ajax():
# 将model中的user.token传给前端token
return JsonResponse({'state_value': 1, 'msg': '登录成功', 'data': {'username': user.username, 'nickname': user.nickname, 'token': user.token}})
# 如果不是ajax请求,就进入下面判断
success_info = '用户登录成功。由于没有指定下一跳地址,请重新访问之前的页面'
# 用户已登录状态,session中有值
if request.session.get('session_simple_user_name') == username:
print('用户已登录,返回:', next_url)
if next_url:
return redirect(next_url)
else:
return render(request, 'simpleauth/auth_v0_2/simple-auth-info.html', {'success_info': success_info})
# return HttpResponse('用户登录成功。
由于没有制定下一跳地址,请重新访问之前的页面')
# 用户未登录状态,添加到session
else:
request.session['session_simple_user_name'] = username
request.session['session_simple_nick_name'] = user.nickname
if next_url:
return redirect(next_url)
else:
return render(request, 'simpleauth/auth_v0_2/simple-auth-info.html', {'success_info': success_info})
else:
msg = '用户名或密码错误'
# 【ajax请求】帐密错误时
if request.is_ajax():
return JsonResponse({'state_value': 0, 'msg': msg})
else:
messages.add_message(request, messages.INFO, msg)
return render(request, 'simpleauth/auth_v0_2/simple-auth-login.html', locals())
else:
# 【ajax请求】表单错误时
if request.is_ajax():
msg = ''
# print(user_login_form.errors.get_json_data()) # {'username': [{'message': '用户不存在,请注册', 'code': ''}]}
try:
for key, value in user_login_form.errors.get_json_data().items():
msg += value[0].get('message')
except BaseException:
msg += '其它错误'
return JsonResponse({'state_value': 0, 'msg': msg})
else:
return render(request, 'simpleauth/auth_v0_2/simple-auth-login.html', locals())
修改普通的认证方式,当用户以ajax请求时,返回json字符串提供给前端显示。
A应用中要设置跨域白名单,首先安装 pip install django-cors-headers
,https://pypi.org/project/django-cors-headers/
修改 settings.py
INSTALLED_APPS = [
# ...
'corsheaders', # 跨域(第1步):添加应用
# ...
]
MIDDLEWARE = [ # Or MIDDLEWARE_CLASSES on Django < 1.10
# ...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
# ...
]
# 跨域(第3步):配置其他信息
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
# CORS_ORIGIN_WHITELIST = ( # 白名单的完整域名
# "http://testdomain.starmeow.cn",
# "https://testdomain.starmeow.cn",
# )
CORS_ORIGIN_REGEX_WHITELIST = [ # 白名单的正则字符串,当有多个域名时,非常有用
r"^http://\w+\.starmeow\.cn",
r"^https://\w+\.starmeow\.cn",
]
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
'VIEW',
)
CORS_ALLOW_HEADERS = (
'XMLHttpRequest',
'X_FILENAME',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
)
修改以前的登录装饰器,增加http_authorization = request.META.get('HTTP_AUTHORIZATION')
的判断,如果session中没获取到用户名,如果没获取到,尝试从token中获取,代码占位,后面补充。
def simple_login_required(view_func):
"""
使用方法:
基于类的视图:@method_decorator(simple_login_required, name='dispatch'),添加到类上方
基于函数的视图:@simple_login_required,添加到函数的上方
如果request.is_ajax(),那么返回json字符串,例如:result = {'state_value': 0, 'msg': msg},0表示失败,1表示成功,msg表示提示信息
:param view_func:
:return:
"""
def my_login(request, *args, **kwargs):
# print(view_func)
next_url = '{}{}'.format(request._current_scheme_host, request.path)
# print('登陆后返回链接:', next_url)
simple_user_name = request.session.get('session_simple_user_name', '')
print('用户登录:', simple_user_name)
# 从session中获取session_simple_user_name,能获取到表明是已登录状态
if simple_user_name:
try:
return view_func(request, *args, **kwargs)
except BaseException as e: # 用户被删异常:simpleauth.models.SimpleUser.DoesNotExist: SimpleUser matching query does not exist.
print(e)
msg = '登录异常,请重新登录'
if simple_user_name == 'admin':
msg += str(e) + '访问/simpleauth/主页生成'
# 如果是ajax访问,返回json提示,否则跳转到登录页面
if request.is_ajax():
result = {'state_value': 0, 'msg': msg}
return JsonResponse(result)
else:
messages.warning(request, msg)
return redirect(reverse('simpleauth:simple_user_login') + '?next={}'.format(next_url))
else:
# 如果session认证登录失败,尝试进行token认证登录
http_authorization = request.META.get('HTTP_AUTHORIZATION')
print(http_authorization)
if http_authorization:
# 尝试token认证登录
pass
else:
# 如果token认证失败,最后再返回 未登录 提示
msg = '用户未登录'
if request.is_ajax():
result = {'state_value': 0, 'msg': msg}
return JsonResponse(result)
else:
messages.warning(request, msg)
return redirect(reverse('simpleauth:simple_user_login') + '?next={}'.format(next_url))
return my_login
使用simple_login_required
这个装饰器,要求只能在登录的情况下才能访问该视图。
# 根据订单号获取已选中的物品
@simple_login_required
def api_get_buyer_purchased(request):
username=get_request_username(request)
# ...其他代码省略
下面来测试通过url访问该视图
通过获取到的token来请求认证
>>> import requests
>>> token = user.token
>>> r = requests.get('http://testdomain.starmeow.cn/api/get/purchased/?order_number=201907032124123963', headers={'Authorization': 'Token ' + token})
在认证的装饰器中,可以通过META
中的HTTP_AUTHORIZATION
得到下面的数据
# 权限认证装饰器中的部分代码
http_authorization = request.META.get('HTTP_AUTHORIZATION')
print(http_authorization)
可以看到输出为Token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI0NDQxOTMsImlhdCI6MTU2MjQ0NDAxMywiZGF0YSI6eyJ1c2VybmFtZSI6ImFkbWluIiwiZW1haWwiOiJhZG1pbkBhZG1pbi5jb20ifX0.cMIevl-9OjgRsU26L45NORk8oPaPG2IlfXNzFgXhZ9Q
如何在Django完成数据的返回?
# 判断是否登录的装饰器
def simple_login_required(view_func):
"""
使用方法:
基于类的视图:@method_decorator(simple_login_required, name='dispatch'),添加到类上方
基于函数的视图:@simple_login_required,添加到函数的上方
如果request.is_ajax(),那么返回json字符串,例如:result = {'state_value': 0, 'msg': msg},0表示失败,1表示成功,msg表示提示信息
如果是Token,认证登录,根据抛出的异常,返回不同的json信息
:param view_func:
:return:
"""
def my_login(request, *args, **kwargs):
# print(view_func)
next_url = '{}{}'.format(request._current_scheme_host, request.path)
# print('登陆后返回链接:', next_url)
simple_user_name = request.session.get('session_simple_user_name', '')
print('用户登录:{},链接:{},通过ajax请求:{}'.format(simple_user_name, request.path, request.is_ajax()))
# 【1、从session中获取登录状态】从session中获取session_simple_user_name,能获取到表明是已登录状态
if simple_user_name:
try:
return view_func(request, *args, **kwargs)
except BaseException as e: # 用户被删异常:simpleauth.models.SimpleUser.DoesNotExist: SimpleUser matching query does not exist.
print(e)
msg = '登录异常,请重新登录'
if simple_user_name == 'admin':
msg += str(e) + '访问/simpleauth/主页生成'
# 如果是ajax访问,返回json提示,否则跳转到登录页面
if request.is_ajax():
result = {'state_value': 0, 'msg': msg}
return JsonResponse(result)
else:
messages.warning(request, msg)
return redirect(reverse('simpleauth:simple_user_login') + '?next={}'.format(next_url))
else:
# 如果session认证登录失败,尝试进行token认证登录
http_authorization = request.META.get('HTTP_AUTHORIZATION')
print('获取HTTP_AUTHORIZATION:', http_authorization) # 如果是Token认证,就是:Token code???????code
if http_authorization:
# 尝试token认证登录
method, code = http_authorization.split(' ') # 空格分割字符串
if method.lower() == 'token':
# 【2、从Token判断登录状态】解析Token判断用户
import jwt
from django.conf import settings
try:
# 尝试进行Token(令牌)解密
decoded = jwt.decode(code, settings.SECRET_KEY, algorithm='HS256')
username = decoded.get('data').get('username') # 获取用户名:model中生成Token时放在payload的data中的数据
print('Token中获取用户名:', username)
nickname = decoded.get('data').get('nickname')
except jwt.DecodeError: # 可以在pyjwt源码中看有哪些错误
return JsonResponse({'state_value': 0, 'status_code': 401, 'msg': '解码错误,请退出后重新登陆'})
except jwt.ExpiredSignatureError:
return JsonResponse({'state_value': 0, 'status_code': 401, 'msg': '签名过期,请退出后重新登陆'})
except jwt.InvalidTokenError:
return JsonResponse({'state_value': 0, 'status_code': 401, 'msg': '令牌无效,请退出后重新登陆'})
except Exception:
return JsonResponse({'state_value': 0, 'status_code': 401, 'msg': '其它错误,请退出后重新登陆'})
# 根据查询到的username从用户数据库中查询
try:
simple_user = SimpleUser.objects.get(username=username)
except SimpleUser.DoesNotExist:
return JsonResponse({'state_value': 0, 'status_code': 401, 'msg': '用户不存在,请检查用户名或重新注册'})
# 最后验证成功后返回原函数
return view_func(request, *args, **kwargs)
else:
# 不是token认证
return JsonResponse({'state_value': 0, 'status_code': 401, 'msg': '暂不支持的认证类型'})
else:
# 【3、session和Token均认证失败】如果token认证失败,最后再返回 未登录 提示
msg = '用户未登录'
if request.is_ajax():
result = {'state_value': 0, 'msg': msg}
return JsonResponse(result)
else:
messages.warning(request, msg)
# return redirect(reverse('simpleauth:simple_user_login') + '?next={}'.format(next_url))
return redirect('http://127.0.0.1/simpleauth/login/?next={}'.format(next_url))
return my_login
通过这个装饰器验证是否已登录,如果是,就返回处理该API的请求结果
在装饰器中完成数据返回后,重新测试
>>> r = requests.get('http://testdomain.starmeow.cn/api/get/purchased/?order_number=201907032124123963', headers={'Authorization': 'Token ' + token})
# 如果返回有错误的,可以通过json来查看里面的值
>>> t.json()
能够正常验证后就返回以前的逻辑return view_func(request, *args, **kwargs)
//JQuery ajax全局配置
$.ajaxSetup({
headers: {
"Authorization": "Token " + $.cookie('simpleauth_token')
}
});
//输入框输入后点击提交
function update_selected_nums(update_id) {
let new_nums = $('#new_nums_{update_id}'.replace(/{update_id}/, update_id)).val();
if (isNaN(parseFloat(new_nums)) || parseFloat(new_nums) <= 0) {
alert('数值错误,请重新输入!');
return;
}
$.post("{% host_url 'update_selected_works' host 'group_buy_v0_3' %}",
{"order_number": order_number, 'works_id': update_id, 'new_nums': new_nums, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
function (data) {
if (data.state_value === 0) { //登录装饰器返回
alert(data.msg);
return;
}
if (data.status !== 1) {
//alert(data.result);
console.log(data.result);
} else {
console.log(data.result);
}
get_selected_and_optional(); // 调用页面更新函数
}
);
}
删除对应的cookie值后,显示登录模态框
$('#id-logout-button').click(function () {
//退出登录,置为空,切记要指定路径
$.cookie('simpleauth_username', '', {path: '/'});
$.cookie('simpleauth_nickname', '', {path: '/'});
$.cookie('simpleauth_token', '', {path: '/'});
show_login_modal(); // 退出登录后显示模态框
})