JSON Web Token令牌(JWT)的使用(二)重写自带的用户认证和token流程
重写django自带的用户认证
前文,查看官方文档:https://yiyibooks.cn/xx/Django_1.11.6/topics/auth/index.html
token应用流程:
- 初次登录:用户初次登录,输入用户名密码
- 密码验证:服务器从数据库取出用户名和密码进行验证
- 生成JWT:服务器端验证通过,根据从数据库返回的信息,以及预设规则,生成JWT
- 返还JWT:服务器的HTTP RESPONSE中将JWT返还
- 带JWT的请求:以后客户端发起请求,HTTP REQUEST HEADER中的Authorizatio字段都要有值,为JWT,注意JWT后带一个空格
内置的认证登陆
在进行用户登陆验证的时候,如果是自己写代码,就必须要先查询数据库,看用户输入的用户名是否存在于数据库中;
如果用户存在于数据库中,然后再验证用户输入的密码,这样一来就要自己编写大量的代码。
事实上,Django已经提供了内置的用户认证功能。
为何要自己写认证
有时候我们需要用到邮箱或者手机登录而不只是用户名,所以需要自己重写功能,在utils文件夹里创建一个文件authenticate.py里面写:
# 在当前文件里面,定义的是自定义的认证系统
from django.contrib.auth.backends import ModelBackend
import re
from users.models import Users
import logging
# 这是日志
log = logging.getLogger('users')
class UserPhoneEmailAuthBackend(ModelBackend):
# 当前的类是用来做自定义的认证用户
def authenticate(self, request, username=None, password=None, **kwargs):
'''重写父类的方法
:param request: 请求
:param username: 可能是手机或者email
:param password: 密码
:param kwargs: 其他参数,键值形式
:return: user模型对象
'''
# 1.不论是用户名还是手机还是邮箱先获取到user对象
try:
if re.match(r'^1[3456789][\d]{9}$', username):
user = Users.objects.get(phone=username)
elif re.match(r'^[\w_]{3,15}@[\w]{2,11}.com$', username):
user = Users.objects.get(email=username)
else:
user = Users.objects.get(username=username)
except Users.DoseNotExist:
user = None
log.error('auth验证失败 ', username)
# 拿到user后进行校验
if user is not None and user.check_password(password):
return user
如何使用的jwt自带的验证视图
直接在对应的urls.py里配置即可
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
# 登录验证
url(r'^login/$', obtain_jwt_token),]
在前端登录ajax请求
通过ajax请求发送用户名和密码。验证成功后会得到token。
然后把token存到localStorage,这个是固定存储网页关闭了也存在,在设置.py里可以设置过期时间。
$('#login').click(function () {
// 用户名,密码
var username = $('#username').val();
var userpwd = $('#userpwd').val();
var send_data = {
'username': username,
'password': userpwd
};
send_data = JSON.stringify(send_data);
// 发送请求
$.ajax({
url:'http://127.0.0.1:8000/users/login/',
type:'POST',
data:send_data,
dataType:'json',
contentType:'application/json',
success:function (rec_data) {
// 返回的数据username, userid, token
console.log(rec_data)
// 先清空本地的再进行存储
localStorage.clear();
localStorage.token = rec_data['token'];
// 获取到本地的token
console.log('本地的token', localStorage.token)
},
fail:function () {
alert('登录失败')
}
})
})
重写jwt的返回内容
因为默认使用的返回只有token,我们需要更多地内容比如username和id,当然也可以前端解析base64来实现,这里使用后端重写来返回更多内容。
还是在utils文件夹里的authenticate.py写一个函数:
def my_jwt_response(token, user=None, request=None):
'''
jwt登录验证成功后,自定义的jwt登录返回数据
:param token:
:param user: user对象
:param request:
:return: 返回json类型数据
'''
return {
'token': token,
'username': user.username,
'user_id': user.id
}
然后在自己的设置文件里配置,我的是dev.py,添加这个配置路径也就是第二个键值对
# 设置过期时间和jwt返回值
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=60*60*24),
'JWT_RESPONSE_PAYLOAD_HANDLER': 'shanghuishop.utils.authenticate.my_jwt_response'
}
这时候前端访问即可得到:
Object {user_id: 21, token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1N…ExIn0.6IEeAsMgg_qRCivw0ErmLqvYmlvVF7ckvpxjNmdfUGs", username: "1111"}
JWT如何返回用户信息或者修改信息
serializers.py写一个序列化器
fields里面填需要的值就行,我在models添加了一些值,便于修改
class UserLoginSerClass(serializers.ModelSerializer):
'''
方便
'''
class Meta:
model = Users
fields = ('id', 'username', 'email', 'phone', 'truename', 'gender', 'qq_num')
view.py设置指定模型(重要)
这个导的是RetrieveUpdateAPIView,可以实现get和put请求
from rest_framework.generics import *
class LoginUserInfoView(RetrieveUpdateAPIView):
serializer_class = UserLoginSerClass
# 指定单个模型
def get_object(self):
# print('展示单个模型', self.request.user)
return self.request.user
js根据JWT的值发送get请求获取信息
这是用户信息界面,get请求需要添加请求头设置:
- ‘Authorization’: 'JWT ’ + token
来访问,如果有效就能取到用户信息,并且设置了当前页面的标签值
$.ajax({
url: 'http://127.0.0.1:8000/users/userinfo/',
method: 'GET',
headers: {
'Authorization': 'JWT ' + token
},
// 成功时调用
success:function (data) {
console.log(data);
// 设置显示的对应内容
$('#username').val(data['username']);
$('#phone').val(data['phone']);
$('#email').val(data['email'])
},
error:function (err_data) {
console.log(err_data);
alert('未登录,请跳转到登录页面');
// 如果提示是401则是JWT过期跳转登录页面
if (err_data['status'] == 401){
location.href = 'http://127.0.0.1:8080/login.html'
}
}
})
js里根据JWT的值发送PUT请求
其实和GET是一样的,要注意添加请求头即可
$('#change_personal_data').click(function () {
// 修改按钮,使可以输入
$('#truename').attr('disabled', false).css('border', 'solid 1px red');
// $('#gender').attr('disabled', false).css('border', 'solid 1px red');
$('#gender').css('display', 'none');
$('.add_sex').css('display', 'block');
$('#qq_number').attr('disabled', false).css('border', 'solid 1px red');
});
$('#change_confirm').click(function () {
// 确认修改
var username = $('#username').val();
var phone = $('#phone').val();
var email = $('#email').val();
var truename = $('#truename').val();
var qq_num = $('#qq_number').val();
var gender = $("input[name='sex']:checked").val();
if (gender==0){
gender = 'male'
}else if(gender==1){
gender = 'female'
}
var send_data = {
'username': username,
'phone': phone,
'email': email,
'truename': truename,
'qq_num': qq_num,
'gender': gender
};
send_data = JSON.stringify(send_data);
console.log(send_data);
// 发送修改用户信息请求
$.ajax({
url: 'http://127.0.0.1:8000/users/userinfo/',
type: 'PUT',
headers:{
'Authorization': 'JWT ' + token
},
data: send_data,
dataType:'json',
contentType:'application/json',
success: function (data) {
console.log(data);
$('#truename').attr('disabled', true).css('border', 'solid 0px red');
// $('#gender').attr('disabled', false).css('border', 'solid 1px red');
$('#gender').css('display', 'block').text(data['gender']);
$('.add_sex').css('display', 'none');
$('#qq_number').attr('disabled', true).css('border', 'solid 0px red');
},
error:function (data) {
alert('failed');
console.log(data)
}
})
})
上一篇: 464. 整数排序 II(排序算法)
下一篇: python中md5加密的实现