1、session/cookie介紹
2、使用session實現登陸驗證
3、flask擴展之flask-login
Session和Cookie的結合使用,通常有兩種存儲方式:html
第一種: session數據存儲在客戶端: Flask採用'secure cookie'方式保存session,即session數據是使用base64編碼後保存在客戶端的cookie中。也就是說無須依賴第三方數據庫保存session數據。前端
第二種: session數據存儲在服務端,分爲如下三步驟:python
步驟1: 當客戶端發送請求到服務端的時候,服務端會校驗請求中cookie參數中的sessionid值,若是cookie中不存在sessionid則認爲客戶端訪問服務端時,是發起了一個新的會話。redis
步驟2: 若是是新的會話,則服務端會傳遞給客戶端一個cookie,並在cookie中存儲一個新的sessionid值,並將相關數據保存在session中。sql
步驟3: 客戶端下次再發送請求的時候,請求上下文對象會攜帶cookie,經過校驗cookie中的sessionid值,便可判斷是不是同一會話。mongodb
步驟4: 若是校驗會話是同一會話,則能夠從session中獲取到以前保存的數據。數據庫
訪問者的標識問題服務器須要識別來自同一訪問者的請求。這主要是經過瀏覽器的cookie實現的。 訪問者在第一次訪問服務器時,服務器在其cookie中設置一個惟一的ID號——會話ID(session)。 這樣,訪問者後續對服務器的訪問頭中將自動包含該信息,服務器經過這個ID號,便可區 隔不一樣的訪問者。flask
官網文檔地址後端
概念:跨域
a)客戶端會話技術,瀏覽器的會話技術 b)數據所有存儲在客戶端中 c)存儲使用的鍵值對結構進行存儲 特性: 支持過時時間 默認會自動攜帶本網站的cookie 不能跨域名 不能跨瀏覽器
建立:
Cookie是經過服務器建立的Response來建立的 設置:set_cookie('key', value, max_ages='', expires='') 刪除, 有三種刪除方式 1. 直接清空瀏覽器的cookie 2. delete_cookie('key') 直接使用delete_cookie函數 3. set_cookie('key','',expires=0) 從新設置key的值爲空,過時時間爲0
獲取:
在每次請求中,url都會向服務器傳遞Request,在request中能夠獲取到cookie的信息 request.cookies.get('name')
例子1,設置cookie:
import datetime @blue.route('/setcookie/') def set_cookie(): temp = render_template('index.html') response = make_response(temp) outdate=datetime.datetime.today() + datetime.timedelta(days=30) # 設置cookie中的name的存在時長,設置爲30天才過時 response.set_cookie('name','cocoococo',expires=outdate) return response
例子2,刪除cookie中的值
@blue.route('/setcookie/') def set_cookie(): temp = render_template('index.html') response = make_response(temp) # 第一種方式,經過set_cookie去刪除 response.set_cookie('name','',expires=0) # 第二種方式,del_cookie刪除 response.del_cookie('name') return response
例子3,獲取cookie中的值
@blue.route('/getcookie/') def get_cookie(): name=request.cookies.get('name') return name
flask-session是flask框架的session組件
該組件則將支持session保存到多個地方
如:
redis:保存數據的一種工具,五大類型。非關係型數據庫 memcached mongodb sqlalchmey:那數據存到數據庫表裏面
pip install flask-session
若是指定存session的類型爲redis的話,須要安裝redis
pip install redis
設置session:
session['key'] = value
讀取session:
result = session['key'] :若是內容不存在,將會報異常 result = session.get('key') :若是內容不存在,將返回None
刪除session:
session.pop('key')
清空session中全部數據:
session.clear()
咱們在初始化文件中建立一個方法,經過調用該方法來獲取到Flask的app對象
def create_app(): app = Flask(__name__) # SECRET_KEY 祕鑰 app.config['SECRET_KEY'] = 'secret_key' # session類型爲redis app.config['SESSION_TYPE'] = 'redis' # 添加前綴 app.config['SESSION_KEY_PREFIX'] = 'flask' # 加載app的第一種方式 se = Session() se.init_app(app=app) #加載app的第二種方式 Session(app=app) app.register_blueprint(blueprint=blue) return app
定義一個登錄的方法,post請求獲取到username,直接寫入到redis中,而且在頁面中展現出redis中的username
a)須要先啓動redis,開啓redis-server,使用redis-cli進入客戶端
b)定義方法
@blue.route('/login/', methods=['GET', 'POST']) def login(): if request.method == 'GET': username = session.get('username') return render_template('login.html', username=username) else: username = request.form.get('username') session['username'] = username return redirect(url_for('first.login'))
c)定義模板
<body> <h3>歡迎:{{ username }}</h3> <form action="" method="POST"> 用戶名:<input type="text" name="username" placeholder="請輸入你的名字"> <input type="submit" value="提交"> </form> </body>
d)redis中數據
注意:咱們在定義app.config的時候指定了SESSION_KEY_PREFIX爲flask,表示存在session中的key都會加一個前綴名flask
e) cookie和session的聯繫
訪問者在第一次訪問服務器時,服務器在其cookie中設置一個惟一的ID號——會話ID(session)。 這樣,訪問者後續對服務器的訪問頭中將自動包含該信息,服務器經過這個ID號,便可區 隔不一樣的訪問者。而後根據不一樣的訪問者來獲取其中保存的value值信息。
將session中的數據存儲在cookie中,並返回給客戶端。
缺點:數據存儲在cookie中,不安全
登陸頁面就兩個輸入框,分別接收用戶名和密碼
<dd class="user_icon"> <input type="text" name="username" placeholder="帳號" class="login_txtbx"/> </dd> <dd class="pwd_icon"> <input type="password" name="password" placeholder="密碼" class="login_txtbx"/> </dd>
@blue.route('login/', methods=['GET']) def login(): if request.method == 'GET': # 獲取提交的用戶名和密碼 username = request.args.get('username') password = request.args.get('password') # 模擬判斷用戶名和密碼 if username == '小明' and password == '123456': # 啓動permanent修改成True session.permanent = True # 在session中記錄登陸狀態 session['login_status'] = 1 return '登陸成功' else: return '登陸失敗'
# session加密方式 app.secret_key = '123' # 設置過時時間,5秒後session失效 app.permanent_session_lifetime = 5
注意:
1)設置一個持久化會話的存活時間,必須修改session.permanent的屬性和flask對象app的permanent_session_lifetime屬性,permanent_session_lifetime屬性做爲datetime.timedelta對象,從Flask0.8開始也能夠用一個整數表示多少秒後過時。
2)加密的強度取決於SECRET_KEY的複雜程度。通常SECRET_KEY能夠經過os.urandom(24)隨機生成。
使用session實現用戶的模擬登錄功能。在前面已經說過了,在用戶第一次訪問服務端的時候,在服務端的redis中會建立一個session值,在客戶端瀏覽器的cookies中也會建立一個session的值。該cookies中的session值和redis中的session值是同樣的,那麼在日後的訪問操做中,請求request都會傳遞給後端,後端在獲取到request的時候,其實就是獲取到了request.cookies中的session的值了,那麼就能夠作登陸的校驗了。校驗功能以下:
登陸頁面就兩個輸入框,分別接收用戶名和密碼
<dd class="user_icon"> <input type="text" name="username" placeholder="帳號" class="login_txtbx"/> </dd> <dd class="pwd_icon"> <input type="password" name="password" placeholder="密碼" class="login_txtbx"/> </dd>
模擬用戶的登陸,直接判斷用戶的名稱爲妲己以及密碼爲123123.若是驗證成功,就向session中保存用戶的id值。若是沒有登陸成功的話,那就對session不作任何的處理,直接跳轉到登陸頁面上去。
@app_blue.route('/new_login/', methods=['GET', 'POST']) def new_login(): if request.method == 'GET': return render_template('login.html') else: username = request.form.get('username') password = request.form.get('password') # 數據庫校驗,用戶密碼是否正確 if username == '妲己' and password == '123123': session['user_id'] = 1 return redirect((url_for('first.index'))) else: return redirect(url_for('first.new_login')) @app_blue.route('/index/', methods=['GET']) def index(): return render_template('index.html')
# 配置session from flask_session import Session # 指定redis做爲緩存數據庫 app.config['SESSION_TYPE'] = 'redis' # 指定訪問哪個redis,ip和端口 app.config['SESSION_REDIS'] = redis.Redis(host='127.0.0.1', port=6379) # 初始化app se = Session() se.init_app(app=app)
使用裝飾器去裝飾咱們的index()函數,若是用戶登陸了,則session中有user_id的key,若是沒有登陸的話,session中是沒有user_id的key的。那麼驗證用戶是否登陸了,其實就是驗證session的user_id
def is_login(func): @wraps(func) def check_login(*args, **kwargs): if 'user_id' in session: return func(*args, **kwargs) else: return redirect(url_for('first.new_login')) return check_login
@app_blue.route('/index/', methods=['GET']) @is_login def index(): return render_template('index.html')
在flask中如何快速的實現登陸註冊註銷功能,以及登陸狀態驗證等功能? flask的擴展庫中有Flask-Login庫就可快速的實現以上的功能,實現起來是很是的便利。
pip install flask-login
{% extends 'base.html' %} {% block title %} 登陸頁面 {% endblock %} {% block content %} <form action="" method="post"> 姓名:<input type="text" name="username"> 密碼:<input type="text" name="password"> <input type="submit" value="提交"> </form> {% endblock %}
登陸方法中定義被login_manager.user_loader裝飾的回調函數,回調函數在以下兩個地方被調用:
1)該函數代表當前用戶登陸成功時調用login_user()方法時,會被回調的函數。回調函數實現的功能是向會話上下文session中存儲最爲中間的鍵值對,key爲user_id, value爲當前登陸用戶的ID值。
2)回調函數在訪問任何一個路由地址時也會被調用。
注意: 由於請求上下文在每次創建鏈接時,都須要獲取當前登陸用戶並將當前登陸用戶設置爲全局上下文current_user,所以回調函數返回的是當前登陸系統的用戶對象。
from flask_login import LoginManager, login_required, login_user, logout_user,current_user # 獲取登陸管理對象 login_manager = LoginManager() @login_manager.user_loader def load_user(user_id): # 必須編寫一個函數用於從數據庫加載用戶。 # 這個函數在login_user(user)存儲當前登陸用戶到session中時,會被調用 # 在每次訪問地址的時候都被被調用,用於向請求上下文中綁定當前登陸的用戶信息 return User.query.get(user_id) @blue.route('/login/', methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') if request.method == 'POST': username = request.form.get('username') password = request.form.get('password') # 校驗用戶名和密碼是否填寫完成 if not all([username, password]): return render_template('login.html') # 經過用戶名獲取用戶對象 user = User.query.filter_by(username=username).first() # 校驗密碼是否正確 if check_password_hash(user.password, password): # 實現登陸 # login_user()可以將已登陸並經過load_user()的用戶對應的User對象保存在session中 # 在session中會建立一個鍵值對,key爲user_id,value爲當前登陸用戶的id值 # 若是但願應用記住用戶的登陸狀態, 只須要爲 login_user()的形參 remember 傳入 True 實參就能夠了. login_user(user) return redirect(url_for('user.index')) else: flash('用戶名或者密碼錯誤') return redirect(url_for('user.index'))
session_protection: 設置存儲用戶登陸狀態的安全級別
login_view: 設置登陸驗證失敗的跳轉地址
from user.views import login_manager app.config['SECRET_KEY'] = os.urandom(24) # 登陸管理,初始化app # 能夠設置None,'basic','strong'以提供不一樣的安全等級,通常設置strong,若是發現異常會登出用戶 # session_protection 可以更好的防止惡意用戶篡改 cookies, 當發現 cookies 被篡改時, 該用戶的 session 對象會被當即刪除, 致使強制從新登陸。 login_manager.session_protection='strong' # 當登陸認證不經過,則跳轉到該地址 login_manager.login_view='user.login' login_manager.init_app(app)
使用裝飾器login_required()進行登陸校驗。
核心思想: 校驗session中是否存在key爲user_id的鍵值對。若是校驗成功,則繼續訪問被裝飾的函數。若是校驗失敗,則跳轉到啓動文件中定義的login_manager.login_view定義的視圖函數。
@blue.route('/index/') @login_required def index(): return render_template('index.html')
若是登陸校驗成功,則渲染index.html首頁,在頁面中能夠解析全局變量current_user參數。
{% extends 'base.html' %} {% block title %} 首頁頁面 {% endblock %} {% block content %} <p>我是首頁</p> <p>當前登陸系統用戶爲: {{ current_user.username }}</p> {% endblock %}
使用logout_user()方法實現註銷,核心功能就是刪除當前會話上下文session中的user_id鍵值對。
# 退出 @blue.route('/logout/', methods=['GET']) @login_required def logout(): logout_user() return redirect(url_for('user.login'))