鉴权是网络安全的关键环节,主要用于确认请求者的身份,并确保其有权限执行请求的操作。本文介绍access_token、AK/SK、session/cookie
access_token是一种临时的访问令牌,用于在服务端验证客户端的身份。这种方式通常涉及以下几个步骤:
access_token适用于需要快速且频繁的身份验证场景,尤其是在Web和移动应用程序中。由于其具有固定的有效期,这种方法可以有效减少重复的身份验证过程,提高系统效率。
一个简单的 access_token 示例,实际应用中应更复杂且安全
ACCESS_TOKEN = "my_secret_token" @app.route('/api/data', methods=['GET']) def get_data(): # 从请求头中获取 access_token token = request.headers.get('Authorization') if token != ACCESS_TOKEN: return jsonify({'error': 'Unauthorized'}), 401 # 如果验证通过,返回数据 return jsonify({'data': 'Here is your data'}) 在这个例子中,当客户端访问 /api/data 时,必须在请求头中提供正确的 Authorization 字段,其值应为 “my_secret_token”,才能成功获取数据。
AK(Access Key)和SK(Secret Key)是一对密钥,用于通过加密签名的方式进行身份验证。这种方式通常包括以下几个步骤:
在对称密钥系统中,相同的密钥(在此情景中为 SK,即 Secret Key)用于加密和解密数据。AK 是用来标识用户的,而 SK 用来生成和验证请求的签名。具体来说:
签名生成:当用户想要发送一个请求到服务器时,会利用 SK 对请求信息(如请求方法、URI、时间戳等)进行加密,生成一个独一无二的签名(Signature)。
签名验证:服务器收到请求后,也会用相同的 SK 对请求信息进行同样的加密操作。服务器生成的签名如果与用户发送的签名一致,那么认为请求是合法的,因为只有掌握正确 SK 的用户才能生成正确的签名。
AK/SK签名鉴权适用于对安全性要求较高的场景,尤其是在对数据完整性和请求的来源进行校验时。这种方法通过加密签名确保请求在传输过程中未被篡改,同时验证了请求者的身份。
ACCESS_KEY = "my_access_key" SECRET_KEY = "my_secret_key" @app.route('/api/secure-data', methods=['GET']) def secure_data(): client_ak = request.headers.get('AK') client_signature = request.headers.get('Signature') # 构造待签名的字符串 message = request.path + ACCESS_KEY # 使用 SK 对 message 进行 HMAC-SHA256 签名 signature = hmac.new(SECRET_KEY.encode(), message.encode(), hashlib.sha256).hexdigest() if client_ak != ACCESS_KEY or client_signature != signature: return jsonify({'error': 'Unauthorized'}), 401 return jsonify({'secure_data': 'Here is your secure data'}) 在Web开发中,Session 和 Cookie 是用来维持用户状态和进行身份验证的常用技术。理解它们的工作原理是保证Web应用安全的重要一环。
Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。Cookie 主要用于以下几个方面:
安全实践:
Session 是另一种服务器端的数据存储方案,用于存储有关用户会话的信息。不同于Cookie直接存储在客户端,Session数据默认存储在服务器上。当用户在应用中进行操作时,服务器会创建一个会话,并发给客户端一个唯一的标识符(通常名为 Session ID),而这个标识符通常通过Cookie发送给客户端,保存在用户的浏览器中。
工作原理:
安全实践:
在 Flask 中,session 的实现通常依赖于客户端的浏览器和服务器之间的交互方式,具体表现为以下两种主要存储机制.默认情况下,Flask 在客户端浏览器中存储加密的 session 信息。如果需要更高的安全性或存储大量的会话数据,可以通过扩展使用服务器端 session 存储
在 Flask 的默认配置中,session 信息是存储在客户端的浏览器中的。这些信息以加密形式存放在一个名为 session cookie 的 cookie 中。服务器通过密钥(SECRET_KEY 配置项)对这些信息进行签名,确保在客户端返回给服务器时能够验证其完整性和未被篡改。
session数据加密后保存在一个 cookie 中。cookie 并在后续请求中发送给服务器。cookie 后,会解密并验证 session 数据,用于确认用户状态或身份。这种方式的优点是简单且易于实现,但安全性依赖于 SECRET_KEY 的保密性和 cookie 的安全设置(如使用 HTTPS、设置为 HttpOnly 等)。
虽然 Flask 默认使用基于 cookie 的 session 存储,你也可以配置 Flask 使用服务器端 session 存储机制。这通常通过第三方扩展如 Flask-Session 实现。在服务器端 session 存储中,session 数据存放在服务器端的存储系统中(如数据库、缓存系统等),只有一个唯一标识符存储在客户端的 cookie 中。
session ID。session ID 来查询存储系统中的 session 数据。使用服务器端存储可以增强安全性,并允许存储更多的数据,因为不受浏览器 cookie 大小限制。但这也可能增加服务器端的复杂性和资源需求。
def login_required(view): @functools.wraps(view) def wrapped_view(**kwargs): if g.user is None: return redirect(url_for("auth.login")) return view(**kwargs) return wrapped_view @bp.before_app_request def load_logged_in_user(): user_id = session.get("user_id") if user_id is None: g.user = None else: g.user = ( get_db().execute("SELECT * FROM user WHERE id = ?", (user_id,)).fetchone() ) @bp.route("/register", methods=("GET", "POST")) def register(): if request.method == "POST": username = request.form["username"] password = request.form["password"] db = get_db() error = None if error is None: try: db.execute( "INSERT INTO user (username, password) VALUES (?, ?)", (username, generate_password_hash(password)), ) db.commit() except db.IntegrityError: # The username was already taken, which caused the # commit to fail. Show a validation error. error = f"User {username} is already registered." else: # Success, go to the login page. return redirect(url_for("auth.login")) flash(error) return render_template("auth/register.html") @bp.route("/login", methods=("GET", "POST")) def login(): """Log in a registered user by adding the user id to the session.""" if request.method == "POST": username = request.form["username"] password = request.form["password"] db = get_db() error = None user = db.execute( "SELECT * FROM user WHERE username = ?", (username,) ).fetchone() if user is None: error = "Incorrect username." elif not check_password_hash(user["password"], password): error = "Incorrect password." if error is None: # store the user id in a new session and return to the index session.clear() session["user_id"] = user["id"] return redirect(url_for("index")) flash(error) return render_template("auth/login.html") 存储位置:
安全性:
使用场景:
目的:
实现方式:
安全性: