1.cookie工作流程
cookie信息是以文本方式存放在客户端的,所以容易引起一些安全隐患,所以不要把隐秘信息以cookie方式保存。
2.session的工作流程
客户端访问服务器,服务器调用request.getSession()方法,产生session对象,用于跟踪用户的
状态,同时,给session对象分配一个唯一标识sessionId。为了管理session对象,以sessionId为键,以session对象为值,封装成Map集合。产生响应时,将sessionId以cookie方式发送给客户端,存放在客户端浏览器的缓存中。当客户端再次请求服务器,会将sessionId以cookie请求头的方式发送给服务器,服务器得到sessionId后,从Map集合中,得到session对象,从而跟踪状态。
3.cookie和session的区别
cookie把键和值都存在浏览器端
session把sessionid存在浏览器端,而真正的数据存放在服务器端
4.jwt
头部--申明自己的形式,加密方式
载荷--存放数据
签名--验证(由头部+载荷+头部里的加密方式+随机字符串构成)
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJ1c2VyX2lkIjo1LCJ1c2VybmFtZSI6IjEwXHU1YzBmXHU3ZWEyIiwiZXhwIjoxNjMyNDc4NDYxLCJlbWFpbCI6IiJ9.
BNhLtmrSMbwo3PMm9ndwdofqaXOKJ8VquG9O0MN79z8
token值有三部分组成中间用点分开,上面就是例子
jwt定义
Json web token(JWT)是为了网络应用环境间传递声明而执行的一种基于JSON的开发标准(RFC 7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
什么情况下使用JWT比较适合? 授权:这是最常见的使用场景,解决单点登录问题。因为JWT使用起来轻便,开销小,服务端不用记录用户状态信息(无状态),所以使用比较广泛;
信息交换:JWT是在各个服务之间安全传输信息的好方法。因为JWT可以签名,例如,使用公钥/私钥对儿 - 可以确定请求方是合法的。此外,由于使用标头和有效负载计算签名,还可以验证内容是否未被篡改。
跨域认证问题
互联网服务离不开用户认证。一般流程是下面这样:
1、用户向服务器发送用户名和密码。
2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
3、服务器向用户返回一个 session_id,写入用户的 Cookie。
4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。
这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。
举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?
一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。 另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。
jwt的原理
JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。
{ "姓名": "张三",
"角色": "管理员",
"到期时间": "2018年7月1日0点0分" }
以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。
服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。
区别 (1) session 存储在服务端占用服务器资源,而 JWT 存储在客户端
(2) session 存储在 Cookie 中,存在伪造跨站请求伪造攻击的风险
(3) session 只存在一台服务器上,那么下次请求就必须请求这台服务器,不利于分布式应用
(4) 存储在客户端的 JWT 比存储在服务端的 session 更具有扩展性
jwt认证流程图
1,浏览器发起请求登陆,携带用户名和密码;
2,服务端验证身份,根据算法,将用户标识符打包生成 token,
3,服务器返回JWT信息给浏览器,JWT不包含敏感信息;
4,浏览器发起请求获取用户资料,把刚刚拿到的 token一起发送给服务器;
5,服务器发现数据中有 token,验明正身;
6,服务器返回该用户的用户资料;
jwt的优缺点
1、JWT默认不加密,但可以加密。生成原始令牌后,可以使用改令牌再次对其进行加密。
2、当JWT未加密方法是,一些私密数据无法通过JWT传输。
3、JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。 4、JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。
5、JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。
6、为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。
(1)安装
pip install djangorestframework-jwt
(2)在setting.py配置
AUTH_USER_MODEL='jwttwo.Jwtone'
(jwttwo是子项目名,Jwtone是models里的表名)
REST_FRAMEWORK = {
# 身份认证
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
# #全局配置JWT验证设置
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
}
import datetime
JWT_AUTH = {
'JWT_AUTH_HEADER_PREFIX': 'JWT',
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_RESPONSE_PAYLOAD_HANDLER':
'jwttwo.views.jwt_response_payload_handler', # 重新login登录返回函数
}
(jwttwo是子项目名)
(datetime.timedelta(days=1)是过期时间)
(3)在models中
from django.db import models from django.contrib.auth.models import AbstractUser class Jwtone(AbstractUser): nickname=models.CharField(max_length=200) age=models.IntegerField() home=models.CharField(max_length=200,default='河南') class Meta: db_table='jwtone'
(注意括号里是AbstractUser,不是models.Model)
(4)在views中
from django.contrib.auth.hashers import make_password #密码加密 from django.shortcuts import render # Create your views here. from rest_framework.response import Response from rest_framework_jwt.settings import api_settings from rest_framework.views import APIView from jwttwo.serializer import Juserser import copy from jwttwo.models import Jwtone from rest_framework.permissions import IsAuthenticated,AllowAny # JWT 注册接口 class Juser(APIView): # 获取数据,密码加密并存到数据库 def post(self,request): data=request.data data3=copy.deepcopy(data) data1=data.get('password') data2=make_password(data1) data3['password']=data2 dataone=Juserser(data=data3) dataone.is_valid() dataone.save() # 生成token值 user=Jwtone.objects.get(username=data3['username']) jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) print(token) # 将token和数据库信息进行返回 response_data=dataone.data response_data['JWTToken']=token return Response({'code':200,'data':response_data}) # 登录 def jwt_response_payload_handler(token, user=None, request=None): ''' :param token: jwt生成的token值 :param user: User对象 :param request: 请求 ''' return { 'token': token, 'username': user.username, 'userid': user.id, 'nickname':user.nickname } # 用来测试 class Test(APIView): permission_classes = [AllowAny] # 对全局都有效 def get(self,request): return Response({'message':'可以访问'}) class Test2(APIView): permission_classes = [IsAuthenticated] # 对局部有效 def get(self,request): return Response({'message':'没登录,不可访问'}) (5)在urls中 from django.urls import path from jwttwo.views import Juser,Test,Test2 from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ path('Juser/',Juser.as_view()), # 注册的路由 path('Test/',Test.as_view()), # 用来测试的路由 path('Test2/',Test2.as_view()), # 用来测试的路由 path('login/',obtain_jwt_token), # 登录的路由 ]