跳转至

仿优酷

储备知识

我们得弄清 cookies、session、token 到底是什么?!
参考文档: https://www.cnblogs.com/liuqingzheng/articles/8990027.html

何为会话?
会话,广义的含义是指有始有终的一系列动作/消息.
在web中,会话对象用来存储特定用户会话所需的属性及配置信息!

HTTP协议是无状态协议?
无状态协议即无法保持会话之间的状态.服务端不晓得客户端是什么状态.
HTTP请求是无状态的,每次都是新的请求!
举个例子,成功登陆一个网站,当访问该网站的其它网页时,该登陆状态会消失!需要重新登陆一次.
此时,我们需要将对应的会话信息(eg:登陆成功的信息等)保存下来 -- Cookie  Session!

会话信息的保存:
    cookie 客户端  ;  session 服务端  ;  token 客户端
    注意.不管是cookie还是token,只要保存在客户端的信息,我们都统称为cookie信息.

无cookie时期 -- 静态网站
cookie认证时期 -- 在线购物网站需要登录的网站等
    当一个浏览器访问某web服务器时
    web服务器会在响应头中添加一个名叫Set-Cookie的响应字段,用于将Cookie返回给浏览器
    当浏览器第二次访问该web服务器时会自动的将该cookie回传给服务器,来实现用户状态跟踪!!
    弊端: 
       尽管浏览器发送登陆信息(pwd)会加密发送到服务端,
       进而服务端返还给客户端的cookie中包含的是客户端/浏览器加密的密码..
       但cookie中始终包含着pwd这些用户的敏感信息. 
       不管是用户首次登陆发送给S端的数据包,还是C-->S的cookie,黑客截获后可以通过撞库破解.
    为了降低风险,我们可以将C-->S的cookie信息中不包含pwd等用户的敏感信息
cookie+session认证时期
    打个比方,将自己比作服务端,给大家发一个会话标识(session id),说白了就是一个随机的字串
    每个人收到的都不一样,每次大家向我发起HTTP请求的时候,把这个session字串放到cookie中一并捎过来
    我在服务端将其与存储的session进行比对,这样我就能区分开谁是谁了!
    弊端:
        1> 每个人只需要保存自己的session id,而服务器要保存所有人的session id 
           随着访问服务端的用户增多,S端存储的session也会变多!
        2> 若同时有10万个并发访问S端,S端收到了10万份包含了session的cookie,需要一一进行比对..
        3> 倘若用两个机器组成了一个集群,小F通过机器A登录了系统,那session id会保存在机器A上
           假设小F的下一次请求被转发到机器B怎么办?机器B可没有小F的 session id啊.
           限制了集群的水平扩展.
           解决办法: 
              session sticky
                就是让小F的请求一直粘连在机器A上,但是这也不管用,要是机器A挂掉了,还得转到机器B去.
              session的复制
                把session id在两个机器之间搬来搬去!
              session的集群
                将多台服务器的session集中管理,放到一处,并对该session做集群,避免单点故障.
Token 
服务端为什么要保存这可恶的session呢,只让每个客户端去保存该多好!
So,Token用计算换取了空间!
    小F已经成功登录系统,我给他发一个令牌(token),里边包含了小F的user id,并对该数据做一个签名
       比如说我用HMAC-SHA256算法,加上一个只有我才知道的密钥,对数据做一个签名
     这个签名和数据一起作为token `即签名+数据=token` 发送给C端,由于密钥别人不知道,就无法伪造token了!!
    这个token我不保存,当小F把这个token给我发过来的时候,我再用同样的HMAC-SHA256算法和同样的密钥
    对数据再计算一次签名和token中的签名做个比较
    若相同,我就知道小F已经登录过了,并且可以直接取到小F的user id;
    若不相同,数据部分肯定被人篡改过,我就告诉发送者:对不起,没有认证!
Ps:Token移动端也可以用!

仿优酷项目

该项目涵盖内容: 线程池, 锁机制, session验证机制, 简易版orm, 大文件md5校验, 数据库操作

系统架构

Ps: 偷个懒吧,不想画图!!直接拍照完事. 随缘画法.

创建表

用sql语句 or 用工具创建表

Ps: 加` `是为了表明它不是关键字,若不加,美化后,它就变成大写的了!
用工具创建类型为timestamp的字段时,若勾选"根据当前时间更新",添加和修改数据时,都会更新时间!

-- 关掉外键约束,表明不用做外键检查,两表有外键关联,也能删除!
set foreign_key_checks = 0; 
-- 若表已存在,删除.
drop table if exists user_info,movie,notice,download_record; 


-- 用户表
CREATE TABLE user_info (
    id INT PRIMARY KEY NOT NULL auto_increment,
    `name` VARCHAR ( 32 ),
    `password` VARCHAR ( 64 ),
    is_vip INT,                 -- 是否是会员 
    locked INT,                 -- 是否锁定
    user_type VARCHAR ( 32 )    -- 管理员还是普通用户
) ENGINE = INNODB,
charset = 'utf8';


-- 视频表
CREATE TABLE movie (
    id INT PRIMARY KEY NOT NULL auto_increment,
    `name` VARCHAR ( 32 ),
    `path` VARCHAR ( 255 ),
    is_free INT DEFAULT 0,      -- 是否收费
    is_delete INT DEFAULT 0,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,    -- 自动添加当前时间戳为上传时间.
    user_id INT,                -- 谁上传的视频
    file_md5 VARCHAR ( 64 ) 
) charset = 'utf8';


-- 公告表
CREATE TABLE notice ( 
    id INT PRIMARY KEY NOT NULL auto_increment, 
    `name` VARCHAR ( 64 ), 
    content VARCHAR ( 255 ), 
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 
    user_id INT
) charset = 'utf8';


-- 下载记录表
CREATE TABLE download_record ( 
    id INT NOT NULL PRIMARY KEY auto_increment, 
    user_id INT,
    movie_id INT
) charset = 'utf8';
搭建项目目录
.
├── youkuClient
   ├── conf
      └── setting.py     
   ├── core
      ├── admin.py       # -- 管理员视图相关功能函数
      ├── src.py         # -- 主视图
      └── user.py        # -- 用户视图相关功能函数
   ├── lib
      └── common.py      # -- 存放公共方法
   ├── start.py           # -- 启动文件
   └── tcpClient
       └── tcp_client.py  # -- 客户端连接
└── youkuServer
    ├── conf
       └── setting.py
    ├── db
       └── models.py            # -- 数据库表对应程序中的类
    ├── interface
       ├── admin_interface.py   # -- 管理员相关操作的接口
       ├── common_interface.py  # -- 公共操作的相关接口(登录,注册)
       └── user_interface.py    # -- 用户相关操作的接口
    ├── lib
       └── common.py
    ├── start.py
    └── tcpServer
        └── tcp_server.py


.
├── youkuClient
   ├── conf
      └── setting.py     # -- 配置信息相关
   ├── core
      ├── admin.py       # -- 管理员视图相关功能函数
      ├── src.py         # -- 主视图
      └── user.py        # -- 用户视图相关功能函数
   ├── download_movie     # -- 存放下载完的电影
   ├── lib
      └── common.py      # -- 存放公共方法
   ├── start.py           # -- 启动文件
   ├── tcpClient          # -- 存放要上传的电影
      └── tcp_client.py  # -- 客户端连接
   └── upload_movie
└── youkuServer
    ├── conf
       └── setting.py
    ├── db
       └── models.py
    ├── interface
       ├── admin_interface.py   # -- 管理员相关操作的接口
       ├── common_interface.py  # -- 公共操作的相关接口(登录,注册)
       └── user_interface.py    # -- 用户相关操作的接口
    ├── lib
       └── common.py
    ├── movie_list
    ├── orm_pool                 # -- 简易版的ORM框架
       ├── db_pool.py
       ├── mysql_conn.py
       └── todo_orm.py
    ├── start.py
    └── tcpServer
        ├── tcp_server.py        # -- 服务端核心代码
        └── use_data.py          # -- 存放用户信息和全局锁
ORM框架
    > 定义字段类表示表中的列
    > 实现不用写__init__,类实例化时可以传任意类型的k=v,把参数放到实例对象的体内 -- 字典
    > 实现字典对象通过 . 访问到属性 -- __getattr____setattr__
    > 用一个类表示数据库当中的一个表,所有的类都得具备表的几个属性(表名主键一堆字段)
      但这些属性的值每张表都不相同.通过控制类的创建过程,让类创建好后就有这几个东西 -- 元类
    > 不写sql 类对应数据库当中的表,类的一个实例对应表中的一条数据
      查询所有数据 -- 类方法
      更新新增 -- 实例绑定方法
      (构建sql语句pymysql接口返回结果)
    > 数据库连接池