0%

debian最小化安装时,debian Standard system utilities并不是必须的,可以选择不安装任何task,或者只选择安装ssh server,系统安装成功后安装需要的软件即可。相反,如果安装了用不到,反而增加了系统安全风险。

MurmurHash是一种非加密的哈希算法,目前主要用于数据分区。

其名字来自于MUltiply and Rotate,因为要经过多次MUltiply and Rotate,所以叫Murmur.

当前版本为MurmurHash3,cassandra使用了这个哈希算法来对数据进行分区,因此对应分区器的名字为Murmur3Partitioner,此分区器是cassandra当前默认的分区器。

推荐使用Murmur3Partitioner分区器。

References:
[1]MurmurHash
[2]陌生但默默一统江湖的MurmurHash

===
[erq]

删除文件或文件夹时,并不会访问文件的内容,因此没有理由非要使用gulp插件处理此事,因为gulp插件主要是用来处理文件流的。

gulp-clean和gulp-rimraf都已经deprecated,使用del和vinyl-paths来做此项工作。

直接删除文件

安装del

1
$ npm install --save-dev del

定义clean任务:

1
2
3
4
5
6
7
8
9
10
11
var gulp = require('gulp');
var del = require('del');

gulp.task('clean', function () {
return del(\['path1',
'path2',
'!path3'
\]);
});

gulp.task('default', \['clean:mobile'\]);

可以为del传递单个路径,也可以传递一个路径数组,支持globbing。

从pipeline流中删除文件

如果想在流中处理之后删除某些文件,可以使用vinyl-paths来获取流中的文件路径,然后传递给del

1
2
3
4
5
6
7
8
9
10
11
12
13
var gulp = require('gulp');
var stripDebug = require('gulp-strip-debug'); // only as an example
var del = require('del');
var vinylPaths = require('vinyl-paths');

gulp.task('clean:tmp', function () {
return gulp.src('tmp/*')
.pipe(vinylPaths(del))
.pipe(stripDebug())
.pipe(gulp.dest('dist'));
});

gulp.task('default', \['clean:tmp'\]);

References:
[1]Delete files and folders
[2]gulp-rimraf
[3]gulp-clean

===
[erq]

unix上的模式匹配glob,其名字缩写来自于global或global command

debian源安装gulp后,执行gulp -v提示:

1
/usr/bin/env: node: No such file or directory

是因为/usr/local/bin/gulp.js第一行引用的是node,而node在debian系统中实际的可执行文件为nodejs,修正之后就正常了。

restful是一种轻量级的web service实现方式。restful并不是标准,也没有对应的软件实体,只是基于http协议的一种指导性的设计方法或原则。

当下流行的open api大部分采用restful的方式实现,但restful并不限于此。普通的web app,移动app,以及系统之间的web service集成都可以采用restful的方式。

restful和http一样是无状态的,也就是单次请求之间并无任何联系,每次请求必须携带全部的状态信息。

对于受保护的资源,restful同样面临认证(authentication)和授权(authorization)的问题。

restful在不同的使用情景下有其相适宜的认证和授权方式。

第三方授权

现在火热的开放平台就是典型的三方授权模式。OAuth(Open Authorization)就是用于三方授权的协议,当前版本为2.0。
OAuth协议重在授权,应用程序无需知道资源拥有者的身份凭证,只需用户授权一定的资源访问权限,获取相应的Access Token就可以在授权范围内访问用户的资源。这种授权是有期限的,而且用户可以随时撤销。

OAuth授权整个流程涉及到用户,资源服务器,认证服务器和第三方应用这四个角色。资源服务器和认证服务器只是概念上的区分,物理上可以存在于同一个服务器。

Access Token就是与一个与用户相关的一个随机数,认证服务器记录了此Access Token所拥有的访问权限。第三方应用访问用户资源时携带Access Token,经过权限检查可以访问被授权的资源。

web应用

前后分离的web应用程序,后端可以采用restful方式向前端提供api接口。这种模式,使用传统的session方式即可满足要求。也可以采用token的方式,用户使用身份凭证通过系统身份认证后,服务端颁发一个随机的token,以后每次访问api时,参数中携带此token即可。token可以设置有效期,过期以后重新认证颁发新的token。

其实传统的session使用的sessionid就是token。无论使用cookie,url重写还是隐藏表单域,无非都是将服务器颁发的sessionid再重新发送给服务器进行认证。sessionid就是会话令牌。

session用于认证,授权则由应用程序自行处理,比如基于角色的权限系统等。

传统的session方式最大的风险在于session劫持,https可以大大缓解这一风险,可以杜绝中间人攻击。

web接口

restful方式实现web service供其他应用使用。这种情形下通过颁发app id(access key/pulic key)和app secret(secret key/private key),并对请求进行签名的方式来保证api的安全,防止有人篡改请求和非授权访问。如果签名中添加timestamp可以进一步防范重放攻击(replay attack)。

此处的access key用户标示用户的身份,客户端必须妥善保存其secret key,这是服务端认证客户端唯一可靠保证。
access key类似传统的用户名,而secret key则是两端共享的私密秘钥,以随机数生成算法生成一个较长的随机字符串即可。

客户请求api时,将请求的动作类型(get,post,put或delete)、uri、请求参数(包括access key)、timestamp使用secret key进行签名,使用HMAC-SHA256等摘要算法。将计算好的摘要同其他请求参与一同发送给服务器。服务端根据access key查找其对应的secret key,然后使用相同的算法重新计算摘要。如果重新计算的摘要与请求传送过来的摘要一致,则可以信任此次请求。添加时间戳的主要目的是用于防范重放攻击。

签名算法

下面是一个签名算法的例子。

算法描述如下:

1
2
3
4
5
signature = HMAC-SHA256(secrey_key, string_to_sign);

string_to_sign = http_verb + "&" + uri + "&" + request_parameters_sorted;

request_parameters_sorted = "key1=value1&key2=value2&..keyn=valuen";

比如以post方式访问https://foo.com/bar/test接口,请求参数为:

1
2
3
4
5
6
7
8
{
"first_name" : "kitty",
"last_name" : "san",
"age" : 8,
"gender" : "female",
"app_id" : "xdfe323423fsvdsefew",
"timestamp" : "2015-10-21 12:06:06"
}

获取当前的UTC时间戳为: 2015-10-21 12:06:06

将请求参数的key以字典序排序得到:

1
2
3
4
5
6
7
8
{
"age" : 8,
"app_id" : "xdfe323423fsvdsefew",
"first_name" : "kitty",
"gender" : "female",
"last_name" : "san",
"timestamp" : "2015-10-21 12:06:06"
}

将排序后的参数拼接得到request_parameters_sorted:

1
request_parameters_sorted = "age=8&app_id=xdfe323423fsvdsefew&first_name=kitty&gender=female&last_name=san&timestamp=2015-10-21 12:06:06";

然后得到将要签署的字符串string_to_sign

1
string_to_sign = "post&/bar/test&age=8&app_id=xdfe323423fsvdsefew&first_name=kitty&gender=female&last_name=san&timestamp=2015-10-21 12:06:06"

最后得到signature

1
signature = HMAC-SHA256('your_secret_key', "post&/bar/test&age=8&app_id=xdfe323423fsvdsefew&first_name=kitty&gender=female&last_name=san&timestamp=2015-10-21 12:06:06");

计算完签名后,将签名作为请求参数之一一起发送给服务端,参数的名称为signature。服务器端接收到请求后以相同算法重新计算签名进行核对即可。服务器端计算签名时要去掉signature参数。

所有的字符都使用UTF-8编码。直接对原始请求参数进行签名即可,无需进行url编码。服务器端接收到的也是原始请求参数,这样计算签名更简单。

防范重放攻击

因为请求中携带了请求发出时的时间戳,服务器可以设置一个合理的请求认证时间窗口,比如10分钟,在当前时间前后10分钟之内的请求都可以视为合法请求。这只是减少了被重放攻击的可能性,但并未完全杜绝重放攻击。如果请求在服务器时间窗内被截获重放,则只靠时间戳是无能为力的。

因此,需要附加另外的机制来防止重放攻击。服务端可以记录每次请求的时间戳和签名,每次请求到达是,先验证请求是否在时间窗口范围内,如果超出时间范围则直接拒绝。如果在时间窗口内,则查询请求记录,如果没有对应的请求记录,则满足此次请求,并将此次请求的时间戳和签名记录下来,并清理掉不在当前时间窗口内的所有请求。

时间戳加记录请求的方式可以完全杜绝重放攻击,而且可以保持一个很小的请求记录表。因为在当前时间窗口外的请求可以随时被清理掉。

请求速率限制

如有需要可以对api请求的速率或次数进行限制。

非对称秘钥加密签名

也可以使用RSA非对称秘钥进行数字签名。

服务端生成RSA公私密钥对和客户端的access key,将私钥和access key交付客户端应用。服务端保存access key和客户公钥。

请求签名时,客户端不再使用HMAC签名算法,而是使用普通的摘要算法,比如SHA256,但此时传送的摘要使用客户私钥进行加密后再随请求参数一起传递。因为谁都可以计算SHA256摘要,所以需要用私钥进行加密保护。

服务端接收到请求后,使用同样的算法计算SHA256摘要,然后使用客户的公钥解密随请求一起发的、客户端计算的、加密后的摘要,如果服务端重新计算的摘要与解密后的摘要相同,则认为请求是合法。

无论使用对称秘钥还是非对称秘钥进行签名,秘钥都要妥善保存,这是整个签名认证算法的基石。

无论何种情形,对于restful api的安全而言,https/ssl加密都是十分有必要的。

注意:使用access key与secret key并对api签名调用的方式,并不适合web前端或者移动app使用。因为web前端无法保密secret key,而将secret key保存在android或ios app中也是无法保证安全的,很容易将secret key从app中破解出来。只有将secret key保存在后端才能保证安全。并且secret key是针对客户端发放的,而不应该是针对每一个客户端的user发放的。

===
[erq]

IE版本检测有很多方法,特性检测是比较好的一种方式。

非标准的document.all对象只存在于IE10及以下版本。其实,其他浏览器比如chrome也实现了document.all对象,不过在这些浏览器中以布尔方式判断document.all对象都是返回undefined的。

可以这样测试:
[javascript]

[/javascript]

再佐以其他IE版本相关的特性,详见参考[1],可以有如下的IE版本检测代码:
[javascript]
var v;

if (document.all) {
if (window.atob) {
v = ‘10’;
}
else if (document.addEventListener) {
v = ‘9’;
}
else if (document.querySelector) {
v = ‘8’;
}
else if (window.XMLHttpRequest) {
v = ‘7’;
}
else if (document.compatMode) {
v = ‘6’;
}
else {
v = ‘5.5 or older’;
}

v = ‘IE’ + v;
}
else {
v = ‘IE11+ or not IE’;
}

console.log(‘Your browser is’ + v);

[/javascript]

References:
[1]Internet Explorer (IE) version detection in JavaScript
[2]JavaScript判断IE各版本最完美解决方案

===
[erq]

非标准、非通用的二进制插件打印方式此处不叙。

打印命令

  • 标准的打印方法为调用window.print(),所有的现代浏览器都支持该方法。

  • 还可以使用Print命令调用Document.execCommand(),也就是:

    1
    document.execCommand('Print')

    虽然所有的桌面浏览器都支持,但这个方法是非标准的。

    execCommand的接口规格:

    1
    bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)

局部打印方法

Javascript局部打印大约有一下这几种方法:

  • print css方式

html支持print css,这是专用于打印设备的,而通常的css用于显示设备

1
<link rel="stylesheet" href="print.css" media="print" />

可以使用print css将无需打印的区域隐藏掉,而需要打印的区域重新设置适合打印的样式,打印时直接调用window.print()即可。

这种打印方式页面不会看到变化,但是样式的调整可能会比较繁琐。

  • screen css方式

使用通常的CSS,在打印之前将无需打印的部分从页面上隐藏,需要打印的区域冲洗设置适合打印的样式。打印完成后再恢复样式。打印时会看到页面的变化。

  • body replace方式

打印之前将页面的body内容替换为需要打印的区域,打印完毕后再恢复body的内容。打印时会看到页面的变化。
这种方式与上种方法虽然做法不同,但其实质是相同的,即讲当前页面显示的内容设置要打印的内容,打印完毕后再恢复页面。

  • iframe方式

打印时生成一个iframe嵌入到主页面中,iframe的内容即为需要打印的内容,然后调用iframe的print()方法就可以了。iframe其实就是一个window。
用iframe的好处是不用弹出新窗口。

但是需要注意:
chrome从45.0开始,默认阻止iframe的print方法,除非iframe的沙箱属性有allow-modal值,并且设置了modal标志。

Starting with Chrome 45.0 print method is blocked inside an iframe unless its sandbox attribute has the value allow-modal and the modal flag is enabled.

  • popup new window方式

new window方式其实与iframe方式基本一样,唯一的区别是需要弹出新窗口。很多浏览器对于弹出新窗口都有严格的管制策略,因此新窗口方式用户体验不佳。

总的来说,iframe的方式比较简单,用户体验也较佳。

===
[erq]