Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

Virtualenv简易教程

发表于 2018-10-09 | 更新于 2021-06-07

virtualenv是一个创建隔离python环境的工具,主要用于解决包冲突问题。

安装方法

1
sudo pip3 install virtualenv -i https://mirrors.aliyun.com/pypi/simple/

使用方法

创建项目的虚拟环境

1
2
$ cd my_project_folder
$ virtualenv venv #venv是虚拟环境名称

执行上述命令后,将生成一个与虚拟环境同名的文件夹,包含 Python 可执行文件和 pip 库的拷贝,可用于安装其他包。

但是默认情况下,虚拟环境中不会包含也无法使用系统环境的global site-packages。比如系统环境里安装了 requests 模块,在虚拟环境里import requests会提示ImportError。如果想使用系统环境的第三方软件包,可以在创建虚拟环境时使用参数–system-site-packages。如下所示:

1
virtualenv --system-site-packages venv

此外可以指定虚拟环境所使用的 Python 版本,但前提是系统中已经安装了该版本:

1
virtualenv -p /usr/bin/python2.7 venv

使用虚拟环境

启动虚拟环境:

1
2
3
cd venv
source bin/activate
python -V

退出虚拟环境:

1
deactivate

virtualenvwrapper

virtualenvwrapper是virtualenv 的扩展工具,提供了一系列命令行命令,可以方便地创建、删除、复制、切换不同的虚拟环境。同时,使用该扩展后,所有虚拟环境都会被放置在同一个目录下。

安装方法

1
pip3 install virtualenvwrapper

环境配置

在$home/.bashrc文件中添加如下几行代码:

1
2
3
4
5
# bash -f file检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true
if [ -f /usr/local/bin/virtualenvwrapper.sh ]; then
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh
fi

使用方法

mkvirtualenv 也可以使用 virtualenv 的参数,比如 –python 来指定 Python 版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mkvirtualenv venv # 创建虚拟环境,虚拟环境目录都在 WORKON_HOME 里

lsvirtualenv -b # 列出虚拟环境

workon [虚拟环境名称] # 切换虚拟环境

lssitepackages # 查看环境里安装了哪些包

cdvirtualenv [子目录名] # 进入当前环境的目录

cpvirtualenv [source] [dest] # 复制虚拟环境

deactivate # 退出虚拟环境

rmvirtualenv [虚拟环境名称] # 删除虚拟环境

conda vs. pip vs. virtualenv

Conda是一个开源包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。 它适用于Linux,OS X和Windows,是为Python程序创建的,但可以打包和分发任何软件。

Pip是一个以Python计算机程序语言写成的软件包管理系统,用于安装和管理软件包。

它们直接的区别如下:

conda vs. pip vs. virtualenv

pip使用方法

1
2
3
4
# 导出python安装包环境
pip freeze > requirements.txt
# 导入requirements文件
pip install -r requirements.txt

参考文献

  1. 最全的Python虚拟环境使用方法,by Stevent.
  2. Pipenv:新一代Python项目环境与依赖管理工具,by 李辉.
  3. Python 修改 pip 源为国内源,by 花晓星辰.

Blender简易教程

发表于 2018-09-19 | 更新于 2024-11-23

最近需要用Blender建3D模型,试用了半天,将经验简单总结一下。

3D建模流程

  • 建立3D模型:通常在Blender的3D视图窗口对物体进行建模。
  • 拆分制作纹理:设置缝合边,再展开。打开U/V图像编辑器窗口即可看到拆分效果。导出图片后制作纹理。
  • 使用材质贴图:设置材质着色,添加纹理。

Blender快捷键

Blender快捷键很多,熟练使用可加速3D模型的建立。

3D视图窗口

字母键

  • A:全选、取消选择
  • Shift+A:增加物件,新建的物件会出现在准星的位置
  • B:框选,加Shift取消选择,按Esc退出选择
  • C:刷选,加Shift取消选择,按Esc退出选择
  • Shift+C:将游标设置到坐标系中心
  • Ctrl+Shift+C:为骨头添加约束
  • Shift+D:复制对象
  • E:挤出选中对象
  • Shift+E:编辑模式下,打开对称镜像挤出骨架
  • F:编辑模式下,在点的基础上构建边或面;在权重绘制模式下,设置笔刷大小
  • G:移动选中对象
  • Alt+G: 在Pose Mode下,按“Alt+G”骨架归位
  • H:隐藏对象(Hide)
  • Alt+H:显示对象
  • I:增加关键帧
  • Ctrl+I:物体模式下,反选所有未选择的物体
  • Shift+I:在Pose Mode下,选中骨架,再按“Shift+I”插入反关节
  • Ctrl+J: 在Pose Mode下,连接两个独立的骨架,以便在编辑模式下同时编辑;在Object Mode下,选择两个物件后合并
  • K: 切割,按“enter”键完成切割。按“k”键接着按住“ctrl”键移动到线框上可以自动定位到线的二分之一处。按“k”键接着按一下“c”键可以水平垂直或者45度进行切割。按“k”键接着按一下“z”键可以对物体一圈进行切割。
  • Ctrl+L:编辑模式下,选择一个点,在Ctrl+L将选择与点相连的连续表面
  • M:选中物体,按“M”键,再选择要将物体放置的图层。
  • N:打开物体属性
  • Ctrl+P:编辑模式下,设置多个骨头的父骨头;或者物体模式下,设置多个物体的父级。
  • Alt+P:物体模式下,取消物体的父子关系。
  • R: 旋转
  • Alt+R: 在Pose Mode下,按“Alt+R”骨架归位
  • Ctrl+R: 环切并滑动
  • S: 缩放
  • S+XYZ:沿XYZ轴方向缩放
  • Alt+S:法向缩放
  • T:打开左侧工具栏
  • U:编辑模式下按“U”,打开UV映射菜单,选择活动面再全选,然后UV展开
  • Ctrl+U:存储为启动文件
  • W:快捷拓展命令栏
  • X:删除
  • Z:线框模式与实体模式之间切换
  • Shift+Z:视口着色切换到实时渲染模式
  • ]:在Pose模式下,选择主骨头,再按“]”可直接选择下一根骨头
  • Shift+]:在Pose模式下,选择主骨头,再按“Shift+]”可直接选择下一根骨头
  • Ctrl+Space:调出三维坐标系操纵物体
  • Ctrl+Tab:进行点、线、面的编辑模式
  • Ctrl+Alt+Q:切换四视图
  • Ctrl+向上箭头:最大化对应视图
  • Shift+空格键:放大视图
  • Tab:编辑模式和物体模式切换
  • Ctrl+句号.:仅变换原点

功能键

  • F12:计算结果(算图模式),如果是黑色的,需要把摄像机和光源放在同一个图层中,或者选择摄像机和光源的图层。

数字键

数字键主要用于视角切换

  • 1:前视图
  • 3:侧视图
  • 5:正交与透视之间切换
  • 7:俯视图

鼠标键

  • 右键:选择物体
  • shift+中键:平移
  • 滚轴/(Ctrl+中键拖拽):缩放

参考文献

  1. Blender常用快捷键,by JinunMeng.
  2. 如何系统的学习blender?,by zhihu.
  3. 贴图、纹理、材质的区别是什么?,by zhihu.
  4. 材质、贴图、纹理的区别,by MATU.
  5. 材质和纹理的区别,by zhuyong006.
  6. 一文看懂材质/纹理 Material, Texture, Shading, Shader 的区别,by kidult00.
  7. Blender 移动、旋转、缩放,by heavi.
  8. 数字人轻松学习Blender系列之七:坐标,by DIGITALMAN.
  9. 【转载】Blender 常用快捷键大全,by ShaderJoy.

Windows下将cmd命令添加到右键菜单

发表于 2018-09-11

经常需要使用cmd命令,如果打开cmd窗口后在cd到目标路径,则非常麻烦。可使用如下方法将cmd添加到右键菜单,这样一打开cmd窗口就到了目标路径。设置方法如下:

  1. 打开注册表
  2. 添加注册项
    1
    2
    3
    a) 找到[HKEY_CLASSESS_ROOT\Folder\shell]子键,在其下新建“cmdPrompt”子项,在窗口右侧名称列上点击右键修改,将数值数据改为“CMD快速通道”。

    b) 再在这个项下,新建名为“command”子项,同样修改数值数据修改为c:\windows\system32\cmd.exe /k cd "%1"
    设置完成后即可在目标路径文件夹上右键单击,选择“CMD快速通道”命令,打开cmd窗口的同时,已跳转到目标路径下。

Windows平台Python安装与配置教程

发表于 2018-09-07 | 更新于 2026-01-24

下面记录在Windows平台安装配置Python的过程。

  1. 去Python官网下载最新的Python3,当前最新版本为3.7。
  2. 双击Python3.7的安装包进行安装,设置安装路径为C:\Python\Python37,同时将Python路径加入系统Path变量中。
  3. 使用如下命令安装ipython。加参数–trusted-host,否则会报错误:SSLError(SSLCertVerificationError(1, ‘[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed。加参数-i,制定国内pip源,显著提高下载速度。
    1
    pip install ipython -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn

pip 使用方法

Python 查看自己的第三方库默认安装路径:

1
2
3
python -m site
pip list
pip show pip

参考链接

  1. python pip安装的包放在哪里(site-packages目录的位置),by 西京刀客.
  2. pip 常用指令 pip show 命令用法介绍,by 团圆吧.

Windows与Linux双系统Grub引导修复

发表于 2018-09-06 | 更新于 2018-11-20

因为个人爱好,我再笔记本中安装了Windows和Linux的双系统。最近因为Windows系统故障原因,重装Windows系统,导致无法引导Linux启动。

故障原因是之前安装双系统时,先安装Windows,再安装Linux,这样将由Grub2来引导Linux和Windows的启动。而重装Windows的过程中,将覆盖Grub2引导程序,导致无法Linux的启动。

故障解决方法是重新在硬盘中写入Grub2引导程序。具体步骤如下:

  1. 使用Linux安装盘,使用Live CD方式进行Linux系统。
  2. 使用fdisk -l命令查看硬盘信息。
  3. 使用grub-install –root-directory=/media/boot /dev/sda将grub2引导程序写入硬盘。/media/boot为引导分区所在位置,grub-install写入grub2引导程序时需要使用引导分区所在linux映像。
  4. 使用update-grub2更新引导配置。

中文文献免费下载方法

发表于 2018-08-26 | 更新于 2018-09-09

下面记录经过验证可用的中文文献下载方法:

  1. idata中国知网
    1
    2
    3
    4
    idata中国知网网址:https://www.cn-ki.net/
    进入系统,注册账号,登陆就可以每天免费下载五篇知网论文。
    额度用完之后,第二天可以继续下载。
    每天五篇的额度基本能满足需要,这样还不够的话,可以多注册几个账号。
  2. 全国图书馆参考咨询联盟
    1
    2
    3
    全国图书馆参考咨询联盟网址:http://www.ucdrs.superlib.net/
    很全,可以查图书,查期刊,报纸等。还有硕士,博士的毕业论文。不仅有中文,还有外文。
    网站无需注册,通过文献传递服务,即通过邮箱接收全文。最快一两分钟,最慢三四个小时即可接收到全文。
  3. 上海研发公共服务平台
    1
    2
    3
    上海研发公共服务平台网址:http://www.sgst.cn/。
    注册后可直接下载,论文、文献数量直接匹配知网和万方数据。
    下载速度较快,但有数量限制,可注册多个用户名。
  4. 库问搜索
    1
    2
    库问搜索网址:http://www.koovin.com/。
    库问搜索提供千万级文献免费下载。

C++跨平台移植开发思考

发表于 2018-07-16 | 更新于 2022-07-17

最近需要将一份C++代码通过Emscripten编译成asm.js,结果失败,因为这份C++代码使用MFC GUI库,导致Emscripten编译失败。这引起了我对C++跨平台移植的思考。

C++代码理论上能实现源代码级别的跨平台移植,即同一份代码可以不加修改的在不同的平台上编译运行且表现一致。这要求编写的C++代码符合跨平台移植的规范,如链接[1]所指出的那样,它给出了很多实现C++代码跨平台移植的编码细节,在编写跨平台的底层库方面十分有用。但在编写跨平台的C++应用方面,应使用经过长期实践证明能用的跨平台库来帮助编码,建立在巨人的肩膀上,使跨平台C++代码编写工作变得简单容易。下面给出一些常用的跨平台C++库供参考:

  1. 语言及基础库
  • 标准 C++:标准 c++ 是98年制定的,现在主流的 c++ 编译器都能够比较好的支持了。这里建议使用VC7.1和GCC4.0及以上版本。在Linux中,glibc是标准C的实现,libstdc++则是标准C++的实现。在Windows中,VS2015之前MSVCRT.DLL是标准C/C++的实现,之后UCRTBASE.DLLz则是标准C/C++的实现。
  • boost:boost 则是 C++ 标准委员会的一群人弄起来的一个 C++ 库集合,其中不少库以经进入 C++ TR1,可以说是准标准。使用这里的库我们有着充分的理由。象字符串的操作可以用 boost 的 String algorithms 库,格式化操作可以用 boost::format,正则式可用 boost::regex 等等。
  1. 网络
  • ACE(Adaptive Communication Environment):ACE是一个以C++的Template技术所做成的开放源代码的可跨平台的网络应用程序的程式库套件。ACE自适配通信环境(ADAPTIVE Communication Environment)是可以自由使用、开放源码的面向对象(OO)框架(Framework),在其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++ Wrapper Facade(包装外观)和框架组件,可跨越多种平台完成通用的通信软件任务,其中包括:事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通信、共享内存管理、消息路由、分布式服务动态(重)配置、并发执行和同步,等等。
  • Boost.Asio:用于网络和底层I/O编程的跨平台的C++库
  1. 数据库
  • OTL (Oracle, Odbc and DB2-CLI Template Library):
    不但支持跨平台,还跨数据库。OTL支持以下面的数据库 : OTL Oracle ,SQL Server, Access, MySQL。还有其它的库如DTL,这个库不但支持ODBC,它还支持数据库的原生接口,可以有更佳的性能。
  1. GUI
  • QT:QT是双认证的,当你的程序免费时它就免费,你的程序打算卖钱时,它也要,而且要价很高。所以,如果有版权,成本上的考虑的话,则可以考虑wxWidgets。
  • wxWidgets:wxWidgets(/wɪksˈwɪdʒɪts/,原名wxWindows)是一个开放源代码且跨平台的对象工具集(widget toolkit),其库可用来创建基本的图形用户界面(GUI)。wxWidgets由Julian Smart于1992年首先开发。
  1. 科学计算
  • GSL:GNU科学库。
  1. 游戏开发
  • Cocos2d-x:一个跨平台框架,用于构建2D游戏,互动图书,演示和其他图形应用程序。
  1. 视频
  • FFmpeg:一个完整的,跨平台的解决方案,用于记录,转换视频和音频流

参考文献

  1. c++跨平台移植指南, by 洪柏敏.
  2. ACE自适配通信环境,by wikipedia.
  3. C++库汇总, by 工程师WWW.
  4. 值得推荐的C/C++框架和库, by zhihu.
  5. printf 格式化输出符号详细说明,by jackytse_.
  6. 理一理字节对齐的那些事,by 守望.
  7. 不见得你会计算C字符串长度,by veryitman.
  8. 浅析C语言之uint8_t / uint16_t / uint32_t /uint64_t,by 海阔天空sky1992.
  9. char,int,float,double所占字节数,by 张小铭.
  10. c++ 时间类型详解 time_t,by runoob.
  11. 网络传输——序列化,by bw_0927.
  12. 干货:构建C/C++良好的工程结构,by Froser.
  13. UTF-8与UTF-8 without BOM,by 苏州-微尘.

Emscripten教程

发表于 2018-07-15 | 更新于 2018-09-09

Emscripten是一种基于LLVM的编译器,理论上能够将任何能够生成LLVM位码的代码编译成javascript的严格子集asm.js,实际上主要用于将C/C++代码编译成asm.js。本文主要介绍Emscripten的安装过程。

下载和安装

从源码编译安装十分麻烦,推荐安装核心的Emscripten SDK。以Windows为例,先使用如下命令下载emsdk。

1
2
3
4
5
# Get the emsdk repo
git clone https://github.com/juj/emsdk.git

# Enter that directory
cd emsdk

再使用如下命令安装配置Emscripten。

1
2
3
4
5
6
7
8
9
10
11
# Fetch the latest registry of available tools.
.\emsdk.bat update

# Download and install the latest SDK tools. Need install Python first.
.\emsdk.bat install latest

# Make the "latest" SDK "active" for the current user. (writes ~/.emscripten file)
.\emsdk.bat activate latest

# Activate PATH and other environment variables in the current terminal
.\emsdk_env.bat

验证

使用如下命令验证Emscripten是否安装配置正确。

1
2
3
4
5
6
7
8
# Enter that directory
cd emsdk

# Activate PATH and other environment variables in the current terminal
.\emsdk_env.bat

# Verifying Emscripten
emcc.bat -v

运行

如果验证通过,即可使用Emscripten编译C/C++代码到asm.js。

创建名为helloWorld.cpp的文件,其内容如下:

1
2
3
4
5
6
#include <stdio.h>

int main() {
printf("hello, world!\n");
return 0;
}

使用如下命令编译:

1
emcc.bat helloWorld.cpp

编译后将生成a.out.js和a.out.wasm两个文件。后者是包含编译后代码的WebAssembly文件,前者是用于加载和执行后者的Javascipt文件。使用如下命令测试编译后生成的代码,将输出“hello,world!”。

1
node a.out.js

参考链接

  1. http://kripken.github.io/emscripten-site/docs/introducing_emscripten/about_emscripten.html. by kripken.

浏览器中运行3D游戏的思考

发表于 2018-07-10 | 更新于 2018-11-20

Web技术突飞猛进,几乎无所不能,无所不在。然而在3D游戏领域,web技术乏善可陈,性能瓶颈问题制约其发展。但是开发者们没有放弃这个梦想,不断努力,孜孜不倦地改进和增强浏览器中运行3D游戏性能的方法和技术,目前已能看到一丝曙光。

对C/S架构的3D游戏而言,渲染和计算通常都在客户端,服务器端负责用户状态的管理和分发。同样对B/S架构的运行在浏览器中的3D游戏,所有的渲染和计算也应该在浏览器端。如果渲染和计算放在服务器端,将导致可怕的延迟,并严重损害3D游戏的可伸缩性。

当前很多3D游戏使用C/C++语言编写,如果能够将C/C++语言编译成JavaScript语言,可大大促进Web 3D游戏的开发。而编译器项目Emscripten正是一个这样的工具。它能将C/C++代码编译成一种叫做asm.js的Javascript变体。需要指出的是,Emscripten的输入,即C/C++代码最好是开放源代码的,因为Emscripten不支持闭源代码的编译。例如Emscripten不支持mfc程序的转换,因为mfc是闭源的[4]。

asm.js是一个JavaScript的严格子集,它只提供32位带符号整数和64位带符号浮点数两种数据类型,其他Javascript类型如字符串、布尔值等以数值形式存在,保存在内存中,通过TypedArray调用。另外asm.js没有垃圾回收机制,所有内存操作都由程序员自己控制。asm.js是优化后的JavaScript,它在浏览器中的运行速度大约是原生代码的一倍左右[1]。

asm.js虽然比原生javascript运行速度快了一倍左右,但是相比C/C++代码,运行速度还是有差距。幸运的是出现了名为WebAssembly的技术。WebAssembly或称wasm是一个实验性的低级编程语言,应用于浏览器内的客户端[5]。WebAssembly是便携式的抽象语法树,被设计来提供比JavaScript更快速的编译及运行。WebAssembly将让开发者能运用自己熟悉的编程语言(最初以C/C++作为实现目标)编译,再藉虚拟机引擎在浏览器内运行。WebAssembly的开发团队分别来自Mozilla、Google、Microsoft、Apple,代表着四大网络浏览器Firefox、Chrome、Microsoft Edge、Safari。2017年11月,所有以上四个浏览器都开始实验性的支持WebAssembly。目前,最新版本的Emscripten已支持将C/C++代码编译成wasm。

参考文献

  1. http://www.ruanyifeng.com/blog/2017/09/asmjs_emscripten.html. by 阮一峰.
  2. https://www.cnblogs.com/slly/p/6639173.html. by 李某龙.
  3. http://kripken.github.io/emscripten-site/. by emscripten.
  4. https://github.com/kripken/emscripten/issues/941. by emscripten.
  5. https://zh.wikipedia.org/wiki/WebAssembly. by wikipedia.

Cookie,Session和Token会话知识整理

发表于 2018-07-08 | 更新于 2022-07-22

HTTP是一种无状态的协议,然而当服务器端需要判断用户能否访问某些资源,记录用户的购物车内容等场景时,就需要一种机制维护会话状态,这时候Cookie、Session和Token就派上了用场。

Cookie

Cookie技术最早用于解决HTTP的会话问题, 它是 http 协议的一部分,它的处理分为如下几步:

  • 服务器向客户端发送 cookie。
    • 通常使用 HTTP 协议规定的 set-cookie 头操作。
    • 规范规定 cookie 的格式为 name = value 格式,且必须包含这部分。
  • 浏览器将 cookie 保存。
  • 每次请求浏览器都会将 cookie 发向服务器。

可选的Cookie参数

其他可选的 cookie 参数会影响将 cookie 发送给服务器端的过程,主要有以下几种:

  • path:表示 cookie 影响到的路径,匹配该路径才发送这个 cookie。
  • expires 和 maxAge:告诉浏览器这个 cookie 什么时候过期,expires 是 UTC 格式时间,maxAge 是 cookie 多久后过期的相对时间。当不设置这两个选项时,会产生 session cookie,session cookie 是 transient 的,当用户关闭浏览器时,就被清除。一般用来保存 session 的 session_id。
  • secure:当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效。
  • httpOnly:浏览器不允许脚本操作 document.cookie 去更改 cookie。一般情况下都应该设置这个为 true,这样可以避免被 xss 攻击拿到 cookie。

常用场景

当给Cookie设置expires和maxAge后,在未到期前,浏览器端的Cookie不会因为浏览器的关闭而消失。该特性常用于自动登录,记录用户浏览信息。例如很多购物网站常用该特性记录用户的喜好和购买的物品。

Cookie安全隐患

Cookie提供了一种手段使得HTTP请求可以附加当前状态, 大多数网站就是靠Cookie来标识用户的登录状态的,例如:

  1. 用户提交用户名和密码的表单,这通常是一个POST HTTP请求。
  2. 服务器验证用户名与密码,如果合法则返回200(OK)并设置Set-Cookie为authed=true。
  3. 浏览器存储该Cookie。
  4. 浏览器发送请求时,设置Cookie字段为authed=true。
  5. 服务器收到第二次请求,从Cookie字段得知该用户已经登录。 按照已登录用户的权限来处理此次请求。

上述认证流程存在安全隐患,因为Cookie是可以被篡改的。如果使用一些HTTP客户端软件,设置Cookie字段为authed=true并发送该HTTP请求,服务器就会被欺骗。

Cookie防篡改机制

服务器为每个Cookie项生成签名,可有效地防止Cookie被篡改。因为用户篡改Cookie后无法生成对应的签名, 服务器便可得知用户对Cookie进行了篡改。一个简单的校验过程可能是这样的:

  1. 在服务器中配置一个不为人知的字符串(我们叫它Secret),比如:x$sfz32。
  2. 当服务器需要设置Cookie时(比如authed=false),不仅设置authed的值为false, 在值的后面进一步设置一个签名,最终设置的Cookie是authed=false|6hTiBl7lVpd1P。
  3. 签名6hTiBl7lVpd1P是这样生成的:Hash(‘x$sfz32’+’false’)。 要设置的值与Secret相加再取哈希。
  4. 用户收到HTTP响应并发现头字段Set-Cookie: authed=false|6hTiBl7lVpd1P。
  5. 用户在发送HTTP请求时,篡改了authed值,设置头字段Cookie: authed=true|???。 因为用户不知道Secret,无法生成签名,只能随便填一个。
  6. 服务器收到HTTP请求,发现Cookie: authed=true|???。服务器开始进行校验: Hash(‘true’+’x$sfz32’),便会发现用户提供的签名不正确。

通过给Cookie添加签名,使得服务器得以知道Cookie被篡改。然而故事并未结束。

因为Cookie是明文传输的, 只要服务器设置过一次authed=true|xxxx我不就知道true的签名是xxxx了么, 以后就可以用这个签名来欺骗服务器了。因此Cookie中最好不要放敏感数据。 一般来讲Cookie中只会放一个Session Id,而Session存储在服务器端。

Session

为了解决Cookie的安全隐患,Session机制应运而生。session机制是一种服务器端的机制,它存储在服务器端的,避免了在客户端Cookie中存储敏感数据。Session可以存储在HTTP服务器的内存中,也可以存在内存数据库(如redis)中, 对于重量级的应用甚至可以存储在数据库中。

客户端对服务端请求时,服务端会检查请求中是否包含一个session标识( 称为session id ).

  • 如果没有,那么服务端就生成一个随机的session以及和它匹配的session id,并将session id返回给客户端.
  • 如果有,那么服务器就在存储中根据session id 查找到对应的session.

基于Session的登录流程

一个简单的使用Session机制的登录流程可能是这样的:

  1. 用户提交包含用户名和密码的表单,发送HTTP请求。
  2. 服务器验证用户发来的用户名密码。
  3. 如果正确则把当前用户名(通常是用户对象)存储到redis中,并生成它在redis中的ID。这个ID称为Session ID,通过Session ID可以从Redis中取出对应的用户对象, 敏感数据(比如authed=true)都存储在这个用户对象中。
  4. 设置Cookie为sessionId=xxxxxx|checksum并发送HTTP响应, 仍然为每一项Cookie都设置签名。
  5. 用户收到HTTP响应后,便看不到任何敏感数据了。在此后的请求中发送该Cookie给服务器。
  6. 服务器收到此后的HTTP请求后,发现Cookie中有SessionID,进行放篡改验证。
  7. 如果通过了验证,根据该ID从Redis中取出对应的用户对象, 查看该对象的状态并继续执行业务逻辑。

Session安全隐患

Session ID作为Cookie存储在浏览器端,因此存在被劫持的风险,尤其是开发者没有正确的关闭会话。用户关闭会话时,应删除传递 Session ID 的 Cookie,同时撤销服务器端的Session内容。例如:

1
2
3
4
5
6
7
8
9
10
11
12
/* 普通用户登出 */
router.post('/signout', function(req, res, next) {
if (_.isEmpty(req.body) === false) {
req.session.account = null; // 删除session
res.json({
message: '登出成功!'
});

} else {
res.send(406, { message: 'The params is not correct!' });
}
});

Token

Token是用户的验证方式,最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。

基于Token的身份验证流程

使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

基于Token方法的优势

  • JWT 方法允许我们进行AJAX调用任何服务器或域。由于HTTP头是用来传输用户信息的。
  • 没必要在服务器存储一个单独的session。JWT本身传达全部的信息。
  • 服务器端减少到只是一个API和可以通过CDN服务的静态资源(HTML,CSS,JS)。
  • 认证系统是手机兼容的,任何设备上可以生成令牌。
  • 由于已经消除了cookie的需要,也不再需要保护跨站请求。
  • API密钥提供非此即彼的解决方案,然而JWT提供更颗粒度的控制,它可以用于任何调试目的的检查。
  • API密钥依赖于中央存储和服务。JWT可以自发行或者外部服务在允许的范围和期限发布它。

JWT结构

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

Cookie、Session和Token对比

cookie与session的区别

  1. cookie数据存放在客户端上,session数据放在服务器上。
  2. cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗。考虑到安全应当使用session。
  3. session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。考虑到减轻服务器性能方面,应当使用COOKIE。

session与token的区别

作为身份认证token安全性比session好,因为每个请求都有签名还能防止监听以及重放攻击。

Session 是一种HTTP存储机制,目的是为无状态的HTTP提供的持久机制。Session 认证只是简单的把User 信息存储到Session 里,因为SID 的不可预测性,暂且认为是安全的。这是一种认证手段。 但是如果有了某个User的SID,就相当于拥有该User的全部权利.SID不应该共享给其他网站或第三方。

Token, 如果指的是OAuth Token 或类似的机制的话,提供的是 认证 和 授权,认证是针对用户,授权是针对App。其目的是让某App有权利访问某用户的信息。这里的Token是唯一的。不可以转移到其它App上,也不可以转到其它用户上。

参考链接

  1. cookie 和 session, by 极客学院.
  2. Cookie/Session的机制与安全, by Harttle Land.
  3. Python中关于JSON网络令牌的实例教程, by Python部落.
  4. 什么是 JWT – JSON WEB TOKEN, by Dearmadman
  5. JSON Web Token 入门教程,by 阮一峰.
  6. localForage,by github.
  7. node session 实现登录状态持久化,by 开心的米卡.
上一页1…505152…54下一页

Jack Huang

535 日志
69 标签
© 2026 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse