基于Token的身份验证

在实现登录功能的时候,正常的B/S应用都会使用cookie+session的方式来做身份验证,后台直接向cookie中写数据,但是由于移动端的存在,移动端是没有cookie机制的,所以使用token可以实现移动端和客户端的token通信。

验证流程

整个基于Token的验证流程如下:

  1. 客户端使用用户名跟密码请求登录
  2. 服务器收到请求,去验证用户名和密码
  3. 验证成功后,服务端会签发一个Token,再把这个Token发送到客户端
  4. 客户端收到的Token以后可以把它存储起来,比如放在Cookie或LocalStorage里
  5. 客户端每次向服务器发送其他请求的时候都要带着服务器签发的Token
  6. 服务器收到请求,去验证客户端请求里面带着的Token,如果验证成功,就像客户端返回请求的数据

JWT

构造Token的方法挺多的,可以说只要是客户端和服务器端约定好了格式,是想怎么写就怎么写的,然而还有一些标准写法,例如JWT读作/jot/,表示:JSON Web Tokens.

JWT标准的Token有三个部分:

  • header
  • payload
  • signature

    三个部分会用点分割开,并且都会使用Base64编码,所以真正的Token看起来像这样
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

Header

header部分主要是两部分内容,一个是Token的类型,另一个是使用的算法,比如下面的类型就是JWT,使用的算法是HS256:

{
"typ": "JWT",
"alg": "HS256"
}

上面的内容要用 Base64 的形式编码一下,所以就变成这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。下面是标准字段:

  • iss:Issuer,发行者
  • sub:Subject,主题
  • aud:Audience,观众
  • exp:Expiration time,过期时间
  • nbf:Not before
  • iat:Issued at,发行时间
  • jti:JWT ID

Signature

JWT的最后一部分是Signature,这部分相当于前两段的摘要,用来防止其他人来篡改Token中的信息,在处理时可以首先将前两段生成的内容使用Base64生成一下再加盐然后利用MD5等摘要算法在生成一遍

服务端生成Token

在服务端生成Token的时候,需要解决两个问题

  1. 使用什么加密算法
  2. Token如何存储

加密算法

这里的加密算法并不是MD5,SHA1这样的哈希算法,因为这种算法是无法解密的,只能用来生成摘要,在Django中内置了一个加密前面模块django.core.signing模块,可以用来加密和解密任何数据,使用签名模块的dumps和load函数来实现

示例:

from django.core import signing
value = signing.dumps({"foo":"bar"})
src = signing.loads(value)
print(value)
print(src)

结果:

eyJmb28iOiJiYXIifQ:1NMg1b:zGcDE4-TCkaeGzLeW9UQwZesciI
{‘foo’: ‘bar’}

Token如何存储

用什么存储

在服务器中Token可以存储在内存中,因为本质是字符串,所以并不会占用很大的内存空间,如果是分布式的存储可以将所有的token信息分段存储在不同的服务器中,也可以存储在数据库中,在Django中提供了缓存类,可以用来存储Token,Django的缓存可以结合Redis来使用,可以借助django-redis来实现

安装

pip install django-redis

配置

为了使用django-redis,需要将django cache setting修改,修改settings.py,默认是没有Cache的配置信息的,在其中添加:

CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}

当然,需要你首先安装了redis

cache使用

cache使用的时候基本可以使用set和get方法来进行存/取数据

>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'

如何存

由于redis是使用k-v模式来进行存储数据的,我们可以使用用户名作为key,而token信息作为value,相较于直接使用token作为key的方式,好处是我们可以使用更少的空间实现一些功能,例如当用户修改了密码或点击注销之后,它的token可以直接失效,直接将该用户名所对应的数据删除就好了,或者用户在一次登录成功后,又一次请求了登录接口,我们可以很简单的更新该用户的token信息,而这样存储所依赖于我们的token可以进行解密,如果你是直接生成了一串无法解密的数据作为token,不能使用用户名作为token了

code

import time
from django.core import signing
import hashlib
from django.core.cache import cache HEADER = {'typ': 'JWP', 'alg': 'default'}
KEY = 'CHEN_FENG_YAO'
SALT = 'www.lanou3g.com'
TIME_OUT = 30 * 60 # 30min def encrypt(obj):
"""加密"""
value = signing.dumps(obj, key=KEY, salt=SALT)
value = signing.b64_encode(value.encode()).decode()
return value def decrypt(src):
"""解密"""
src = signing.b64_decode(src.encode()).decode()
raw = signing.loads(src, key=KEY, salt=SALT)
print(type(raw))
return raw def create_token(username):
"""生成token信息"""
# 1. 加密头信息
header = encrypt(HEADER)
# 2. 构造Payload
payload = {"username": username, "iat": time.time()}
payload = encrypt(payload)
# 3. 生成签名
md5 = hashlib.md5()
md5.update(("%s.%s" % (header, payload)).encode())
signature = md5.hexdigest()
token = "%s.%s.%s" % (header, payload, signature)
# 存储到缓存中
cache.set(username, token, TIME_OUT)
return token def get_payload(token):
payload = str(token).split('.')[1]
payload = decrypt(payload)
return payload # 通过token获取用户名
def get_username(token):
payload = get_payload(token)
return payload['username']
pass def check_token(token):
username = get_username(token)
last_token = cache.get(username)
if last_token:
return last_token == token
return False

非常详细的Django使用Token(转)的更多相关文章

  1. django CSRF token missing or incorrect

    django 异步请求时提示403 按照一般情况权限问题,python文件没有问题,仔细看了下response里有一句 CSRF token missing or incorrect.这个肯定是因为安 ...

  2. Django JWT Token RestfulAPI用户认证

    一般情况下我们Django默认的用户系统是满足不了我们的需求的,那么我们会对他做一定的扩展 创建用户项目 python manage.py startapp users 添加项目apps INSTAL ...

  3. django drf Token验证

    https://www.django-rest-framework.org/api-guide/authentication/#basicauthentication 1.INSTALLED_APPS ...

  4. Django restframework Token拥有不过期的认证

    REST框架中的Token认证不像Session认证一样,它是没有办法设置过期时间的,但是有时我们需要对Token做过期验证,比如说用户在A设备登陆之后获取一个Token,如果用户在没有清空浏览器缓存 ...

  5. Django (七) token&静态文件&媒体文件

    token&静态文件&媒体文件 1. token 1. 会话技术 2. 服务端会话技术 3. 它实际上就是手动实现的session 4. 实现token 4.1 在models.py中 ...

  6. jquery 设置django全局token

    通过JQUEYR中的ajaxSetup,来设置django中的token,即不需要再每次都去引用: 第一步: 先django中的html中设置 {%  csrf_token %} 第二步: 新一个js ...

  7. 很详细的Django入门详解

    Django 是用Python开发的一个免费开源的Web框架,可以用于快速搭建高性能,优雅的网站!采用了MVC的框架模式,即模型M,视图V和控制器C,也可以称为MVT模式,模型M,视图V,模板T.在学 ...

  8. django csrf token添加

    #views.py from django.shortcuts import render_to_response, RequestContext from django.views.decorato ...

  9. Django 序列化-token

    幂等性 幂等性:多次操作的结果和一次操作的结果是一样的 ,put请求是幂等的 post请求不是幂等的 序列化组件 全局和局部钩子函数 异常信息抛出过程 认证 路由里的,login.as_view() ...

随机推荐

  1. stixel_world+Multi_stioxel_world+semantic_stixel_world知识拓展

    Semantic_Stixel_World 学习笔记 因项目方向更改,该研究暂停, 转为opengl等3D渲染. Author: Ian 星期四, 20. 六月 2019 06:11下午 最近看网络上 ...

  2. 细说Ansible主机清单inventory

    Ansible是一个系列文章,我会尽量以通俗易懂.诙谐幽默的总结方式给大家呈现这些枯燥的知识点,让学习变的有趣一些. Ansible系列博文直达链接:Ansible入门系列 前言 关于Ansible是 ...

  3. C语言入门4-运算符和表达式

    一.     分类 C语言一共有34种运算符,10种运算类型,本节我们要掌握的有( 7 种) 算术运算符(+.-.*./.%). 关系运算符(>.>=.==.!=.<.<=). ...

  4. NPOI 日期类型的判断

    NPOI目前我用到有两套类,一套是为了读写XLS:一套是读写XLSX 在读取文件时大都会判断单元格类型,方式大同小异,只有日期类型不同. 默认日期类型的单元格在NPOI都认为是数值类型(CellTyp ...

  5. 【MySQL】(三)文件

    本篇文章分析构成MySQL数据库和InnoDB存储引擎表的各种累类型文件.这些文件有以下这些. 参数文件:告诉MySQL实例启动时在哪里可以找到数据库文件,并且指定某些初始化参数,这些参数定义了某种内 ...

  6. 【Java中级】(一)面向对象的特性与八种基本类型

    1.1.Java 基本数据类型: Java提供了8种基本类型.六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型. byte.short.int.long.float.double. ...

  7. typedef int a[10];怎么解释?

    typedef int a[10]; a b[10]; 为什么分配400个字节的空间? int a[10];为什么分配了40个字节的空间? 问题:应该怎么解释typedef的这种行为呢?而如果换成是# ...

  8. 图解Redis之数据结构篇——跳跃表

    前言       跳跃表是一种有序的数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的.这么说,我们可能很难理解,我们可以先回忆一下链表. 一.复习跳跃表 1.1 什么 ...

  9. mysql 不同版本下 group by 组内排序的差异

    最近发现网上找的 group by 组内排序语句在不同的mysql版本中结果不一样.   建表语句:   SET FOREIGN_KEY_CHECKS=0;   -- ---------------- ...

  10. 异常 Java oop

    1.捕获异常 try——执行可能产生异常的代码 catch——捕获异常 finally——无论是否发生异常,代码总能执行 2.声明异常 throws——声明方法可能要要抛出的各种异常 3.抛出异常—— ...