依赖MVC设计模式实现轻量级Web框架(六) : Http会话维持的实现

关于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='/')

测试反馈:

  1. 访问http://127.0.0.1:8000/login浏览器渲染的页面

在这里插入图片描述
2. 登陆之后浏览器渲染的页面
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值