Pyhton专项进阶——http协议、cookie、session和认证-3

关于cookie的报文首部相关属性熟悉后,下面就是实际应用。

使用cookie实现用户登录验证(初步)

思路(一):显示登录页面,输入用户和密码,后端验证,如果验证通过,则登录成功,响应报文中将用户、密码写入cookie,访问其他页面时,通过cookie获取用户信息,能够获取,则直接访问页面,不能获取,则跳转至登录页面。

细节再加工:第一次登录成功后,在cookie的有效时间内,再次访问(登录页面),不显示登录页面直接跳转至主页面(登录用户还有效)

代码:cookiedemo.py

from bottle import template, Bottle,request,response,redirect
import os,time,datetime
import bottle
bottle.TEMPLATE_PATH.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "templates")))
root = Bottle()@root.route('/login/',method=["GET","POST"])
# 装饰器,定义了URL,即/hello/这个url由index这个函数来处理,就是路由系统
def login():message=""if request.method == "GET":# GET方法请求时,也判断是否已登录,即cookie是否保存用户名和密码username=request.get_cookie("username")password=request.get_cookie("passwd")if username == "root" and password == "222222":# 如果cookie存在对用的用户和密码,则直接跳转到index主页面redirect("/index/")else:   # 否则,打开登录页面return template("login.html")else: # 对于POST方法,是登录过程,要获取用户名和密码username = request.POST.get("user")password = request.POST.get("pwd")print(username,password)if not username:message = "用户名不能为空"print(message)return template("login.html",msg=message)else:print("查询用户名是否存在,密码是否匹配:")print("select username,password from user where username=username and password=password")if username == "root" and password == "222222":print("用户存在,登录成功,写入cookie")response.set_cookie("username","root",max_age=50,path="/")response.set_cookie("passwd","222222",max_age=50,path="/")redirect("/index/")else:message = "用户名或密码错误!"print(message)return template("login.html",msg=message)@root.route('/index/')
def index():username=request.get_cookie('username')passward=request.get_cookie('passwd')if username != "root" or passward != "222222":redirect('/login/')else:return template("index.html",username=username)@root.route("/content1.html")
def cont1():username=request.get_cookie('username')passward=request.get_cookie('passwd')if username != "root" or passward != "222222":redirect('/login/')else:return template("content1.html",username=username)@root.route("/content2.html")
def cont2():username=request.get_cookie('username')passward=request.get_cookie('passwd')if username != "root" or passward != "222222":redirect('/login/')else:return template("content2.html",username=username)root.run(host='localhost', port=8080)
<!-- login.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>Bottle登录</h1><form action="/login/" method="post"><input type="text" name="user" placeholder="用户名"><input type="password" name="pwd" placeholder="密码"><input type="submit" value="提交"><span style="color: red">{{get('msg','')}}</span></form>
</body>
</html><!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>业务主页面</h1><hr/><span style="color: green">登录用户:{{get('username','')}}</span><br><a href="/content1.html">业务一</a><br><a href="/content2.html">业务二</a>
</body>
</html><!-- content1.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>业务一主页</h1>----{{get("username","")}}
</body>
</html>

上面的实现,存在的问题有:

  • 在cookie有效期内,每次访问都发送用户名和密码,并且是明码,安全性差,被截获的可能性很大;
  • 判断是否登录的代码重复出现;

解决发送明码用户名和密码的问题,可以使用加密的方法,将用户名和密码改为加密字符串保存到cookie中,使用这个串来验证用户;代码重复的问题,可以将相关代码抽取,形成单独的函数,做成装饰器。

解决安全性,使用md5加密:

from bottle import template, Bottle,request,response,redirect
import os,time,datetime
import bottle
from hashlib import md5bottle.TEMPLATE_PATH.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "templates")))
root = Bottle()
userdb={"root":"222222","admin":"333333"}  # 模拟用户数据库
md5db={}# 数据示例如下:# {"md5":{"username":"",#          "is_login":True,#          "expiredate":""}}  # 登录后保存用户信息def myauth(username,passwd):# 模拟用户验证,存在用户且密码正确,返回Trueif userdb.get(username) == passwd:return Trueelse:return Falsedef mymd5(md5val=None):# 产生MD5值,操作md5db,用户登录成功后,保存相关信息#nowtime = datetime.datetime.utcfromtimestamp(time.time())if md5val in md5db.keys():tem = md5db[md5val]if tem['is_login'] == True and tem['expiredate'] > nowtime:md5val_new = md5(((tem['username']+str(nowtime)).encode('utf-8'))).hexdigest()md5db[md5val]['expiredate']= datetime.datetime.utcfromtimestamp(time.time() + 60)md5db[md5val_new] = md5db.pop(md5val)return [md5val_new,md5db[md5val_new]]else:return None@root.route('/login/',method=["GET","POST"])
# 装饰器,定义了URL,即/hello/这个url由index这个函数来处理,就是路由系统
def login():message=""if request.method == "GET":md5val=request.get_cookie('hashval')tem = mymd5(md5val=md5val)if tem:response.set_cookie("hashval",tem[0])return redirect("/index/")else:   # 否则,打开登录页面return template("login.html")else: # 对于POST方法,是登录过程,要获取用户名和密码username = request.POST.get("user")password = request.POST.get("pwd")if not username:message = "用户名不能为空"print(message)return template("login.html",msg=message)else:print("查询用户名是否存在,密码是否匹配:")if myauth(username,password):print("用户存在,登录成功,写入cookie")nowtime = datetime.datetime.utcfromtimestamp(time.time() + 60)md5val_new = md5((username + str(nowtime)).encode('utf-8')).hexdigest()md5db[md5val_new]={'username':username,'is_login':True,'expiredate':nowtime}response.set_cookie('hashval',md5val_new,expires=nowtime,path='/')return redirect("/index/")else:message = "用户名或密码错误!"print(message)return template("login.html",msg=message)@root.route('/index/')
def index():md5val = request.get_cookie("hashval")tem = mymd5(md5val)if tem:md5val_new = tem[0]expiredate = tem[1]['expiredate']response.set_cookie('hashval',md5val_new,expires=expiredate,path='/')return template("index.html",username=tem[1]['username'])else:return redirect("/login/")@root.route("/content1.html")
def cont1():md5val = request.get_cookie("hashval")tem = mymd5(md5val)if tem:md5val_new = tem[0]expiredate = tem[1]['expiredate']response.set_cookie('hashval',md5val_new,expires=expiredate,path='/')return template("content1.html", username=tem[1]['username'])else:return redirect("/login/")@root.route("/content2.html")
def cont2():md5val = request.get_cookie("hashval")tem = mymd5(md5val)if tem:md5val_new = tem[0]expiredate = tem[1]['expiredate']response.set_cookie('hashval',md5val_new,expires=expiredate,path='/')return template("content2.html", username=tem[1]['username'])else:return redirect("/login/")root.run(host='localhost', port=8080)

这样,cookie传递的是用户md5后的散列值,并且每次请求都不一样,一定程度上解决了用户、密码安全性问题。

解决重复问题,使用装饰器:

from bottle import template, Bottle,request,response,redirect
import os,time,datetime
import bottle
from hashlib import md5bottle.TEMPLATE_PATH.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "templates")))
root = Bottle()
userdb={"root":"222222","admin":"333333"}
md5db={}# 键值对示例如下:# {"md5":{"username":"",#           "is_login":True,#           "expiredate":""}}def myauth(username,passwd):# 模拟用户验证,存在用户且密码正确,返回Trueif userdb.get(username) == passwd:return Trueelse:return Falsedef mymd5(md5val=None):# 产生MD5值,操作md5db,用户登录成功后,保存相关信息#nowtime = datetime.datetime.utcfromtimestamp(time.time())if md5val in md5db.keys():tem = md5db[md5val]if tem['is_login'] == True and tem['expiredate'] > nowtime:md5val_new = md5(((tem['username']+str(nowtime)).encode('utf-8'))).hexdigest()md5db[md5val]['expiredate']= datetime.datetime.utcfromtimestamp(time.time() + 60)md5db[md5val_new] = md5db.pop(md5val)return [md5val_new,md5db[md5val_new]]else:return None
tem = None
def logindecorator(f):print("装饰器")def inner(*args,**kwargs):global temmd5val = request.get_cookie('hashval')tem = mymd5(md5val)print("执行函数:",f.__name__)print("===>", tem)if tem:response.set_cookie("hashval",tem[0],expires=tem[1]['expiredate'],path="/")return f()  # 注意,这里必须使用returnelse:redirect("/login/")return inner@root.route('/login/',method=["GET","POST"])
# 装饰器,定义了URL,即/hello/这个url由index这个函数来处理,就是路由系统
def login():message=""if request.method == "GET":md5val=request.get_cookie('hashval')tem = mymd5(md5val=md5val)if tem:response.set_cookie("hashval",tem[0])return redirect("/index/")else:   # 否则,打开登录页面return template("login.html")else: # 对于POST方法,是登录过程,要获取用户名和密码username = request.POST.get("user")password = request.POST.get("pwd")if not username:message = "用户名不能为空"print(message)return template("login.html",msg=message)else:print("查询用户名是否存在,密码是否匹配:")if myauth(username,password):print("用户存在,登录成功,写入cookie")nowtime = datetime.datetime.utcfromtimestamp(time.time() + 60)md5val_new = md5((username + str(nowtime)).encode('utf-8')).hexdigest()md5db[md5val_new]={'username':username,'is_login':True,'expiredate':nowtime}response.set_cookie('hashval',md5val_new,expires=nowtime,path='/')return redirect("/index/")else:message = "用户名或密码错误!"print(message)return template("login.html",msg=message)@root.route('/index/')
@logindecorator
def index():return template("index.html", username=tem[1]['username'])@root.route("/content1.html")
@logindecorator
def cont1():return template("content1.html", username=tem[1]['username'])@root.route("/content2.html")
@logindecorator
def cont2():return template("content2.html", username=tem[1]['username'])root.run(host='localhost', port=8080)

如此,重复的问题也解决了。

关于session

Cookie是一种记录客户状态的机制,如前面的例子,用户信息保存在客户端,而Session是另一种记录客户状态的机制,不同于Cookie的是,Session将用户信息保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。

如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。服务器要查询客户档案,也需要一个标识,这个标识是服务器在建立session是生成的一个字符串(加密的),然后保存到客户端的cookie。所以,session也需要使用cookie。

Session 代表着服务器和客户端一次会话的过程。Session 对象存储:特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。

如下示例:

from flask import Flask,render_template,redirect,request,make_response,session,url_forapp = Flask(__name__)
app.config.update(SECRET_KEY=b'qwe123!@#1qazxsw23edcvfr')@app.route('/')
def hello_world():return 'Hello World!'
@app.route('/login/',methods=['GET','POST'])
def login():if request.method == 'POST':session['user'] = request.form.get('user')  # 设置session内容return redirect('/index/')else:return render_template('login.html')
@app.route('/index/')
def index():print(session)return render_template('index.html')if __name__ == '__main__':app.run()

在后台打印的结果:<SecureCookieSession {'user': 'root'}>

通过抓包,可以发现,所谓的session就是在cookie中保存了一个key为session,value是加密字符串的东西,而在服务器端,保存了对应的用户信息,就是SecureCookieSession对象,其中保存了user:root这个信息,通过session的值,就能找到这个对象及其相关信息。

所以,在前面cookie的例子中,实现的其实就是session的功能,md5db就相当于服务器端的session信息库,mymd5()就是服务器实现session的功能函数,保存到cookie中的hashval,就是这里的写入cookie的session。只不过这里session的实现更全面,考虑的东西更多。

测试session过期时间:

from flask import Flask,render_template,redirect,request,make_response,session,url_for
from datetime import timedelta
app = Flask(__name__)
app.config.update(SECRET_KEY=b'qwe123!@#1qazxsw23edcvfr')
userdb = {"root":"1","admin":"2"}def myauth(username,password):if userdb.get(username) == password:return Trueelse:return False@app.route('/')
def hello_world():return 'Hello World!'
@app.route('/login/',methods=['GET','POST'])
def login():if request.method == 'POST':username = request.form.get('user')password = request.form.get('pwd')if myauth(username,password):session['username'] = username  # 设置session内容session['is_login'] = Truesession.permanent = Trueapp.permanent_session_lifetime = timedelta(seconds=50)return redirect('/index/')else:return render_template('login.html')else:return render_template('login.html')
@app.route('/index/')
def index():print("获取的session内容:",session.get('is_login'))return render_template('index.html')if __name__ == '__main__':app.run()

通过session.permanent=True和app.permanent_session_lifetime,可以设置session有效时间,这里设置为50秒,然后可以看一下cookie的设置及后台打印结果:

get访问login时:

响应报文没有写cookie,登录:

因为是redirect,进行了两次请求,POST方法的login,以及GET方法的index

login的cookie被写入session键及值,过期时间是当前时间+50秒

index请求头中携带了cookie,即login过程中的session键值对。

后台打印:

session没有过期,所以获取了session内容。

50秒后,重新刷新index,没有cookie被发送,后台打印

如果在有效期内,刷新login或index,则会重复生成新的session键值对,并写入cookie,过期时间也会重新生成,在当前时间加上50秒。这跟前面测试cookie时自己写的模拟程序mymd5()功能一样。

加上装饰器,对是否登录进行自动判定:

from flask import Flask,render_template,redirect,request,make_response,session,url_for
from datetime import timedelta
app = Flask(__name__)
app.config.update(SECRET_KEY=b'qwe123!@#1qazxsw23edcvfr')
userdb = {"root":"1","admin":"2"}def myauth(username,password):if userdb.get(username) == password:return Trueelse:return False@app.route('/')
def hello_world():return 'Hello World!'def logindecorator(f):def inner(*args,**kwargs):if session.get('is_login'):return f()else:return redirect('/login/')return inner@app.route('/login/',methods=['GET','POST'])
def login():if request.method == 'POST':username = request.form.get('user')password = request.form.get('pwd')if myauth(username,password):session['username'] = username  # 设置session内容session['is_login'] = Truesession.permanent = Trueapp.permanent_session_lifetime = timedelta(seconds=50)return redirect('/index/')else:return render_template('login.html')else:if session.get('is_login'):return redirect('/index/')else:return render_template('login.html')@app.route('/index/')
@logindecorator
def index():print("获取的session内容:",session.get('is_login'))return render_template('index.html',username=session.get('username'))if __name__ == '__main__':app.run()

至此,使用cookie与session的原理与流程大体搞明白了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/252896.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【TCP】四次挥手(终止连接)

前言 TCP&#xff08;传输控制协议&#xff09;是互联网协议&#xff08;IP&#xff09;中的一种重要传输层协议&#xff0c;用于在通信的计算机之间建立可靠的、有序的和错误校验的数据传输。在TCP连接中&#xff0c;数据传输是双向的&#xff0c;因此需要一种机制来开始和结…

Mac上新版InfluxDB使用教程

一、简介 官网&#xff1a;influxdb 二、influxdb安装 建议使用Homebrew在 macOS 上安装 InfluxDB v2&#xff1a; brew install influxdb启动influxdb服务&#xff1a;brew services start influxdb 停止influxdb服务&#xff1a;brew services stop influxdb 查看是否启…

docker 运行jar包 指定配置文件

要在Docker中运行JAR包并指定配置文件&#xff0c;你可以创建一个Dockerfile来定义你的容器环境&#xff0c;并在其中指定如何运行JAR包和配置文件。下面是一个简单的例子&#xff0c;展示了如何在Dockerfile中设置这些配置&#xff1a; 第一步&#xff1a;创建 Dockerfile文件…

《MySQL》超详细笔记

目录 基本知识 主流数据库 数据库基本概念 MySQL启动 数据库基本命令 数据库 启动数据库 显示数据库 创建数据库 删除数据库 使用数据库 查询当前数据库信息 显示数据库中的表 导入数据库脚本 表 查看表的结构 查看创建某个表的SQL语句 数据库的查询命令 查询…

人工智能(pytorch)搭建模型24-SKAttention注意力机制模型的搭建与应用场景

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型24-SKAttention注意力机制模型的搭建与应用场景&#xff0c;本文将介绍关于SKAttention注意力机制模型的搭建&#xff0c;SKAttention机制具有灵活性和通用性&#xff0c;可应用于计算机视…

canvas缩放坐标系(scale)

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

2024最新版鸿蒙HarmonyOS开发工具安装使用指南

2024最新版鸿蒙HarmonyOS开发工具安装使用指南 By JacksonML 0. 什么是鸿蒙Harmony OS&#xff1f; 华为鸿蒙系统&#xff08;HUAWEI Harmony OS&#xff09;&#xff0c;是华为公司在2019年8月9日于东莞举行的华为开发者大会&#xff08;HDC.2019&#xff09;上正式发布的分…

Java20:新特性

一&#xff1a;Lambda表达式&#xff1a; 1. Lambda表达式使用前后对比&#xff1a; 举类一&#xff1a; Testpublic void test(){ Runnable r1 new Runnable() {Overridepublic void run() {System.out.println("我爱北京天安门&#xff01;");} };r1.run();Syst…

基于SSM的协同过滤技术的网上书城(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的协同过滤技术的网上书城&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Sp…

手撕spring bean的加载过程

这里我们采用手撕源码的方式&#xff0c;开始探索spring boot源码中最有意思的部分-bean的生命周期&#xff0c;也可以通过其中的原理理解很多面试以及工作中偶发遇到的问题。 springboot基于约定大于配置的思想对spring进行优化&#xff0c;使得这个框架变得更加轻量化&#…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Radio组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Radio组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Radio组件 单选框&#xff0c;提供相应的用户交互选择项。 子组件 无。 接口 …

第十五篇【传奇开心果系列】Python的OpenCV库技术点案例示例:图像配准

传奇开心果短博文系列 系列短博文目录Python的OpenCV库技术点案例示例系列短博文目录前言一、常见的图像配准任务介绍二、图像配准任务:图像拼接介绍和示例代码三、图像配准任务:图像校正介绍和示例代码四、图像配准任务:图像配准介绍和示例代码五、基于特征点的配准方法介绍…

面试150 颠倒二进制位 位运算分治 逻辑右移

Problem: 190. 颠倒二进制位 文章目录 思路复杂度位运算分治法 思路 &#x1f468;‍&#x1f3eb; 参考题解 >>>&#xff1a;逻辑右移&#xff08;符号位一起移动&#xff0c;高位补零&#xff09; 复杂度 时间复杂度: O ( log ⁡ n ) O(\log{n}) O(logn) 空间…

Nacos1.X源码解读(待完善)

下载源码 1. 克隆git地址到本地 # 下载nacos源码 git clone https://github.com/alibaba/nacos.git 2. 切换分支到1.4.7, maven编译(3.5.1) 3. 找到启动类com.alibaba.nacos.Nacos 4. 启动VM参数设置单机模式, RUN 启动类 -Dnacos.standalonetrue 5. 启动本地服务注册到本…

3、生成式 AI 如何帮助您改进数据可视化图表

生成式 AI 如何帮助您改进数据可视化图表 使用生成式 AI 加速和增强数据可视化。 图像来源:DALLE 3 5 个关键要点: 数据可视化图表的基本结构使用 Python Altair 构建数据可视化图表使用 GitHub Copilot 加快图表生成速度使用 ChatGPT 为您的图表生成相关内容使用 DALL-E 将…

elementPlus实现动态表格单元格合并span-method方法总结

最近在做PC端需求的时候&#xff0c;需要把首列中相邻的同名称单元格合并。 我看了一下elementPlus官网中的table表格&#xff0c;span-method可以实现单元格合并。 我们先看一下官网的例子&#xff1a; 合并行或列 多行或多列共用一个数据时&#xff0c;可以合并行或列。 …

IDEA生成可执行jar包

1. 进入需要打包的项目&#xff0c;选择 最上方菜单栏的 File → Project Structure 2. 选择 左侧菜单栏 Artifacts → 加号 → JAR → from modules with dependencies 3. 选择入口类 Main Class&#xff08;点击文件夹图标可以快速选择&#xff09;&#xff0c;点击 OK&#…

go语言每日一练——链表篇(六)

传送门 牛客面试必刷101题—— 判断链表中是否有环 牛客面试必刷101题—— 链表中环的入口结点 题目及解析 题目一 代码 package mainimport . "nc_tools"/** type ListNode struct{* Val int* Next *ListNode* }*//**** param head ListNode类* return bool…

[Python] scikit-learn中数据集模块介绍和使用案例

sklearn.datasets模块介绍 在scikit-learn中&#xff0c;可以使用sklearn.datasets模块中的函数来构建数据集。这个模块提供了用于加载和生成数据集的函数。 API Reference — scikit-learn 1.4.0 documentation 以下是一些常用的sklearn.datasets模块中的函数 load_iris() …

Prompt Engineering实战-构建“哄哄模拟器”

目录 一 背景 二 “哄哄模拟器”的Prompt Prompt 的典型构成 三 操作步骤 3.1 创建对话 3.2 游戏测试 一 背景 前几天《AI 大模型全栈工程师》第二节课讲了“Prompt Engineering&#xff0c;提示工程”&#xff0c;里面提到一些prompt相关的技巧&#xff0c;原则&#xf…