import os from flask import Flask, session, redirect, escape, request app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY', default=None) @app.route('/') def index(): if 'username' in session: return 'Logged in as %s' % escape(session['username']) return 'You are not logged in' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect('/') return ''' <form method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form> ''' @app.route('/logout') def logout(): session.pop('username', None) return redirect('/')
$ flask run -h 0.0.0.0 -p 5000 --reload
然后转到 http://your_flask_endpoint:5000,在这里,您会看到您的应用程序正在运行。您需要按照要求进行操作,用 EC2 实例的公有 DNS 名称替换 your_flask_endpoint。
有三种路由可供选择:“/”、“/login”和“/logout”。依次访问它们,熟悉其基本行为。
完成模块所需时间:60-90 分钟
示例使用 Cookie 存储用户名。首次访问应用程序时,会话对象中并没有存储用户名。转到“/login”并提供一个用户名后,值存储在签名的 Cookie 中,而您将重定向至主页。然后,如果您转到“/logout”,则用户名将从会话中删除,而您将回到最初的位置。
您将通过服务器端会话扩展此基本应用程序,不过请先确保您可以连接到 Redis。
在示例中,每次提及 Redis 终端节点时,都应使用主终端节点的主机名。
syntax: shell
$ export REDIS_URL="redis://your_redis_endpoint:6379"
syntax: shell
$ python
syntax: python
>>> import redis >>> client = redis.Redis.from_url('redis://your_redis_endpoint:6379') >>> client.ping() **True**
如果挂起,说明您被“安全组”设置阻止。验证 EC2 实例是否可以访问分配给 ElastiCache 实例的安全组。例如,假设您的 EC2 实例已分配给默认安全组。现在,您可以修改 Amazon ElastiCache 实例的安全组,并添加一个自定义 TCP 规则,以允许来自默认安全组中任何实例的端口 6379 上的连接:
我们再回到示例中来。现在,您将了解一些小的更改,这些更改可帮助您将会话数据存储在 Redis 中。以下是实施了这些更改的另一个示例:
syntax: python
import os import redis from flask import Flask, session, redirect, escape, request app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY', default=None) # BEGIN NEW CODE - PART 1 # REDIS_URL = os.environ.get('REDIS_URL') store = redis.Redis.from_url(REDIS_URL) # END NEW CODE - PART 1 # @app.route('/') def index(): if 'username' in session: # BEGIN NEW CODE - PART 2 # username = escape(session['username']) visits = store.hincrby(username, 'visits', 1) return ''' Logged in as {0}.<br> Visits: {1} '''.format(username, visits) # END NEW CODE - PART 2 # return 'You are not logged in' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect('/') return ''' <form method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form>''' @app.route('/logout') def logout(): session.pop('username', None) return redirect('/')
现在,已导入 Redis 并初始化,索引负责增加一个计数器并向用户显示。
请注意,用于增加计数器的 Redis 命令是 HINCRBY。这是因为您正在为每个用户的会话使用 Redis 哈希。
现在,您要为服务器端会话设置生存时间 (TTL)。它只是多了一行代码,这个代码段将只包含 / route。
syntax: python
... @app.route('/') def index(): if 'username' in session: username = escape(session['username']) visits = store.hincrby(username, 'visits', 1) # BEGIN NEW CODE # store.expire(username, 10) # END NEW CODE # return ''' Logged in as {0}.<br> Visits: {1} '''.format(username, visits) return 'You are not logged in' ...
存储会话的键将在 10 秒后过期。这是一个非常短的 TTL,仅在此示例中有用。如果立即运行应用程序,您将看到访问索引如何更新“访问”计数器,但是如果您不访问该页面,而是等待 10 秒钟,则下次刷新时,计数器将返回 1。
这个简短的示例足够您了解服务器端会话的基础知识,但是在现实情况中,您的操作会有所不同:对用户进行身份验证后,您将生成并存储令牌,而不是存储用户名。该令牌将代表 Redis 键,所有服务器端会话数据都将存储在该键下。您可能还想抽象化此处介绍的某些行为,以便对服务器端会话的任何访问都可以刷新 TTL,而无需干预。在存储库中,有一个示例提供了一个抽象化该行为的 SessionStore 类。