关于Http协议 : Http协议是一种无状态的协议,也就是说客户端对服务端的每一次请求,服务端都不会对客户端进行记录。但是有时候需要保存客户端的状态,比如说购物平台中创建的购物车功能,这就需要通过一种机制来维持客户端与服务端的状态,故提出了Http会话维持机制,在自定义轻量级中是通过session与cookie机制实现的。
关于cookie与session机制:cookie保存在客户端,且容量是有限的,所有关于用户信息都是通过明文的形式保存在cookie中,所以这样就有一个致命的问题,如果在传输过程中cookie被黑客劫持,黑客通过伪造cookie之后访问服务端,那么就会造成用户隐私的泄露,所以为了防患于未然,在cookie传输过程中需要对其进行加密处理之后传递给服务端。而session则是保存在服务端用来存储用户数据的机制,相比于cookie,session存储的数据容量比较大且相对于比较安全,但是,session并非就是完美的,如果存储session的数据库存储在单台服务器上,随着用户量的激增,存储session的数据库就会面临一定的压力,这样就引出了JWT的模式,相比于cookie于session机制,JWT避免了其缺点,关于JWT的原理网上有许多的文章可供参考,这里就不必赘述了。
Http会话维持之验证机制: 客户端第一次请求服务端的时候,服务端并未对客户端的信息进行保存,所以第一次请求过来之后,服务端首先会生成一个唯一的ID用来标识该客户,并将该ID保存在服务端,在返回响应的时候,会携带该ID到客户端,以后该客户端的每一次请求都会携带该ID来到服务端,服务端校验成功之后,就会将该客户端的请求信息返回给客户端。
服务端session的设计 :
session_id的生成 :
def setter_session_id() :
return base64.encodebytes(str(time.time()).encode()).decode()[:-2][::-1]
session_id的获取 :
def getter_session_id(request) :
return request.cookies.get('session_id','')
对Session基类的设计 : 为了保证全局共用一个session,所以利用设计模式中的单例模式实现。单例模式指得是一个基类在内存中生成一个对象,单例模式分为两种:饿汉模式以及懒汉模式。对于单例模式最通俗的解释就是创建对象完成之后直接使用还是即用即创建?对于前一种就称为饿汉模式,后一种就称为懒汉模式,饿汉模式生成的对象占用的内存是一直固定的,而懒汉模式则创建对象不唯一,所以说饿汉模式是线程安全的,而懒汉模式是线程不安全的,需要使用的时候加锁处理。
Python实现饿汉模式 :
class SignalPattern :
def __new__(cls, *args, **kwargs):
if not hasattr(cls,"instance") :
cls.instance = super().__new__(cls)
return cls.instance
Python实现懒汉模式 :
class SignalPattern :
def __init__(self) :
if not hasattr(self,"_instance") :
print("对象未被创建")
else :
print("对象创建成功")
@classmethod
def get_instance(cls) :
if not hasattr(cls,"_instance") :
cls._instance = SignalPattern()
else :
return cls._instance
关于Session基类的创建锁涉及到的相关操作,并非非常复杂,且客户端session全部保存在本地,具体的业务逻辑也是非常容易理解的,代码中也有着详细的解释。
import base64
import time
import os
import json
def setter_session_id() :
return base64.encodebytes(str(time.time()).encode()).decode()[:-2][::-1]
def getter_session_id(request) :
return request.cookies.get('session_id','')
class Session :
def __new__(cls, *args, **kwargs):
if not hasattr(cls,"instance") :
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self,session_path = 'sessions'):
#所有的客户端的session信息全部都是通过session_id : information的形式保存在__session__map__中
self.__session_map__ = { }
self.__storage_path__ = session_path # 会话本地存放目录
if not os.path.exists(self.__storage_path__):
os.makedirs(self.__storage_path__)
def push(self,request,item,value):
#向session中添加信息
session_id = getter_session_id(request)
if session_id in self.__session_map__ :
self.__session_map__[session_id][item] = value
else :
self.__session_map__[session_id] = {item : value}
self.storage(session_id)
def pop(self, request, item, value=True):
#删除session中信息
session_id = getter_session_id(request)
current_session = self.__session_map__.get(session_id, {})
if item in current_session:
current_session.pop(item)
self.storage(session_id)
def set_storage_path(self, session_path):
self.__storage_path__ = session_path
if not os.path.exists(self.__storage_path__):
os.makedirs(self.__storage_path__)
def storage(self, session_id):
#session持久化机制,也就是将session保存在本地的目录之下
filename = os.path.join(self.__storage_path__, session_id)
with open(filename, 'wb') as f:
content = json.dumps(self.__session_map__.get(session_id))
f.write(base64.encodebytes(content.encode()))
def load_all_session(self):
#在框架启动的时候,将本地session加载到项目中
session_file_list = os.listdir(self.__storage_path__)
for session_id in session_file_list:
path = os.path.join(self.__storage_path__, session_id)
with open(path, 'rb') as f:
content = f.read()
content = base64.decodebytes(content)
self.__session_map__[session_id] = json.loads(content.decode())
def get(self,request):
return self.__session_map__.get(getter_session_id(request), {})
def get_item(self, request, item):
return self.get(request).get(item, None)
session = Session()
测试用例 :在访问http://127.0.0.1:8000/login
的浏览器会跳转到登陆页面,并跳出登陆框,输入登陆信息的时候点击登陆的时候,服务端会生成一个唯一ID在本地存储session的目录之下,浏览器会跳转到http://127.0.0.1:8000
并在浏览器中渲染出页面的关于用户的信息,点击退出的时候,服务端会删除保存在本地session中的信息,并重定向到http://127.0.0.1:8000/music
为了完成对登陆的校验 实现的session校验器 :
class AuthSession:
# session检验器
@classmethod
def auth_session(cls, f, *args, **options):
def decorator(obj, request):
if cls.auth_logic(request, *args, **options):
return f(obj, request)
return cls.auth_fail_callbacer(request, *args, **options)
return decorator
@staticmethod
def auth_logic(request, *args, **options):
raise NotImplementedError
@staticmethod
def auth_fail_callbacer(request, *args, **options):
raise NotImplementedError
实现的session检验基类
from authentication import AuthLogin
class SessionHandler(BaseHandler):
@AuthLogin.auth_session
def dispatch_request(self, request, *args, **options):
return super(SessionHandler, self).dispatch_request(request, *args, **options)
登陆校验类的实现 :
from session import AuthSession,session
from others import redirect
class AuthLogin(AuthSession) :
@staticmethod
def auth_fail_callbacer(request, *args, **options):
return redirect(url='/')
@staticmethod
def auth_logic(request, *args, **options):
if 'user' in session.get(request) :
return True
return False
测试代码:
class TemplateHomePageHandler(SessionHandler) :
def get(self,request,*args,**options):
user = session.get_item(request,'user')
return simple_template('template.html',user = user,message = 'Session Page')
class UserloginHandler(BaseHandler) :
def get(self,request,*args,**options):
return simple_template("login.html")
def post(self,request,*args,**options):
user = request.form['user']
session.push(request,'user',user)
return '登录成功,<a href="/">返回</a>'
class UserlogoutHandler(SessionHandler) :
def get(self,request,*args,**options):
session.pop(request,'user')
return redirect(url='/')
测试反馈:
- 访问
http://127.0.0.1:8000/login
浏览器渲染的页面
2. 登陆之后浏览器渲染的页面