Python从0到100(四十八):前后端分离架构实践使用Django构建安全的Session验证系统

举报
是Dream呀 发表于 2024/08/28 22:15:10 2024/08/28
【摘要】 Python从0到100(四十八):前后端分离架构实践使用Django构建安全的Session验证系统

在这里插入图片描述

前言: 零基础学Python:Python从0到100最新最全教程 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、 计算机视觉、机器学习、神经网络以及人工智能相关知识,成为学习学习和学业的先行者!
欢迎大家订阅专栏:零基础学Python:Python从0到100最新最全教程!

一、Session机制的深入理解与应用

1.1 Session ID的生成与作用

Django 中 该表 名字就叫 django_session, 如下所示。
在这里插入图片描述
大家可以发现session id (表中的session key)通常就是 一串字符串 用来标记一个session的。 而session对应的数据在这里是加密的。
通过这张表,服务端 可以根据 session号(通常叫session ID) 查到 session 的信息数据。
作用:
1.在用户登录成功后, 服务端就在数据库session表中 中创建一条记录,记录这次会话。
也就是创建一个新的 sessionid 插入到 该表中。
同时也 放入一些 该session对应的数据到 记录的数据字段中,比如登录用户 的 信息。
然后在该登录请求的HTTP响应消息中, 的头字段 Set-Cookie 里填入 sessionid 数据。
类似这样

Set-Cookie: sessionid=6qu1cuk8cxvtf4w9rjxeppexh2izy0hh

根据http协议, 这个Set-Cookie字段的意思就是 要求前端将其中的数据存入 cookie中。 并且随后访问该服务端的时候, 在HTTP请求消息中必须带上 这些 cookie数据
cookie 通常就是存储在客户端浏览器的一些数据。 服务端可以通过http响应消息 要求 浏览器存储 一些数据。
以后每次访问 同一个网站服务, 必须在HTTP请求中再带上 这些cookie里面的数据。
cookie数据由多个 键值对组成, 比如:

sessionid=6qu1cuk8cxvtf4w9rjxeppexh2izy0hh
username=byhy
favorite=phone_laptop_watch

2.该用户的后续操作,触发的HTTP请求, 都会在请求头的Cookie字段带上前面说的sessionid
如下所示:

Cookie: sessionid=6qu1cuk8cxvtf4w9rjxeppexh2izy0hh

服务端接受到该请求后,只需要到session表中查看是否有该 sessionid 对应的记录,这样就可以判断这个请求是否是前面已经登录的用户发出的。
如果不是,就可以拒绝服务,重定向http请求到登录页面让用户登录

在Web开发中,Session ID是用于标识用户会话的关键。在Django框架中,Session ID是通过加密的方式存储在数据库中的。以下是生成Session ID并将其设置到Cookie中的示例代码:

from django.contrib.sessions.backends.db import SessionStore

def create_session(request):
    # 创建一个新的session
    session = SessionStore()
    session['usertype'] = 'mgr'  # 假设用户类型为mgr
    session.save()  # 保存session

    # 将session ID设置到Cookie中
    session_id = session.session_key
    response = HttpResponse("Session Created")
    response.set_cookie('sessionid', session_id)
    return response

1.2 Session的验证流程

在处理API请求时,我们需要验证请求是否来自已登录的管理员。以下是一个验证Session的示例:

from django.http import JsonResponse

def validate_session(request):
    # 检查请求中是否包含sessionid
    if 'sessionid' not in request.COOKIES:
        return JsonResponse({'error': 'No session ID found'}, status=401)

    # 根据sessionid获取session数据
    session = SessionStore(request.COOKIES['sessionid'])
    if 'usertype' not in session or session['usertype'] != 'mgr':
        return JsonResponse({'error': 'Unauthorized'}, status=403)

    # 如果验证通过,继续处理请求
    # ...

二、Token机制的原理与实现

使用session机制验证用户请求的合法性 的主要缺点有两个:

  1. 性能问题
    因为,验证请求是根据sessionid 到数据库中查找session表的,而数据库操作是服务端常见的性能瓶颈,尤其是当用户量比较大的时候。
  2. 扩展性问题
    当系统用户特别多的时候,后端处理请求的服务端通常由多个,部署在多个节点上。 但是多个节点都要访问session表,这样就要求数据库服务能够被多个节点访问,不方便切分数据库以提高性能。

最近比较流行的一种token机制可以比较好的解决这些问题。

token 简单来说,就是包含了 数据信息 和 校验信息的 数据包。

2.1Session|token机制

是把 数据信息(比如session表)放到 服务端,服务端数据是客户无法篡改的,从而保证验证的 可靠性。

而 token机制 数据信息 直接传给 客户端,客户每次请求再携带过来给服务端。服务端无需查找数据库,直接根据token里面的数据信息进行校验。

那么问题来了:客户数据直接发送给客户端,如果 客户端篡改了数据, 比如把自己改为 vip用户怎么办? 服务端怎么验证数据有没有被客户端篡改(术语叫完整性验证)呢?
token 机制的原理如下:
1. 服务端配置一个密钥(secret key),该密钥是服务端私密保存的,不能外泄

2. 在用户登录成功后, 服务端将 用户的信息数据 + 密钥 一起进行一个哈希计算, 得到一个哈希值。
注意:哈希算法保证了, 哈希值只能根据 同样的 源数据得到。如果谁修改了用户信息, 除非他知道密钥,再次使用哈希算法才能得到 正确的新的 哈希值。所以这个哈希值,就是用来校验数据是否被修改的.

然后将 用户数据信息 和 哈希值 一起 做成一个字节串 ,这个字节串就称之为 token 。
大家可以发现 token 里面 包含了用户数据信息 和 用于校验完整性的哈希值。
然后,服务端返回给客户的HTTP响应中 返回了这个token。 通常token是放在HTTP响应的头部中的。 具体哪个头部字段没有规定,开发者可以自行定义。

3. 该用户的后续操作,触发的HTTP API请求, 会在请求消息里面 带上 token 。
具体在请求消息的什么地方 存放 token, 由开发者自己定义,通常也存放在http 请求 的头部中。

服务端接收到请求后,会根据 数据信息 和 密钥 使用哈希算法再次 生成 哈希值。

  • 如果客户修改了数据信息, 因为他不知道密钥,没法得到正确的新的哈希值,那么 服务端根据 篡改后的数据+密钥 得到的新 哈希值一定和 保存在token中的老哈希值 不同。就知道数据被修改了。
  • 如果客户没有修改数据,服务端 根据 原来的数据+密钥 得到的哈希值 和保存在token中原来的哈希值一致,就校验通过。

校验通过后,就确信了数据没有被修改,可以放心的使用token里面的数据 进行后续的业务逻辑处理了。

上述处理中,由于不需要服务端访问查找数据库,从而大大了提高了处理性能

2.2Token的生成与校验

Token机制通过在客户端和服务器之间传递一个包含用户信息和校验信息的数据包来实现身份验证。以下是使用JWT(JSON Web Tokens)生成和校验Token的示例:

import jwt
from datetime import datetime, timedelta

SECRET_KEY = 'your_secret_key'

def generate_token(user_data):
    # 生成Token
    payload = {
        'usertype': user_data['usertype'],
        'exp': datetime.utcnow() + timedelta(days=1)  # 设置Token有效期为1天
    }
    token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
    return token

def validate_token(token):
    # 校验Token
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload['usertype'] == 'mgr'
    except jwt.ExpiredSignatureError:
        return JsonResponse({'error': 'Token expired'}, status=401)
    except jwt.InvalidTokenError:
        return JsonResponse({'error': 'Invalid token'}, status=401)

# 使用示例
def protected_view(request):
    token = request.headers.get('Authorization')
    if not token or not validate_token(token):
        return JsonResponse({'error': 'Unauthorized'}, status=401)
    # Token验证通过,继续处理请求
    # ...

三、Django中的Session验证

在Django中,Session验证可以通过中间件自动处理,但有时我们需要手动验证Session,以下是如何在Django视图中手动验证Session的示例:
在这里插入图片描述
这行代码的作用 就是在登录认证后,将 用户类型保存到session数据中, 也就是存入前面数据库的那张图的 会话数据记录中。
Django 框架 会自动在 HTTP 响应消息头中 加入 类似下面的 sessionid cookie

Set-Cookie: sessionid=???

修改 mgr/customer.pydispatcher 函数:
在这里插入图片描述

在前面加上如下代码:

   # 根据session判断用户是否是登录的管理员用户
    if 'usertype' not in request.session:
        return JsonResponse({
            'ret': 302,
            'msg': '未登录',
            'redirect': '/mgr/sign.html'}, 
            status=302)

    if request.session['usertype'] != 'mgr' :
        return JsonResponse({
            'ret': 302,
            'msg': '用户非mgr类型',
            'redirect': '/mgr/sign.html'} ,
            status=302)

在这里插入图片描述
注意request对象里面的session属性对应的就是 session记录里面的数据。该数据对象类似字典,所以检查是否有usertype类型为mgr的信息,就是这样写:

if request.session['usertype'] != 'mgr' 

四、最佳实践与问题解决

在实际开发中,我们可能会遇到各种问题,例如Session丢失、Token被篡改等。以下是一些最佳实践和问题解决策略:

  • Session安全:使用HTTPS来保护Cookie不被中间人攻击。
  • Token安全:使用强密钥,定期更换密钥,设置Token的有效期。
  • 性能优化:对于Token机制,可以考虑使用缓存来减少对数据库的访问。

五、总结

在Python Web开发中,合理使用Session和Token机制可以有效地保护我们的应用免受未授权访问。通过上述示例和最佳实践,我们可以构建一个既安全又高效的Web应用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。