hacker0limbo / my-blog

个人博客
5 stars 1 forks source link

session 和 cookie 整理 #3

Open hacker0limbo opened 5 years ago

hacker0limbo commented 5 years ago

session 和 cookie 总结

直接使用cookie也是可以追踪用户的, 但是不安全, 所以比较好的做法为, 在第一次登录以后, 往客户端发送一个 cookie, cookie 里面的 value 设置为session_id, 发送请求到服务器的时候, 服务器有一个 session 使用[session_id: username]来验证发送过来的cookie

这样的好处是:

具体流程为:

1, 第一访问登录页面, 提交登录表单信息
2, 服务端得到用户提交的登录数据, 设置: 
  session, session[session_id] = user.username
3, 服务端返回 response 带上 cookie 信息:
  headers['Set-Cookie'] = f'user={session_id}'
4, 一个 current_user 函数, 可以用户从用户的 cookie 里得到 session_id, 与服务端匹配, 验证用户:
  session_id = request.cookies.get('user', '')
  username = session.get(session_id, '游客')
5, 如果对应的 session_id 匹配成功, 说明用户处于登录状态, 可以被追踪, 否则用户没有登录
6, 用户如果关闭浏览器, cookie 不受影响一直保存在浏览器里(除非过期), 下次打开页面可以直接免登录
7, 只要服务器不关闭, 那么 session 里面的用户信息是永久保存的, 只要客户端设置了 cookie, 验证成功那么该用户就可以被追踪, 即处于登录成功的状态

示例代码可以参见: cookie&session

模拟代码

from bottle import route, run, response, request, redirect

login_user = {} # 就是 session

@route('/login')
def login():
    key = hash("test password") 
    login_user[key] = "test password"
    response.set_cookie('session_id', str(key)) # 设置Cookie值, 下次可以免登录
    return 'login successfuly!'

@route('/logout')
def logout():
    key = request.get_cookie('session_id')
    login_user.pop(int(key), None) # 删除 session 里面的用户, 这样无法在服务端验证
    return 'logout successfuly!'

@route('/logintest')
def logintest():
    key = request.get_cookie('session_id') # 获取Cookie值
    if key is not None and int(key) in login_user: # 看看 session 字典里存的有没有用户
        return 'login test successfuly!'
    else :
        return redirect('/beforelogin')

@route('/beforelogin')
def beforelogin():
    return 'please login!'

run(host='localhost', port=8080, debug=True)

flash message 原理

需求

客户端提交登录登录表单, 如果成功重定向到首页, 否则停留(重定向)在登录页面, 并显示"登录失败"

原理如下

使用 session

对于 login controller, 需要知道客户端显示登录错误, 还是显示登录成功?

也就是 lgoin:post controller 需要给 redirect 后的 login:get controller 发送一个消息告诉他上次登录失败, 两个 controller 直接无法直接发送消息

由于可以在服务端内部共享消息, 可以将用户的动作存储在 session 里, 具体为:

login:post 发送登录失败就在对应 session 里写入一个标记, redirect 到的 login:get 渲染页面的时候检查是否有登录失败标记, 如果有就在渲染的页面里面取出标记对应的信息, 然后在这个将这个标记从这个 session 里面清楚, 伪代码可以为:

session['操作'] = 'msg'
session.get('操作', '') # 获取该操作对应提示信息
session.pop('操作') # 删除该操作以及信息

使用 cookie

有时候用户可能没有正确的 redirect 到 login:get, 那么这个 flash message 还是存在 session 里面, 也就是说用户下次访问 login:get 会提示登录错误, 这样是不合理的

可以使用 cookie, 做法为:

login:post 检测发现错误了就给客户端加一个 cookie 并且发送 redirect 到 login:get, 也就是说这个 cookie 只有 redirect 成功以后才会被客户端带上, 那么重定向以后只要检查是否带有这个 cookie, 有就渲染出对应的错误消息. 即使 redirect 没有成功, 那么附带的 cookie 也无法正确被发送过去, 因而也不会显示错误信息

当然匹配了 cookie 以后需要手动设置 cookie 过期时间清除 cookie, 防止下次访问该页面出现同样的 flash message

参考