文章目录
- 学习大纲
- 前言
- 用户功能设计与实现
- 一、w
- 1、路由配置
- 2、登录代码
- 二、认证接口
- 1、Django
- 2、中间件技术 Middleware
- 3、装饰器*
- 4、Jwt过期问题
前言
用户功能设计与实现
- 提供用户注册处理
- 提供用户登录处理
- 提供路由配置
一、w
- 接收用户通过POST方法提交的登录信息,提交的数据是JSON格式数据
{ "password":"abc", "email":"wayne@magedu.com" }
- 从user表中email找出匹配的一条记录,验证密码是否正确。
- 验证通过说明是合法用户登录,显示欢迎页面。
- 验证失败返回错误状态码,例如4xx
- 整个过程都采用A JAX异步过程,用户提交JSON数据,服务端获取数据后处理,返回JSON。
- URL:/user/login
- METHOD:POST
1、路由配置
-
from django.conf.urls import url from .views import reg, login urlpatterns = [ url(r'^reg$', reg), url(r'^login$', login), ]
2、登录代码
-
def login(request:HttpRequest): payload = simplejson.loads(request.body) # # 获取登录信息数据 try: email = payload['email'] user = User.objects.filter(email=email).get() if bcrypt.checkpw(payload['password'].encode(), user.password.encode()): # 验证通过 token = get_token(user.id) print(token) res = JsonResponse({ 'user':{ 'user_id':user.id, 'name':user.name, 'email':user.email }, 'token':token }) res.set_cookie('Jwt', res) return res else: return HttpResponseBadRequest() except Exception as e: # 有任何异常,都返回 logging.info(e) return HttpResponseBadRequest() # 这里返回实例,这不是异常类
二、认证接口
- 如何获取浏览器提交的token信息?
- 使用Header中的Authorization
通过这个header增加token信息。
通过header发送数据,所有方法可以是Post、Get - 自定义header
JWT来发送token
我们选择第二种方式
- 使用Header中的Authorization
- 认证
- 基本上所有的业务都需要认证用户的信息。
- 在这里比较时间戳,如果过期,就直接抛未认证401,客户端收到后就该直接跳转到登录页。
- 如果没有提交user id,就直接重新登录。如果用户查到了,填充user对象。
- request -> 时间戳比较 -> user id 比较 -> 向后执行
1、Django
- django.contrib.auth中提供了许多方法,这里主要介绍其中的三个:
- 1、authenticate( ** credentials)
- 提供了用户认证,即验证用户名以及密码是否正确
- user = authenticate(username=‘someone’,password=‘somepassword’)
- 2、login(HttpRequest, user, backend=None)
- 该函数接受一个HttpRequest对象,以及一个认证了的User对象
- 此函数使用django的session框架给某个已认证的用户附加上session id等信息。
- 3、logout(request)
- 注销用户
该函数接受一个HttpRequest对象,无返回值。
当调用该函数时,当前请求的session信息会全部清除
该用户即使没有登录,使用该函数也不会报错
- 注销用户
- 1、authenticate( ** credentials)
- 还提供了一个装饰器来判断是否登录django.contrib.auth.decorators.login_required
- 本项目使用了无session机制,且用户信息自己建表管理,所以,认证需要自己实现。
2、中间件技术 Middleware
- 官方定义,在Django的request和response处理过程中,由框架提供的hook钩子
- 中间件技术在1.10后发生了改变,我们当前使用1.11版本,可以使用新的方式定义。
- 参看 https://docs.djangoproject.com/en/1.11/topics/http/middleware/#writing-your-own-middleware
# class BlogAuthMiddleware(object): '''自定义认证中间件''' def __init__(self, get_response): self.get_response = get_response # 初始化执行一次 def __call__(self, request): # 视图函数之前执行 # 认证 print(type(request), '+++++++++++++++++') print(request.GET) print(request.POST) print(request.body) # json数据 print('-'*30) response = self.get_response(request) # 视图函数之后执行 return response # 要在setting的MIDDLEWARE中注册
- 但是,这样所有的请求和响应都拦截,我们还得判断是不是访问的想要拦截的view函数,所以,考虑其他方法。
3、装饰器*
- 在需要认证的view函数上增强认证功能,写一个装饰器函数。谁需要认证,就在这个view函数上应用这个装饰器。
AUTH_EXPIRE = 8 * 60 * 60 # 8小时过期 def authenticate(view): def wrapper(request:HttpRequest): # 自定义header jwt token = request.META.get('HTTP_JWT') # 会被加前缀HTTP_且全大写 if not token: # None没有拿到,认证失败 return HttpResponse(status=401) try: # 解码 payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256']) print(payload) except: return HttpResponse(status=401) # 验证过期时间 current = datetime.datetime.now().timestamp() if (current - payload.get('timestamp', 0)) > AUTH_EXPIRE: return HttpResponse(status=401) print('-' * 30) # 到这个位置,说明该用户是合法的用户了 try: user_id = payload.get('user_id') user = User.objects.filter(pk=user_id).get() request.user = user print('-' * 30) except Exception as e: logging.info(e) return HttpResponse(status=401) ret = view(request) # 调用视图函数 # 特别注意view调用的时候,里面也有返回异常 return ret return wrapper @authenticate # 很自由的应用在需要认证的view函数上 def test(request:HttpRequest): return HttpResponse(b'test jwt')
4、Jwt过期问题
- pyjwt支持过期设定,在decode的时候,如果过期,则抛出异常。需要在payload中增加claim exp。exp要求是一个整数int的时间戳。
import jwt import datetime import threading event = threading.Event() key = 'magedu' # 在jwt的payload中增加exp claim data = jwt.encode({'name':'tom', 'age':20, 'exp': int(datetime.datetime.now().timestamp())+10}, key) print(jwt.get_unverified_header(data)) try: while not event.wait(1): print(jwt.decode(data, key)) # 过期,校验就会抛出异常 print(datetime.datetime.now().timestamp()) except jwt.ExpiredSignatureError as e: print(e)
- 重写Jwt过期
- 测试时注意重新login一下,否则测试的token还是之前的,{‘user_id’: 6, ‘timestamp’: 1617320236},没有key为exp的键值对,就不会触发过期时间,正确格式应该是:{‘user_id’: 7, ‘exp’: 1617322461}
AUTH_EXPIRE = 8 * 60 * 60 # 8小时过期 def get_token(user_id): '生成token' return jwt.encode({ # 增加时间戳,判断是否重发token或重新登录 'user_id':user_id, 'exp': int(datetime.datetime.now().timestamp()) + AUTH_EXPIRE # 要取整 }, settings.SECRET_KEY, 'HS256').decode() # 字符串 def authenticate(view): def wrapper(request:HttpRequest): # 自定义header jwt token = request.META.get('HTTP_JWT') # 会被加前缀HTTP_且全大写 if not token: # None没有拿到,认证失败 return HttpResponse(status=401) try: # 解码 payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256']) print(payload) except Exception as e: print(e, '~~~~~~~~~~~~~~~~~') return HttpResponse(status=401) # 验证过期时间 # current = datetime.datetime.now().timestamp() # if (current - payload.get('timestamp', 0)) > AUTH_EXPIRE: # return HttpResponse(status=401) # 到这个位置,说明该用户是合法的用户了 try: user_id = payload.get('user_id') user = User.objects.filter(pk=user_id).get() request.user = user print('-' * 30) except Exception as e: logging.info(e) return HttpResponse(status=401) ret = view(request) # 调用视图函数 # 特别注意view调用的时候,里面也有返回异常 return ret return wrapper @authenticate # 很自由的应用在需要认证的view函数上 def test(request:HttpRequest): return HttpResponse(b'test jwt')
#段落从此开始
背景色yellow |