bigpipe技术初探

情景模拟:
当网页越来越大,相应的请求数据,css,js等也越来越多,页面的加载速度就变的越来越慢慢,通过研究数据表明,用户能够容忍的大概为2.5s,如果达到3到4s还是白页的话,那么用户基本也就失去耐心。

目前我们的处理方法基本基于ajax来减少加载时间。

页面完整加载流程如下:
1) 用户访问网页,浏览器发送一个HTTP 请求到网络服务器
2) 服务器解析这个请求,然后从存储层去数据,接着生成一个html 文件内容,并在一个HTTP Response 中把它传送给客户端
3) HTTP response 在网络中传输
4) 浏览器解析这个Response ,创建一个DOM 树,然后下载所需的CSS 和JS文件
5) 下载完CSS 文件后,浏览器解析他们并且应用在相应的内容上
6) 下载完JS 后,浏览器解析和执行他们

可以看出,这些都是按照严格的顺序来执行的,当其中有一个操作没有执行结束,后面的操作就不会之执行,即操作之间不能重叠。这样就易造成性能的瓶颈。(这样就造成性能的瓶颈:服务器生成一个页面的内容时,浏览器是空闲的,显示空白内容;而当浏览器加载渲染页面内容时,服务器又是空闲的,时间与性能的浪费由此产生。)

解决方案:

bigpipe 是有Facebook发明的一种页面加载技术,即一种减少http请求的,首屏快速加载的异步加载页面方案;其核心思想是并行,服务器生成的数据和浏览器渲染数据的并行。

php实现原理:利用ob_flush()与flush()将缓冲区的内容提前输出,浏览器可提早加载这部分的内容,无需等待所有输出完成再加载。将页面内容划分为一个个小块,输出一个后在输出下一个,使用户尽早看到页面内容,优化用户体验。

详细内容:
bigPipe提出分块的概念,即根据页面内容位置的不同,将整个页面分成不同的块儿– 称为pagelet。
将众多pagelet加载的不同阶段像流水线一样在浏览器和服务器上执行,这样就做到了浏览器和服务器的并行化,从而达到重叠服务器端运行时间和浏览器端运行时间的目的。使用BigPipe 不仅可以节省时间,使加载的时间缩短,而且可以同过pagelet的分步输出,使一部分的页面内容更快的输出,从而获得更好的用户体验。BigPipe 中,用户提出页面访问请求后,页面的完整加载流程如下:

1) Request parsing:服务器解析和检查http request
2)Datafetching:服务器从存储层获取数据
3) Markup generation:服务器生成html 标记
4) Network transport : 网络传输response
5) CSS downloading:浏览器下载CSS
6) DOM tree construction and CSS styling:浏览器生成DOM 树,并且使用CSS
7) JavaScript downloading: 浏览器下载页面引用的JS 文件
8) JavaScript execution: 浏览器执行页面JS代码

初略看一下也许会好奇这个和刚开始说的那个没什么区别,但这8个流程属于一个pagelet,而多个pagelet 的不同操作阶段就可以像流水线一样进行执行了。

与ajax比较

1) AJAX 的核心是XMLHttpRequest,客户端需要异步的向服务器端发送请求,然后将传送过来的内容动态添加到网页上。如此实现存在一些缺陷,即发送往返请求需要耗费时间,而BigPipe 技术使浏览器并不需要发送XMLHttpRequest 请求,这样就节省时间损耗。

2) 使用AJAX时,浏览器和服务器的工作顺序执行。服务器必须等待浏览器的请求,这样就会造成服务器的空闲。浏览器工作时,服务器在等待,而服务器工作时,浏览器在等待,这也是一种性能的浪费。使用BigPipe,浏览器和服务器可以并行同时工作,服务器不需要等待浏览器的请求,而是一直处于加载页面内容的工作阶段,这就会使效率得到更大的提高。
3) 减少浏览器发送到请求。对一个5亿用户的网站来说,减少了使用AJAX额外带来的请求,会减少服务器的负载,同样会带来很大的性能提升。

缺点
不利于seo,针对爬虫需要通过ua判断,爬虫还是用传统模式,用户就用bigpipe

注意事项:
1.如果使用nginx作为web服务器,那么nginx可能会缓冲php的输出。即便是调用了flush方法,相应内容也会被nginx缓冲,而不会输出到浏览器。
2.某些浏览器也会有缓冲,如在接收的数据小于一定值的时候,不会对代码进行渲染。

补充:
php文件最终在浏览器上显示,走过3个缓冲阶段
即:php buffer=》web server buffer=》浏览器buffer

1)php buffer
php运行的结果先放入缓冲区(buffer),只有当缓冲区满了或者php运行完毕,才将数据输出去。
所以这边通过ob_flush将缓冲区的内容强制输出。

2)web server buffer
目前我们接触的也就apache和nginx
apache buffer
当php的数据给apache服务器时,也会想放入缓冲区(当缓冲区数据满或者执行完毕时,才执行数据)
这边我们可以在php层使用flush()来强制将缓冲区数据输出
(旧版本Apache可能需要关闭GZip。)

nginx buffer
nginx使用fastcgi缓冲区来缓存数据,fastcgi是强制将buffer打开的,无法关闭。
应对方法:通过apache+php实现bigpipe,nginx放在代理服务器的角色上,并使用proxy_buffering关闭代理缓存

3)浏览器buffer
这里研究得不深,不过这里指的不是讲图片,样式,头信息和js等缓存,而是和上面一样,将内容想放入缓存区中,当请求完毕和缓存写满后在输出。
这一层清楚方法未知,不过影响不是很大,一般主流的不超过4k。

实现代码
见附件

直接将txt换成php运行即可、
aaa

这边只是用php简单的实现其大概思想,但这样并不支持多线程,但对于小网站来说这样串行加载已经可以达到加载要求了。

对于大型网站,那么就需要考虑多线程执行这些pagelet。由于php不支持线程,所以可以使用的方式大致有curl,stream_select和php5新增的stream_socket_client函数或者用PHP 的扩展模块paaactnl模块中的pcntl_fork()函数来生成子进程。

——————————————————————————————

相关参考

http://www.searchtb.com/2011/04/an-introduction-to-bigpipe.html(bigpipe详细介绍以及相关bigpipe参考链接)
http://taobaofed.org/blog/2016/03/25/seller-bigpipe-coding/(bigpipe编程实践)
http://blog.sina.com.cn/s/blog_4dd475390102ebe6.html(bigpipe注意事项)
http://www.bo56.com/%E4%BB%8Ephp%E7%9A%84%E7%BC%93%E5%86%B2%E5%8C%BA%E8%AF%B4%E8%B5%B7/(从php缓存去说起)

开发过程的一些问题总结

一、大家都讨厌的deadline

要求:
1、进度安排
xx-xx号 完成xxx
2、结果:可展示的内容
后台是这样的,接口我写好了。

其实是个更简单的想法:
给自己要求2天完成,可能最后2天半才完成。
如果没定计划的话,可能需要3天。

TIP:你可能会觉得有和没有并没有区别,我觉得是给自己一种压力。

二、表达能力问题

暂时没找到解决方法

TIP: 理清自己的思路,就更容易清楚表达。
可以祭出流程图(龙哥带的,是会被影响的)
栗子:上次保险的流程逻辑

也可以在表达之前用纸理下思路。 龙哥,传哥都在用。
(15块钱一本500张)

三、反馈

如果可以,最好告诉别人结果:我处理完了。
而不是因为xxx,还没搞定。

四、看似莫名其妙的问题,怎么办?

1、自己搞不定,可以询问下其他人。
比如上周:js注入微博的一个链接。
到这周一才解决。

2、建议:追求一个结果。
什么结果?就是这个事情解决完了。

3、栗子
很久之前,sphinx,api不能用,因为版本问题。
常见的例子:测试上是好的,正式机上就不行。

4、不想去解决?
解决是一次进步。

五、项目方面

1、保证和需求方想法一致
实现方案有多种,要的是哪种?
有的时候接的需求,并不完成清楚,就去多问。
当然自己也要想清楚,不耽误他们的时间。

2、完成比完美重要
开发时:怎么方便,怎么来。
开发后:

3、开发后期,先完成的模块可以先上。

4、维护阶段
不能一次性解决,每次就优化一点。
比如上次吐槽apidoc不好用,可以想下其他方法。

30分钟,快速在项目中使用git


编   写:袁 亮
时   间:2017-01-17
说   明:30分钟,快速在项目中使用git

一、账号环境
    1、https://gitlab.oneitfarm.com
        使用自己的企业邮箱注册自己的账号
    2、安装windows环境
        https://git-scm.com/downloads/guis
        \\192.168.0.18\运维网络硬盘\y袁亮\software\Git-2.8.1-64-bit.exe
        cmd下运行 git 配置 
            git config --global user.email ""
            git config --global user.name ""
    3、生成sshkey (https,安全证书,一台电脑一个)
        任意目录,右键,打开 Git GUI
        help => show sshkey => Generate Key
    4、在gitlab添加ssk key
        https://gitlab.oneitfarm.com/profile/keys
    
二、加入开发组 
    1、申请加入开发者 https://gitlab.oneitfarm.com/geek
    2、审核通过,并分配相应权限
    
三、导出项目代码
    1、本地无代码,从远程拉取【必须会】
        1.1 注意事项 
            使用https协议的,ssh的服务器没有开启
            所有跟远程代码库交互,都需要输入git账号和密码
            windows下,空项目一直clone不下来
        1.2 linux
            git clone https://gitlab.oneitfarm.com/geek/dealer.git yl
        1.3 windows
            空目录,右键Git Gui
            Clone Existing Respository
            ps:需要输3次账号密码,不知道是什么鬼
    2、本地有代码,关联到远程一个空代码库【了解即可】
        2.1 linux
            a. 创建本地仓库 git init
            b. git remote add 
        2.2 windows
            a. 创建本地仓库 Git Gui => Respository => New
            b. 将本地代码,提交到本地仓库 stage change => commit
            c. 关联远程代码仓库 Remote => Add
                origin
                https://gitlab.oneitfarm.com/geek/dealer.git
            d. 提交到远程代码仓库
                push 
    3、本地有代码,关联到远程一个非空代码库
        略
    
四、常规操作 【必须会】
    1、本地仓库 查看当前git状态
        1.1 git status (svn st)
        1.2 可以通过配置alish,使用git st    
    2、本地仓库 新增文件
        2.1 新增 
            git add application/controllers/Store.php
        2.2 提交到本地仓库
            git commit -m '增加store控制器' application/controllers/Store.php
    3、本地仓库 修改文件
        3.1 查看文件改动
            git diff webroot/index.php
        3.2 不想修改,恢复文件
            git checkout webroot/index.php
        3.3 文件改动存入缓存区
            git add webroot/index.php
        3.4 改错,不想保持,取消修改
            git reset webroot/index.php
            git checkout webroot/index.php
        3.4 提交修改
            git commit -m 'index.php代码' webroot/index.php
    4、本地仓库 删除文件
        4.1 本地仓库删除 
            git rm debug.php
        4.2 发现删了,撤销
            git reset HEAD debug.php
            git checkout debug.php
        4.3 确认删除,提交
            git commit -m '删除调试代码' debug.php
        4.3 提交之后发现删错了
            a. 查看 log
                git log 
            b. 找到删除前的版本,并回滚到那个版本
                git reset --hard {commit_id}
                将被删除的文件备份出来
            c. 查看所有的版本log,并找到要恢复的版本
                git reflog
            d. 恢复到最新版本
                 git reset --hard {commit_id}
            e.将备份出来的代码还原回去
                cp或者mv回来,然后add,commit
    5、查看文件版本记录
        git log webroot/index.php
    6、提交代码到远程仓库
        git push -u origin master
    7、从远程代码库拉取最新代码
        git pull 
    8、常用设置
        8.1 pull push 免去每次输账号密码
            git config --global credential.helper store
        8.2 颜色设置
            git config --global color.ui true
        8.3 常用alias
            git config --global alias.st 'status'
            git config --global alias.ci 'commit'
            git config --global alias.co 'checkout'
            git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
    9、忽略文件(日志,配置等不想要提交的文件)
        在项目文件下新建.gitignore文件
        文件中写入 /.idea/*  即忽略.idea文件 (!/.idea/echo.php 即不忽略.idea文件夹下的echo.php文件)
        *.txt 忽略txt类型的文件
        最后别忘了提交.gitignore文件
            
五、分支操作
    1、查看本地仓库分支及当前所在分支
        git branch 
    2、新建分支
        2.1 正常创建
            git branch dev
            git checkout dev
        2.2 命令合并
            git checkout -b dev
        2.3 从远程仓库创建分支
            a. 确保远程仓库有这个分支
            b. 更新本地仓库,确保本地知道远程仓库的分支 
                git remote update
                git fetch
                git remote show origin :查看远程分支
            c. 从远程分支中checkout一个分支到本地
                git checkout -b yl origin/yl
                ps:本地分支名可以起的跟远程分支名不一样,但不建议这么做,除非本地多个分支对应远程同一个分支
            d. 分支改动push到对应远程分支
                git push origin yl
    3、切换分支
        git checkout master
    4、删除分支
        git branch -d dev
        ps:如果删除未合并的分支,需要用-D参数,慎用,除非是写临时脚本的分支
    5、合并分支
        5.1 git merge dev 
        5.2 该命令是将dev分支合并到当前所在分支,因此需要先checkout到你所想要合并到的分支
            git checkout master
        5.3 冲突解决
            a. 手动修改冲突文件,然后提交
            b. 借助 mergetool (还没用过)
        5.3 查看分支合并情况
            git log --graph --pretty=oneline --abbrev-commit
    6、给绑定分支绑定远程分支
        git branch --set-upstream demo origin/yl
    7、拉取合并指定指定远程仓库的指定分支
        git pull  
        git pull origin yl

六、GitLab Flow 工作流
    1、参考文档
        http://www.ruanyifeng.com/blog/2015/12/git-workflow.html
    2、我们采用的方式
        Gitlab flow
        版本发布
    
七、远程仓库使用
    1、添加远程仓库
        git remote add  
        shortname默认是origin
    2、查看远程仓库信息
        git remote :查看shortname
        git remote -v :查看完整信息
    3、拉取远程仓库的信息
        git fetch  :也可以使用,默认是origin
        ps:fetch只会拉取数据,但并不合并,pull是fetch之后会自动合并git merge
    4、推送到远程仓库
        4.1 git push origin master
        4.2 将本地master分支推送到origin远程仓库中对应的分支
        4.3 如果origin仓库里对应的分支已经有人改过,必须先pull下来,然后才能推送
        4.4 master分支推送到origin仓库的具体哪个分支,是由之前创建分支的时候决定的,并不是根据分支名来
            如果两个名字不一样,很容易出现一些看起来莫名其妙的问题,所以不要干这个事
    5、查看远程仓库
        git remote show [remote-name] 

八、标签tag (对应之前的release)
    1、本地仓库 查看所有标签
        1.1 git tag
        1.1 git tag -l '匹配规则,可以写正则'
        1.2 以标签名排序,不以创建时间
    2、本地仓库 创建标签
        2.1 git tag -a v1.0.0 -m '标签注释'
        2.2 不输入-m,在运行编辑器里可以更方便的写各种备注
        2.3 不允许打轻量标签,除非是本地测试用
    3、本地仓库 查看标签信息
        git show v.1.0.0
    4、将标签推送到远程仓库
        4.1 git push origin [tagname]
        4.2 多个标签,也单个单个推,不允许一次性把所有标签推上去
    5、从标签中检出新的分支
        5.1 git checkout -b fix-v2.0.0 v2.0.0
        5.2 类似分支的创建,如果本地没有v2.0.0这个标签,需要先从远程仓库pull或者fetch下来
    6、修复线上tag两种办法
        
        
参考文档:
    1、Git教程
        http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
    2、Git 工作流程
        http://www.ruanyifeng.com/blog/2015/12/git-workflow.html
    3、Git分支管理策略
        http://www.ruanyifeng.com/blog/2012/07/git.html
    4、Git 使用规范流程
        http://www.ruanyifeng.com/blog/2015/08/git-use-process.html
    5、git 文档
        https://git-scm.com/book/zh/v2/
    6、Git可视化极简易教程 — Git GUI使用方法 
        http://www.runoob.com/w3cnote/git-gui-window.html
    7、GitLab Flow的使用
        https://www.15yan.com/story/6yueHxcgD9Z/
        https://about.gitlab.com/2014/09/29/gitlab-flow/
    8、如何使用gitlab的flow以及代码review
        http://mojito515.github.io/blog/2016/03/09/ru-he-shi-yong-gitlabde-flowyi-ji-dai-ma-review/
    9、The 11 Rules of GitLab Flow
        https://about.gitlab.com/2016/07/27/the-11-rules-of-gitlab-flow/
    10、使用git、git-flow与gitlab工作
        http://blog.2baxb.me/archives/736
    11、GitLab的Pull Request工作流
        http://www.jianshu.com/p/6bcd082101c1
    12、常用 Git 命令清单
        http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html
        
        

深入理解CI框架:数据库的加载过程


说明:没特别指明的情况下,本文涉及的Codeigniter均为3.1.2版。


要在 CI 框架里使用数据库,你可以显示地加载它:

$this->load->database(db_to_load);

也可以在加载 model 的时候顺带加载数据库:

// 第三个参数
$this->load->model(model_to_load, name, db_to_load);

当我们只有一个数据库的时候,上面两种方法都行得通。默认情况下,CI 把数据库连接赋值给一个叫 db 的属性。但如果有多个数据库(>=2),并且要经常切换,就只能使用 database() 并且第二个参数传 true,例如:

$this->dblink = $this->load->database(db_to_load, true);//return ?

我们看一下 database() 方法的部分实现细节:

// database() 方法开头会判断 db 是否已被赋值
if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
{
    return FALSE;
}
...
// 返回数据库连接
if ($return === TRUE)
{
    return DB($params, $query_builder);
}
...
// 赋值给 db
$CI->db =& DB($params, $query_builder);

看得出来,每次执行 database(db_to_load, true) 的时候都会连接一次数据库,所以在必要时才 load model。

注:1. 上面指的是调用 database(db_to_load, true);不是 database(db_to_load);
2. 读者事先看过 DB() 方法,知道 DB() 会去连数据库 o( ̄▽ ̄)o 。

下面是 DB() 方法的部分实现细节:

...
//此处有很多 include 和 require_once
...
// $params['dbdriver'] 在配置数据库时指定,比如 mysql
$driver = 'CI_DB_'.$params['dbdriver'].'_driver';
$DB = new $driver($params);
...
// $driver 继承的某个抽象类有 initialize() 方法
$DB->initialize();

下面是 initialize() 方法的部分实现细节:

//这是 CI_DB_driver 类
if ($this->conn_id)
{
    return TRUE;
}
// db_connect 由继承该类的派生类实现
$this->conn_id = $this->db_connect($this->pconnect);
...
// 最后设置mysql(客户端)编码
return $this->db_set_charset($this->char_set);

如果你再深入源码了解 CI_DB_driver 类,你会发现下面的关系链:

$driver extends CI_DB { } // drivers 举例: CI_DB_mysql_driver

class CI_DB extends CI_DB_query_builder { } // CI_DB 是个空类

// or: class CI_DB extends CI_DB_driver { }

abstract class CI_DB_query_builder extends CI_DB_driver { }

abstract class CI_DB_driver { }

上面出现了两个抽象类,它们一个偏向于 Query,一个偏向于 DB,下面是两个类的注释:

CI_DB_query_builder : This is the platform-independent base Query Builder implementation class.
CI_DB_driver: This is the platform-independent base DB implementation class.

CI_DB 是个空类,没有任何实现,但是所有数据库驱动类(例如 CI_DB_mysql_driver)都继承 CI_DB
注意到 &DB(params, query_builder_override) 方法的第二个参数,它决定了 CI_DB 继承 CI_DB_query_builder 还是 CI_DB_driver :

if (! isset($query_builder) OR $query_builder === TRUE) 
{
    if ( ! class_exists('CI_DB', FALSE))
    {
        class CI_DB extends CI_DB_query_builder { }
    }
} 
elseif ( ! class_exists('CI_DB', FALSE))
{
    class CI_DB extends CI_DB_driver { }
}

可以看出来,CI_DB 起到了“适配器”的作用。

完。

6位数支付密码安全使用


编   写:袁 亮
时   间:2017-02-13
说   明:6位数支付密码安全使用

一、目的
    1、用户可以设置6位数字的支付密码用来使用自己的账号余额
    2、确保用户的支付密码使用、存储安全
    
二、业务场景描述
    1、设置支付密码
        在相应的业务流程中,用户设置自己的支付密码
    2、使用支付密码
        在支付、提现等地方输入密码进行操作
    3、重置支付密码
        忘记密码的情况下,提供重置密码的流程
    
三、用户密码安全分类
    1、流程逻辑漏洞
        1.1 平行权限
            比如仅仅在展示页面验证了权限,但实际操作的ajax请求等未做验证之类的
            或者验证之后,存储了一个已验证通过标识,然后通过这个标识去做后续操作
        1.2 业务逻辑漏洞
            比如找回密码,直接将原密码通过邮件等方式发送给了用户
            比如找回密码的token,没有绑定用户,导致可以修改任意用户
                或者没有过期时间,或者使用过之后还能再用
    2、明文密码
        2.1 http传输
            劫持、监听、篡改、xss攻击等
        2.2 GET传输
            url中会有密码,从而导致很多地方会泄露,比如统计代码、url日志等等
        2.3 log日志记录
            开发过程中,有人会将这个数据写入到日志里,日志泄露等导致密码泄露
        2.4 客户端存储
            比较菜的情况下,会把密码这种东西写在cookie里...
            移动app中把密码记录在app里
        2.5 数据库明文存储
            内部人员能看到,后台、数据库等等
            或者api里,读取所有字段等,导致把密码泄露
            数据被人拖库或者sql注入直接显示在页面上等等
    3、撞库攻击
        3.1 弱密码
            常用弱密码库,1000个不到
        3.2 响应时间差攻击
            不同错误密码判断所需时间不同,导致跨域逐步猜测
        3.3 网上已泄露的密码库,直接撞库攻击
            多次大规模安全事故,已经有极其庞大的密码库被公开
    4、应用层攻击
        4.1 sql注入
            构造特殊参数,直接绕过判断条件
        4.2 webshell
            通过webshell拦截密码或者获取到对应的数据库数据
        4.3 服务器入侵
    5、拖库后,从hash值获取对应明文
        5.1 反向查表
        5.2 彩虹表
            优化存储空间之后的一种反向查表攻击
        5.3 暴力破解
            a. 根据hash值和密码设置规则,强行遍历所有组合的所有hash,比对已有hash是否匹配
            b. 单个 GPU 上以每秒 36.5 亿次计算的速率破解 MD5 的哈希值
                以每秒 13.6 亿次计算的速度破解 SHA-1 的哈希值
                而且很多hash算法还能被硬件加速
                
四、实施方案
    1、第一次设置支付密码
        1.1 业务流程要求
            必须登陆状态
            用户必须绑定过手机号(不然的话找回密码就不行了)
        1.2 开发注意事项
            必须https,以POST形式传输数据
            ua必须是正常的
            不能以任何形式将密码记录在服务器硬盘上,比如log日志等
            不允许存储在本地客户端,比如cookie,本地存储
        1.3 密码存储
            使用password_hash方法,将密码进行hash运算,结果存储到数据库中
            记录用户设置环境的log
                ua,x-forword-for,访问url、设置时间等等
    2、使用支付密码
        2.1 业务流程要求
            必须登陆状态
            账号需要未冻结
        2.2 开发注意事项
            必须https,POST传输
            ua必须是正常的
            支付密码必须要跟所需操作同时发送过期,内部调用验证支付密码,不允许分步来做
            支付密码后期可能会替换为指纹
        2.3 验证逻辑
            验证连续错误次数是否达到上限
            是否频繁操作,频率限制
            每天最大验证次数超出上限,异常报警(内部限制上限)
            获取用户对应password_hash,验证是否正确
            记录日志:登陆信息及是否正确等信息
            正确
                验证数据库里存储的hash是否是用最新的hash生成方式生成,如果不是,需要刷新重新写入
            错误
                错误提示
    3、重置支付密码
        3.1 业务流程要求
            用户登陆
            只能使用之前绑定的手机号发送短信验证码
                ps:现在其实是可以随时绑定新的手机号,有逻辑上的漏洞
        3.2 开发注意事项
            https,POST数据
            验证码和新密码必须在同一次请求里发送,不允许分开
        3.3 重置逻辑
            a. 判断条件
                登陆判断
                手机号是否是用户绑定的手机号
                短信验证码是否正确
                密码是否合法
            b. 处理逻辑
                生成password_hash
                记录修改记录
                更新用户password_hash
        ps:如果是邮箱形式的密码修改,注意事项
            发送token
            token不能有任何的用户信息
            token必须有失效条件
                发送了新的,旧的就必须失效
                有效期
                使用过之后就失效            
                不要通过email发送用户的新密码

五、知识拓展,了解即可
    1、散列hash
        1.1 内容摘要算法
        1.2 加密和hash的区别
        1.3 hash碰撞
            hash长度固定,而且集合比内容集合小,所以存在内容不一样,hash值一样的情况
        1.4 hash函数和加密hash函数的区别
        1.5 慢速hash函数
        1.6 盐值
    2、hash破解办法
        反向查表
        彩虹表
        暴力破解
    3、撞库攻击
    4、安全分类
        浏览器安全
            xss攻击
            csrf
            点击劫持
            H5
        应用服务器安全
            sql注入
            文件上传
            会话劫持
            访问控制
            web框架安全
            DDOS攻击
            PHP语言安全
            webSERVER 配置安全
        运维安全
            服务器入侵
            防火墙
            开源产品安全设置
                redis、memcache、mongodb等等

六、参考文档
    OWASP
        https://www.owasp.org/index.php/Main_Page
    Salted Password Hashing - Doing it Right
        https://crackstation.net/hashing-security.htm
    如何安全的存储用户的密码
        http://www.freebuf.com/articles/web/28527.html
    更新后的 PHP: 现代 PHP 中的密码安全性
        http://www.ibm.com/developerworks/cn/web/wa-php-renewed_2/
    浅谈密码存储安全
        https://my.oschina.net/hjh188/blog/491666
    用户密码加密存储十问十答,一文说透密码安全存储
        http://www.cnblogs.com/xinzhao/p/6035847.html   
    Dropbox是如何安全地存储用户密码的
        http://www.infoq.com/cn/news/2016/12/How-Dropbox-securely-passwords
    Portable PHP password hashing framework
        http://www.openwall.com/phpass/
    hash_values
        http://msdn.microsoft.com/en-us/library/92f9ye3s.aspx#hash_values
    How do you use bcrypt for hashing passwords in PHP?
        http://stackoverflow.com/questions/4795385/how-do-you-use-bcrypt-for-hashing-passwords-in-php
    PHP处理密码的几种方式
        https://segmentfault.com/a/1190000003024932
    密码散列安全
        http://php.net/manual/zh/faq.passwords.php

开发流程


编   写:袁 亮
时   间:2017-02-10
说   明:开发流程

一、需求分析
    1、需求文档
    2、会议需求讨论、问题沟通
    
二、原型、设计图
    1、原型
    2、设计图
    3、前端人员提前参与
    
三、技术方案
    1、技术方案确认
    2、流程明确
    3、数据字典设计
    4、时间预估、排期、人员安排
    
四、具体开发文档
    1、各开发小组编写
    2、功能细分
        小组分工
        完成时间点
    3、问题记录
        业务上不清楚的
        实现方案不清楚的
        流程不确定的
        业务后续可能扩展
    4、接口列表
        跟其他组对接,接口定义、分配
    5、开发小组内测问题列表
    
五、完整测试
    1、团队相关人员都需要参与,包括产品、运营、设计、前端等
    2、维护统一的bug列表
    3、每个bug各开发认领修复
    
六、打包上线
    1、合并打包
    2、线上测试、回滚
    
七、文档
    1、数据字典入库
    2、apidoc文档编写
    

PHP 基于 QueryList 抓取 京东、天猫、淘宝、阿里巴巴小结

编写:刘锦良
时间:2017-02-09

php基于QueryList 抓取 阿里巴巴商品信息小结如下:

1、需求整理:在阿里巴巴指定商品页面获取:商品title,商品price,商品photo,商品banners,商品introduction_pics

2、基本思路:

    1)对于不需要加载js代码就可以获得到的数据:

        ——获取网页源代码

        ——通过QueryList::Query对源代码进行采集信息并保存到相关变量中

    2)对于需要加载js代码才可以获得到的数据:

        ——获取需要加载的js代码url

        ——获取网页源代码

        ——通过QueryList::Query对源代码进行采集信息并保存到相关变量中

3、相关实现:

    1)前端展示(249):application/views/item/item_add.php

    2) 接口调用(244):application/controllers/Material.php

                        application/models/Material.php

在postman中调用自定义签名方式的API


编   写:袁 亮
时   间:2017-02-09
说   明:在postman中自定义签名方式调用接口

一、目的
    1、电商项目内部api接口使用的签名方式导致在postman中不能使用
        需要在postman中,自行实现签名方式以方便测试API接口
    2、接口地址
        https://api.shop.ci123.com/apidoc/
    
二、实现原理
    1、在postman实际调用接口前,可以执行自定义js,从而实现数据签名
    2、自定义JS可以获取到请求参数、链接参数,并且可以自定义变量

三、实现过程
    1、增加链接参数 params
        appid:{{appid}}
    2、接口post参数增加
        expire:{{expire}}
        mdstr:{{mdstr}}
    3、添加预执行脚本 Pre-request script
        var appid = '10101';
        var appsercet = '8e2160ec2b07f1faca9055be530bf09d';

        // 定义appid
        postman.setEnvironmentVariable("appid", appid);

        // 定义时间戳
        var expire = new Date().getTime();
        postman.setGlobalVariable("expire", expire);

        // 数据签名
        var data = [];
        jQuery.each(request.data, function(key, value){
            if (key == 'expire') { // request里存储的是定义的值,而不是我们赋的值,需要单独处理
                value = expire;
            }
            if (key != 'mdstr') {
                data.push(key+'='+value);
            }
        });
        data.sort();
        var str = data.join('&')+appsercet;
        var mdstr = CryptoJS.MD5(str).toString().toLowerCase();
        postman.setGlobalVariable("mdstr", mdstr);
    
四、环境切换
    1、变量
        环境变量:EnvironmentVariable
            在整个environment内都可以用
            ps:在预执行脚本中,需要用environment.变量名来获取,不能执行获取到
        全局变量:GlobalVariable
            可以在任意环境下使用
            ps:预执行脚本中定义的变量并不能在url、params使用
                原因是因为拼接在预执行脚本之前
                单在body里又是可以用的,数据组装在预执行脚本代码之后
        自定义变量:
            仅能在预执行脚本中用
    2、环境管理
        2.1 可以将不同环境的变量定义在环境变量中
            比如测试机host,比如appid和appsecret等
        2.2 在url、params、body中的数据,预执行脚本等地方使用
    3、接口调用
        3.1 选择环境
        3.2 接口地址、参数等使用环境变量{{变量名}}
        3.3 添加接口特定参数
    4、添加预执行脚本
        // 定义时间戳
        var expire = new Date().getTime();
        postman.setGlobalVariable("expire", expire);

        // 数据签名
        var data = [];
        jQuery.each(request.data, function(key, value){
            if (key == 'expire') { // request里存储的是定义的值,而不是我们赋的值,需要单独处理
                value = expire;
            }
            if (key != 'mdstr') {
                data.push(key+'='+value);
            }
        });
        data.sort();
        var str = data.join('&')+environment.appsercet;
        var mdstr = CryptoJS.MD5(str).toString().toLowerCase();
        postman.setGlobalVariable("mdstr", mdstr);