更多内容请点击 我的博客 查看,欢迎来访。

官方文档: https://pyjwt.readthedocs.io/en/latest/installation.html

JWT定义

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”)]

  1. 应用(或者客户端)想授权服务器请求授权。例如,如果用授权码流程的话,就是/oauth/authorize
  2. 当授权被许可以后,授权服务器返回一个access token给应用
  3. 应用使用access token访问受保护的资源(比如:API)

JWT特点

  • 体积小,因而传输速度快
  • 传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输
  • 严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。
  • 支持跨域验证,可以应用于单点登录。
  • jwt 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
  • jwt在不加密的情况下,不能将涉密信息写入jwt

与session共享机制的对比

  1. 代码侵入,在每个服务上必须有存取session鉴权判断的代码;
  2. 不安全,虽然redis等可以很方便的进行分布式部署,假若session服务器挂掉,系统便完全无法使用,不满足分布式系统中的高可用;
  3. 内存无法控制,若用户访问量激增极可能冲破内存,对内存要求高;

与之相反这些正是JWT的优势所在:

  1. 无代码侵入 ,只需要配置一个认证服务,其他服务可以无需关注权限,并可以提高到API网关进行权限拦截;
  2. JWT不依赖session,对内存无要求,大量的访问也从容应对,不易产生硬件瓶颈;
  3. 可以在JWT中存储角色等信息,减少数据库查询或无需查询;

安装pyjwt

pip install pyjwt

测试jwt生成和解码

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'

三个参数:

  • 第一个是payload,是一个Json对象,主要用来存放有效的信息,例如用户名,过期时间等等所有你想要传递的信息;
  • 第二个是密钥,这里是读取配置文件中的SECRET_KEY配置变量,这个秘钥主要用在下文Signature签名中,服务端用来校验Token合法性,这个秘钥只有服务端知道,不能泄露;
  • 第三个是生成Token的算法。

payload,这是认证的依据,也是后续解析token后定位用户的依据,需要包含特定用户的特定信息,如下面示例中注册了data声明,data声明中用户的usernamenickname等,在“用户鉴权”方法中,解析token完成后要利用这个username来查找并返回用户信息给用户。这里的data声明是我们自己加的,pyjwt内置注册了以下几个声明(建议但不强制使用):

  • “exp”: 过期时间,是按当地时间确定,所以设置时要使用utc时间。
  • “nbf”: 表示当前时间在nbf里的时间之前,则Token不被接受
  • “iss”: token签发者
  • “aud”: 接收者
  • “iat”: 发行时间

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<Z\x97\x17\xba'

验证Token

# 解密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')

Django使用Token认证

说明:A应用为www,B应用为testdomain

子域名 testdomain.starmeow.cn 想要请求 www.starmeow.cn/login/ 认证用于登录,涉及到跨域请求,可以把 testdomain.starmeow.cn 当作一个独立的应用。

前端Ajax Post登录获取Token

渲染一个模板页面,加载jquery登录

这里直接修改的普通视图,先判断session,当用户在这个域没登陆时(因为没在session没在这个域中对这个键赋值,一般是没有值的)。
再来尝试获取Token,由于前端通过AuthorizationToken请求会传过来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???????codeif 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 jwtfrom django.conf import settingsdecoded = jwt.decode(code, settings.SECRET_KEY, algorithm='HS256')username = decoded.get('data').get('username')  # 获取用户名:model中生成Token时放在payload的data中的数据except:passreturn usernameclass 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 responseprint('获取到用户名:', request_username)# 其他代码

jquery中进行判断cookie值

如果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();}

模态框代码如下

<!-- 模态框(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"><!--button type="button" class="close" data-dismiss="modal" aria-hidden="false">×</button--></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><!-- /.modal-content --></div><!-- /.modal-dialog -->
</div><!-- /.modal -->

[外链图片转存失败(img-Be8ijwak-1562813971812)(https://blog.starmeow.cn/media/blog/images/2019/07/BLOG_20190711_105256_93.png “博客图集BLOG_20190711_105256_93.png”)]

该模态框限制不可退出。

jquery登录验证,请求另一域名A应用登录

!jquery登录post(一)

点击登录按钮,获取表单,进行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)}},})
});

A应用:后端接收该表单进行验证

!模型中生成用户Token方法(二)

定义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的tokendef _generate_jwt_token(self):import datetimeimport jwtfrom django.conf import settingsfrom django.utils import timezonepayload = {'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类型的tokentoken = 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)

命令行测试生成用户的Token

在model中编写完生成Token的方法后,需要重启Django Console才可进行下面的测试,否则obj.token无法使用

>>> from simpleauth.models import SimpleUser
>>> user = SimpleUser.objects.get(username='admin')user.token
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI0NDM5ODcsImlhdCI6MTU2MjQ0MzgwNywiZGF0YSI6eyJ1c2VybmFtZSI6ImFkbWluIiwiZW1haWwiOiJhZG1pbkBhZG1pbi5jb20ifX0.Ry5BHjIdCuqhAyC-Wo558q4JQI9Qaf4NgTIm034cumg'

!A应用登录验证帐密(三)

关闭csrf验证

from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exemptclass 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')  # 获取隐藏表单的nextusername = 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传给前端tokenreturn 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('<h3>用户登录成功。</h3>由于没有制定下一跳地址,请重新访问之前的页面')# 用户未登录状态,添加到sessionelse:request.session['session_simple_user_name'] = usernamerequest.session['session_simple_nick_name'] = user.nicknameif 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应用跨域解决方案

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',
)

B应用页面jquery获取数据

A应用登录认证装饰器代码判定位置

修改以前的登录装饰器,增加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认证登录passelse:# 如果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

B应用视图中加上该装饰器验证登录(五)

使用simple_login_required这个装饰器,要求只能在登录的情况下才能访问该视图。

# 根据订单号获取已选中的物品
@simple_login_required
def api_get_buyer_purchased(request):username=get_request_username(request)# ...其他代码省略

下面来测试通过url访问该视图

命令行测试通过Token请求B应用的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完成数据的返回?

A应用登录认证装饰器判断返回数据(四)

# 判断是否登录的装饰器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???????codeif http_authorization:# 尝试token认证登录method, code = http_authorization.split(' ')  # 空格分割字符串if method.lower() == 'token':# 【2、从Token判断登录状态】解析Token判断用户import jwtfrom django.conf import settingstry:# 尝试进行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的请求结果

命令行通过Token请求返回

在装饰器中完成数据返回后,重新测试

>>> 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全局带token请求(六)

//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

删除对应的cookie值后,显示登录模态框

$('#id-logout-button').click(function () {//退出登录,置为空,切记要指定路径$.cookie('simpleauth_username', '', {path: '/'});$.cookie('simpleauth_nickname', '', {path: '/'});$.cookie('simpleauth_token', '', {path: '/'});show_login_modal(); // 退出登录后显示模态框
})

Django使用pyjwt实现Token跨域认证登录过程实践相关推荐

  1. JSON Web Token(缩写 JWT) 目前最流行、最常见的跨域认证解决方案,前端后端都需要会使用的东西

    JSON Web Token(缩写 JWT)是目前最流行,也是最常见的跨域认证解决方案.无论是咱们后端小伙伴,还是前端小伙伴对都是需要了解. 本文介绍它的原理.使用场景.用法. 关于封面:这个冬天你过 ...

  2. JSON Web Token(缩写 JWT) 目前最流行的跨域认证解决方案

    JSON Web Token(缩写 JWT) 目前最流行的跨域认证解决方案 参考文章: (1)JSON Web Token(缩写 JWT) 目前最流行的跨域认证解决方案 (2)https://www. ...

  3. 手绘10张图,把CSRF跨域攻击、JWT跨域认证说得明明白白的

    作者 | 写代码的明哥 来源 | Python编程时光 这篇文章本应该是属于 HTTP 里的一部分内容,但是我看内容也挺多的,就单独划分一篇文章来讲下. 什么是跨域请求 要明白什么叫跨域请求,首先得知 ...

  4. 干掉Session?这个跨域认证解决方案真的优雅

    用户登录认证是 Web 应用中非常常见的一个业务,一般的流程是这样的: 客户端向服务器端发送用户名和密码 服务器端验证通过后,在当前会话(session)中保存相关数据,比如说登录时间.登录 IP 等 ...

  5. SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题

    SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题 参考文章: (1)SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题 (2)https://www. ...

  6. 解决Django Rest Framework中的跨域问题

    解决Django Rest Framework中的跨域问题 参考文章: (1)解决Django Rest Framework中的跨域问题 (2)https://www.cnblogs.com/qicu ...

  7. 跨域认证--使用Passport

    打算实现跨域认证.看了一些资料,决定使用Passport实现. 所谓的passport方式,就是将用户验证,用户状态管理独立出来成为一个单独的站点. 相关站点都通过Passport进行用户验证和用户状 ...

  8. Cookie同域,跨域单点登录

    Cookie 同域单点登录最近在做一个单点登录的系统整合项目,之前我们使用控件实现单点登录(以后可以介绍一下).但现在为了满足客户需求,在不使用控件情况下实现单点登录,先来介绍一下单点登录.单点登录: ...

  9. JavaWeb跨域单点登录

    B/S架构前后端分离项目实现跨域单点登录方案设计 项目需求: 1.前后端分离项目,由后端实现会话管理,同时校验用户权限: 2.多个项目单点登录,允许跨域访问: 3.预留CS架构客户端点击按钮打开浏览器 ...

  10. C#开发中Windows域认证登录2(扩展吉日嘎拉GPM系统)

    为什么80%的码农都做不了架构师?>>>    原文地址:http://www.cuiwenyuan.com/shanghai/post/Windows-AD-Logon-Inter ...

最新文章

  1. 使用hibernate与mysql时数据不能插入的原因及解决办法
  2. 并发编程的艺术:第二章
  3. 快搜浏览器_郑秀晶因腿粗再上热搜:怎么减肥才能不反弹?
  4. 第二十一期:老大难的GC原理及调优,这全说清楚了
  5. leetcode - 739. 每日温度
  6. html lineheight div,html – Chrome上的文本输入:line-height似乎有最小值
  7. or计算机二级,计算机二级VF历年上机试题or答案
  8. 智能优化算法:基于梯度的优化算法-附代码
  9. 保护水资源公益网站html,保护水资源公益广告词
  10. 基于php的地铁查询系统,动手构建地铁关系网,实现最短路径查询
  11. Android 判断通知栏是否打开及前往设置页面
  12. Hadoop MapReduce Splits 切片源码分析及切片机制
  13. 3dsmax烘焙模型
  14. 在Linux Mint上玩转蓝牙机械键盘
  15. 一个“后浪”的狂欢,一群中年人的孤单!
  16. 最长递增子序列问题 nyoj 17单调递增最长子序列 nyoj 79拦截导弹
  17. Vue前端页面跳转,登录成功跳转页面
  18. X线DR医学图像 --- DR医用滤线栅及摩尔纹详解 (一) 滤线栅的原理
  19. 如何将多个excel表格合并成一个_多个PDF如何合并成一个?就用这个PDF在线工具!...
  20. 嘉立创 指定位置放编号的规则

热门文章

  1. 融云观察:壳壳语音新玩法,深挖语音社交市场
  2. 运营必备 - CPA、CPS、CPC、CPM推广是什么意思?
  3. 新手小白也看得懂的电脑win10安装教程
  4. 计算机cmp代表什么意思,CMP是什么
  5. html 图片导出excel,html静态表格导出到excel
  6. 【基础整理】Mapping representation 机器人所用地图种类及相关介绍
  7. 深度学习(6): RNN
  8. WOS/EI/SCOPUS 三大文献检索数据库区别你了解吗?
  9. 怎么清除DNS缓存?
  10. mysql 打开sql日志,记录所有sql