上次我们学到了redis的一些操作,下面来实际运用以下。
这里我们先来学习一下什么是cookie和session。
什么是Cookie
其实简单的说就是当用户通过http协议访问一个服务器的时候,这个服务器会将一些Name/Value键值对返回给客户端浏览器,并将这些数据加上一些限制条件。在条件符合时,这个用户下次再访问服务器的时候,数据又被完整的带给服务器。
因为http是一种无状态协议,用户首次访问web站点的时候,服务器对用户一无所知。而Cookie就像是服务器给每个来访问的用户贴的标签,而这些标签就是对来访问的客户端的独有的身份的一个标识,这里就如同每个人的身份证一样,带着你的个人信息。而当一个客户端第一次连接过来的时候,服务端就会给他打一个标签,这里就如同给你发了一个身份证,当你下次带着这个身份证来的时候,服务器就知道你是谁了。所以Cookie是存在客户端的,这里其实就是在你的浏览器中。
Cookie中包含了一个由名字=值(name=value)这样的信息构成的任意列表,通过Set-Cookie或Set-Cookie2HTTP响应(扩展)首部将其贴到客户端身上。
Cookie的分类
这里Cookie主要分为两种:
会话Cookie:不设置过期时间,保存在浏览器的内存中,关闭浏览器,Cookie便被销毁
普通Cookie:设置了过期时间,保存在硬盘上
关于Session
上面我们知道了Cookie可以让服务器端跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果Cookie很多,这无形地增加了客户端与服务端的数据传输量,而Session的出现正是为了解决这个问题。
同一个客户端每次和服务端交互时,不需要每次都传回所有的Cookie值,而是只要传回一个ID这个ID是客户端第一次访问服务器的时候生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的ID,客户端只要传回这个ID就行了,这个ID通常是NANE为JSESIONID的一个Cookie。所以Session其实是利用Cookie进行信息处理的。
cookie和session的共同之处在于:cookie和session都是用来跟踪浏览器用户身份的会话方式。
cookie和session的区别是:cookie数据保存在客户端,session数据保存在服务器端。
cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session,当然也没有绝对的安全,只是相对cookie,session更加安全
session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE
cookie和session各有优缺点,所以将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中。
实例:
先来验证用session登录的程序,分析:
首先先设计一个登录的程序,生成session写入redis,然后设计一个查询程序,传入session进行校验。
第一次登录:从my_user表中验证用户是否存在,用户存在且密码正确的场合生成session(规则:(当前时间戳+username))存入redis。
第二次登录查看:
#1、先验证用户是否登陆,username,session #2、验证session是否正确,判断和redis里边是否一致 #3、如果一致返回查询结果 #4、如果不一致,提示session非法 # session不存在,则提示用户未登录 # session不一致,登陆失败 #在K-V中传入session
以下是常用工具,放在lib目录的tools文件中:
#常用工具 import pymysql,hashlib,time import redis from conf import setting # #def op_mysql(host,user,passwd,db,sql,port=3306,charset='utf8'): def op_mysql(sql): conn=pymysql.connect(host=setting.MYSQL_HOST,user=setting.USER,passwd=setting.PASSWORD,port=setting.PORT,db=setting.DB,charset='utf8') cur=conn.cursor(cursor=pymysql.cursors.DictCursor) cur.execute(sql) sql_start=sql[:6].upper() if sql_start=='SELECT':#取sql的前6位,判断它是什么类型的语句。 res=cur.fetchall() else: conn.commit() #res='ok' cur.close() conn.close() return res
def op_redis(k,v=None,expired=0,db=0): #def op_redis(host,passwd,k,v=None,port=6379,db=1): #r=redis.Redis(host=host,password=passwd,port=port,db=db) r=redis.Redis(host=setting.REDIS_HOST,password=setting.REDIS_PASSWORD,port=setting.REDIS_PORT,db=db) if v: r.setex(k,v,expired) res='ok' else: #res=r.get(k).decode()#获取不到的时候返回none,none不能直接进行decode操作 res=r.get(k) if res: res=res.decode() else: res='none' return res def md5_password(st:str):#规定传参的类型必须是str类型 #st = hashlib.md5() bytes_st=st.encode() m=hashlib.md5(bytes_st) return m.hexdigest()
#main.py
import flask,time,hashlib from lib.tools import op_mysql,op_redis,md5_password import json server=flask.Flask(__name__) @server.route('/login',methods=['post']) def login(): username=flask.request.values.get('name','') password=flask.request.values.get('passwd','') sql="select * from my_user where username='%s';"%(username) if username and password: if op_mysql(sql): print(op_mysql(sql)) if password==op_mysql(sql)[0]['passwd']: session=md5_password(str(time.time())+username) op_redis('session:%s'%username,session,expired=6000,db=2) response={'code':200,'msg':'登陆成功','session':session} else: response={'code':101,'msg':'密码不正确'} else: response={'code':102,'msg':'用户不存在'} else: response={'code':103,'msg':'用户名或密码不能为空'} return json.dumps(response,ensure_ascii=False)
启动服务后,在postman中调用127.0.0.1:8088/login?name=pei&passwd=123456,执行一个正确的用户登录,:
{"msg": "登陆成功", "code": 200, "session": "28b14dea0c7a668650fbb19f6364f482"}
查看到redis里边已经插入了一个session数据:
下面进行第二部,传入用户名、session,如果验证正确则查询中表中的数据:
@server.route('/getall',methods=['post','get']) def getall(): username=flask.request.values.get('name') session=flask.request.values.get('session') k='session:%s'%(username) redis_session=op_redis(k,db=2) if username and session: if redis_session: if redis_session==session: sql="select red,blue from seq " response = op_mysql(sql) else: response={'code':101,'msg':'session非法'} else: response={'code':102,'msg':'未登录'} else: response={'code':103,'msg':'必填参数为空'} return json.dumps(response,ensure_ascii=False)
以上是在k-v参数中传入session值,如需需要在cookie中传入session,代码如下:
就是把session=flask.request.values.get('session')改为session=flask.request.cookies.get('session')则取值就是在cookie中进行取值了。
@server.route('/getall_cookie',methods=['post','get']) def getall_cookie(): username=flask.request.values.get('name') session=flask.request.cookies.get('session') k='session:%s'%(username) redis_session=op_redis(k,db=2) if username and session: if redis_session: if redis_session==session: sql="select red,blue from seq " response = op_mysql(sql) else: response={'code':101,'msg':'session非法'} else: response={'code':102,'msg':'未登录'} else: response={'code':103,'msg':'必填参数为空'} return json.dumps(response,ensure_ascii=False)
在我们登录购物网站的话,如果勾选记住密码,一般情况下就是在客户端浏览器上添加了cookie,这样用户打开网站后后台会自动校验cookie信息,不用每次都传一次,如下是设置cookie的代码:
要在页面添加cookie需要先response=flask.make_response(),然后response.set_cookie('session',session)set上要添加的cookie信息
@server.route('/login_setcookie',methods=['post']) def login_setcookie(): username=flask.request.values.get('name','') password=flask.request.values.get('passwd','') sql="select * from my_user where username='%s';"%(username) if username and password: if op_mysql(sql): print(op_mysql(sql)) if password==op_mysql(sql)[0]['passwd']: session=md5_password(str(time.time())+username) op_redis('session:%s'%username,session,expired=6000,db=2) response=flask.make_response() response.set_cookie('session',session) msg={'code':200,'msg':'登陆成功','session':session} else: msg={'code':101,'msg':'密码不正确'} else: msg={'code':102,'msg':'用户不存在'} else: msg={'code':103,'msg':'必填参数为空'} return response
添加cookie后,我们在调用getall_cookie时只传入name,就能直接获取到seq表中的数据了。
补充:
mysql注入原理
上述代码中存在的sql语句如下:
sql="select * from my_user where username='%s';"%(username)
在获取username时,由于存在‘’,所以存在漏洞;
我们知道‘1’=‘1’是恒为真的,我们可以通过‘’,来模拟制造出这种恒等式,使sql在执行时跳过一些校验,从而进阶访问系统。
当我们输入name=“' or '1'='1”
我们将上述name的值带入sql如下:
sql="select * from my_user where username='' or '1'='1';"
我们在sqlyog中my_user表中执行这个语句,查询出了所有的用户信息,
在一些查询操作中,很容易产生漏洞,造成信息的泄露。
还有一种注入方式是:username="' show tabales ; --"
sql="select * from my_user where username='' show tabales ; --';"
sql语句中--表示注释掉后边的语句,这样就查询出了所有的表,可以对数据库随意进行操作。
为了防止sql的注入,在编写sql的时候我们尽量避免使用‘’,写成如下方式:
sql="select * from my_user where username=%s;",username
这样在传参的时候,可以直接传到输入的参数进行取值,例如:
def op_mysql_new(sql,*data):#第一个是位置参数,第二个是可变参数 cur.execute(sql,data) #data是一个元祖,*data获取出所有传过去的参数 print(cur.fetchall())
sql="select * from users where name=%s and passwd=%s;" name='pei' passwd='123456' op_mysql_new(sql,name,passwd)
###################################################33
传参: def test(a,b): print(a,b) li=[1,2] test(*li)#一个星代表把list里边的数据穿进去 d={'a':'123','b':'456'} test(**d)#两个星代表从字典里边的数据传进去
最后:下方这份完整的【软件测试】视频学习教程已经整理上传完成,朋友们如果需要可以自行免费领取 【保证100%免费】