Python 现代开发学习笔记

(十一)把身份、会话更换与记录权限分开

1. 登录和退出都更换编号

继续加入密码核对与会话更换:

# 续写同一文件:web_project/src/webbook/security.py
# 先查用户,再把输入密码与已保存的哈希比较;返回相同错误信息处理登录失败。
def authenticate(connection, username, password):
    user = connection.execute(
        "SELECT * FROM users WHERE username=?", (username,)
    ).fetchone()
    if user is None or not check_password_hash(user["password_hash"], password):
        raise Problem(401, "账号或密码不正确")
    return user
# 续写同一文件:web_project/src/webbook/security.py
# 登录和退出都先作废旧会话,再生成新编号,旧 Cookie 就不能继续表示当前身份。
def rotate_session(connection, previous, now, user=None):
    connection.execute("DELETE FROM sessions WHERE sid=?", (previous.sid,))
    return new_session(connection, now, user)

登录成功时删除旧匿名会话,创建关联用户的新会话;退出时删除旧登录会话,换成匿名会话。旧编号因此不能继续代表刚才的身份,新的 csrf 令牌也随会话更换。

验证密码后删除s1并建立关联alice的s2,退出删除s2再建立匿名s3,旧编号均不能继续使用

2. 找到账号后,再判断这一条记录

身份检查回答“这个请求由谁操作”,记录权限回答“这个人能不能操作这本书”。bob 即使知道 alice 的书籍编号,也要经过 owned_book;数据库里那条书籍的 user_id 不匹配,就返回 403。

页面隐藏按钮可以让界面更清楚,服务器的查询与更新分支仍然会核对会话和归属。新增数据的 owner 参数由会话取得,JSON 里额外写一个 user_id 不会替换它。

# 项目文件:demos/session_rotation.py
# 登录与退出都更换会话编号;旧编号和已经过期的会话不能继续代表登录身份。
from webbook.lab import lab_client, login

with lab_client() as (client, app, _database):
    client.get("/api/session")
    old_sid = client.cookies.get("reading_sid")
    token = login(client)
    print("登录换编号", old_sid != client.cookies.get("reading_sid"))
    print(
        "旧编号",
        client.get(
            "/api/books", headers={"Cookie": f"reading_sid={old_sid}"}
        ).status_code,
    )
    client.post("/logout", data={"csrf_token": token})
    print("退出后", client.get("/api/books").status_code)
    login(client)
    now = app.now()
    app.now = lambda: now + 3601
    print("过期后", client.get("/api/books").status_code)

输出中,登录前后编号不同,旧编号、退出后的会话、超过过期时间的会话都不能继续读取书籍,状态为 401。已登录但操作其他账号的记录则为 403。

会话先找到bob,再对照书籍属于alice,归属不匹配时在SQL更新前停止,不能只看按钮显示

笔记正文与全部题解 · Ctrl / ⌘ K