杂记

  • 京城米贵,居之不易,多少强颜欢笑的背后,都是紧咬的牙关。–普通朋友
  • 人啊,整天算计来算计去,累不累啊。
  • 你最亲近的人都不能理解你支持你
  • 长眠是不是解决问题的办法
  • 对别人好一定要计较回报吗?

React Native for Android 环境搭建-坑

准备工作

  • 操作系统:OSX

基本环境

Homebrew

新安装

1
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

已安装-务必更新

1
2
3
4
cd /usr/loacl
git fetch origin
git reset --hard origin/master
brew update

Nodejs

安装 nvm

1
2
brew install curl  # 确保安装了 curl
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.29.0/install.sh | bash

安装 Nodejs

此处需要安装4.0以上版本

1
nvm install node && nvm alias default node

NPM 国内源

1
2
npm config set registry https://registry.npm.taobao.org
npm config set disturl https://npm.taobao.org/dist

watchman

watchman 是 Facebook 的一个用于监控文件变更并触发指定操作的工具:

1
brew install watchman

flow

Flow 是一个 JavaScript 的静态类型检查器,建议安装它,以方便找出代码中可能存在的类型错误:

1
brew install flow

Android SDK

SDK:

  • Android SDK Tools
  • Android SDK Platform-tools
  • Android SDK Build-tools
  • Android SDK Platform
  • Android Support Repository

    模拟器

  • API 19以上

    环境变量

  • ANDROID_SDK
  • ANDROID_NDK

详细介绍可以参考 这篇文章

React Native

1
npm install -g react-native-cli

使用步骤

新建工程

1
react-native init demoProject

运行工程

1
react-native run-android

常见问题

启动速度慢

Android App使用 gradle 打包,在第一次启动时,会下载 gradle 和相应的依赖包,所以会造成启动速度缓慢。可以自行修改 app 中 /android/gradle.properties的配置,使用本地已经配置好的环境优化启动速度。

端口占用

如果 Running Packager 提示 “Packager can’t listen on port 8081” ,说明 8081 端口被占用,可以检查是什么程序占用了这个端口并杀掉它:

1
2
$ sudo lsof -n -i4TCP:8081 | grep LISTEN
$ kill -9 <进程id>

加载失败

加载失败
你还需要进行如下设置:

  • 更新 brew 和 watchman :brew update && brew upgrade watchman;参考 brew 安装中的说明
  • 按下菜单按钮呼出菜单
    菜单
    然后点击【Dev Settings】菜单项进入应用的选项界面,再点击【Debug server host for device】选项,填入你的Mac主机的 ip,然后重启 APP ;
    Debug server host for device
    其他选项,按需勾选。
    Auto reload on JS Change - 自动刷新界面

Example App

下载

1
2
git clone https://github.com/facebook/react-native.git
git checkout 0.13-stable

注意选择稳定分支,不要使用 master 分支

运行

Exmaple App 中,Movies 和 UIExplorer 两个是 IOS 和 Android 都可以的,剩下俩个都是只有 IOS 版本的。这两个 APP 和上面说的通过 react-native init方式创建的不同,需要使用不同的方式来启动。
详情

安装依赖

  • react-native依赖

    1
    2
    cd react-native
    npm install
  • Android
    需要 SDK、Build-tools、NDK、Emulator

编译基础

1
2
cd react-native
./gradlew :ReactAndroid:assembleDebug

编译运行

编译过程中需要下载依赖包,所以要耐心等待。

  • UIExplorer

    1
    2
    3
    cd react-native
    ./gradlew :Examples:UIExplorer:android:app:installDebug
    ./package./packager/packager.sh
  • Movies

    1
    2
    3
    cd react-native
    ./gradlew :Examples:Movies:android:app:installDebug
    ./package./packager/packager.sh

Mongodb和Python进行数据统计

Group

Mongodb语法

db.collection.group({ key, reduce, initial [, keyf] [, cond] [, finalize] })

keyf:用来对要 group 的字段进行处理,类似 sql 中通过 date 函数按日期分组

详细文档:http://docs.mongodb.org/manual/reference/method/db.collection.group/

例子:
1
2
3
4
5
6
7
8
9
10
11
db.orders.group(
{
key: { ord_dt: 1, 'item.sku': 1 },
cond: { ord_dt: { $gt: new Date( '01/01/2012' ) } },
reduce: function( curr, result ) {
result.total += curr.item.qty;
result.count++;
},
initial: { total : 0,count:0 }
}
)

等价于

1
2
3
4
SELECT ord_dt, item_sku, SUM(item_qty) as total,count(0) as count
FROM orders
WHERE ord_dt > '01/01/2012'
GROUP BY ord_dt, item_sku

PyMongo语法

1
2
3
4
5
6
7
8
from bson.code import Code
reducer = Code("""
function( curr, result ) {
result.total += curr.item.qty;
result.count++;
}
""")
results = db.orders.group(key={"ord_dt": 1, "item.sku": 1}, condition={"ord_dt": { "$gt": "new Date( '01/01/2012' )" }}, initial={"count": 0,"total:0"}, reduce=reducer)
PS:results 的结果是一个数组,这就受到 mongo 单个数据集大小的限制,最大为20000。

Aggregate

Mongodb语法

图片引自 mongodb.org
上图写明了 mongo 处理聚集的方式,和 linux 中管道的理念一致,每一级的处理结果作为下一级的输入,最终完成数据的计算。
其中$match、$group及更多参见:
Aggregation Pipeline Operators

例子:
1
2
3
4
5
6
db.orders.aggregate([
{$match:{ord_dt: { $gt: new Date( '01/01/2012' ) }}},
{$group:{_id:{ ord_dt: "$ord_dt", sku: "$item.sku" },total:{$sum:"$item.qty"},count:{$sum:1}}},
{$project:{_id:0,ord_dt:"$_id.ord_dt",sku:"$_id.sku",total:1,count:1}}
]
)

PyMongo语法

1
2
3
4
5
6
pipeline = [
{"$match":{"ord_dt": { "$gt": "new Date( '01/01/2012' )" }}},
{"$group": {"_id": {"ord_dt": "$ord_dt", "sku": "$item.sku"}, "total":{"$sum":"$item.qty"},"count": {"$sum": 1}}},
{"$project":{"_id":0,"ord_dt":"$_id.ord_dt","sku":"$_id.sku","total":1,"count":1}}
]
result = db.requestLog.aggregate(pipeline)

PS:aggregate返回的是 cursor,需要进行迭代获取数据,并且可用进行分页查询

Flask中ObjectId 处理

1
2
3
4
5
6
7
8
9
10
11
12
class ObjectIDConverter(BaseConverter):
def to_python(self, value):
try:
return ObjectId(base64_decode(value))
except (InvalidId, ValueError, TypeError):
raise ValidationError()

def to_url(self, value):
return base64_encode(value.binary)


app.url_map.converters['objectid'] = ObjectIDConverter

iOS的DeviceToken随生产环境和开发环境变化

当我用XCode直接运行到手机上的时候,Device token是以3开头的,而当我打包上传到fir.im,再下载安装的时候,Device token就变成以5开头了

其实这是生产环境和开发环境的问题,在这两个环境下Device token是不同的

如果你手机中的App是通过XCode直接安装的话,那么你的App就属于开发环境,想要推送成功就需要创建开发环境的证书;
如果你的App是打包成ipa文件安装的(不管是正式上线还是自己测试),那么就是生产环境,需要创建生产环境证书,这两个环境的Device token是不同的

Nginx+Tomcat 配置 https

SSL/TLS简介

安全传输层协议(TLS)与其前辈加密套接字(SSL)都是用于保证 Web 浏览器与 Web 服务器通过安全连接进行通信的技术。利用这些技术,我们所要传送的数据会在一端进行加密,传输到另一端后再进行解密(在处理数据之前)。这是一种双向的操作,服务器和浏览器都能在发送数据前对它们进行加密处理。

SSL/TLS 协议的另一个重要方面是认证。当我们初始通过安全连接与 Web 服务器进行通信时,服务器将提供给 Web 浏览器一组“证书”形式的凭证,用来证明站点的归属方以及站点的具体声明。某些情况下,服务器也会请求浏览器出示证书,来证明作为操作者的“你”所宣称的身份是否属实。这种证书叫做“客户端证书”,但事实上它更多地用于 B2B(企业对企业电子商务)的交易中,而并非针对个人用户。大多数启用了 SSL 协议的 Web 服务器都不需要客户端认证。

证书申请

使用 OpenSSL 生成 SSL Key 和 CSR

域名,也称为 Common Name,因为特殊的证书不一定是域名:example.com

组织或公司名字(Organization):Example, Inc.

部门(Department):可以不填写,这里我们写 Web Security

城市(City):Beijing

省份(State / Province):Beijing

国家(Country):CN

加密强度:2048 位,如果你的机器性能强劲,也可以选择 4096 位

按照以上信息,使用 OpenSSL 生成 key 和 csr 的命令如下

1
openssl req -new -newkey rsa:2048 -sha256 -nodes -out example_com.csr -keyout example_com.key -subj "/C=CN/ST=Beijing/L=Beijing/O=Example Inc./OU=Web Security/CN=example.com"

PS:如果是泛域名证书,则应该填写*.example.com
你可以在系统的任何地方运行这个命令,会自动在当前目录生成 example_com.csr 和 example_com.key 这两个文件
这个 CSR 文件就是你需要提交给 SSL 认证机构的,当你的域名或组织通过验证后,认证机构就会颁发给你一个 example_com.crt
而 example_com.key 是需要用在 Nginx 配置里和 example_com.crt 配合使用的,需要好好保管,千万别泄露给任何第三方。

Nginx

证书处理

Nginx和Tomcat不一样,它要求的是证书文件 .crt 和私钥 .key 。遗憾的是,我们的私钥在keystore里面,而JDK自带的keytool并不提供私钥的导出功能,所以我们得借助第三方工具来导出私钥。

1. 导出私钥

有一个开源的私钥导出工具叫做 java-exportpriv 。它是一个简单的java程序,你下载以后参考它的说明,编译,然后运行即可,非常简单,我就不多罗嗦了。

2. 创建 crt

和Apache不一样,Nginx没有Certificat Chain这个参数,所以你要把你的证书和中间证书合并。合并证书很简单,创建一个先的文件 oschina-chain.crt,内容如下:

1
2
3
4
5
6
-----BEGIN CERTIFICATE-----
这里是你证书的内容
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
这里是中间证书的内容
-----END CERTIFICATE-----

修改配置

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 443 ssl;
server_name localhost;
ssl on;
ssl_certificate /oschina/webapp/oschina-chain.crt;
ssl_certificate_key /oschina/webapp/oschina.key;

location / {
include proxy.conf;
proxy_pass https://61.145.122.155:443;
}

}

强制 https

1
2
3
4
5
server {
listen 80;
server_name localhost;
rewrite ^/(.*) https://$server_name/$1 permanent;
}

增强

但是这么做并不安全,默认是 SHA-1 形式,而现在主流的方案应该都避免 SHA-1,为了确保更强的安全性,我们可以采取迪菲-赫尔曼密钥交换

首先,进入 /etc/ssl/certs 目录并生成一个 dhparam.pem

1
openssl dhparam -out dhparam.pem 2048 # 如果你的机器性能足够强大,可以用 4096 位加密

生成完毕后,在 Nginx 的 SSL 配置后面加入

1
2
3
4
5
6
7
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";
keepalive_timeout 70;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

同时,如果是全站 HTTPS 并且不考虑 HTTP 的话,可以加入 HSTS 告诉你的浏览器本网站全站加密,并且强制用 HTTPS 访问

1
2
3
add_header Strict-Transport-Security max-age=63072000;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

Tomcat

证书

Tomcat要求的是包含签名过证书的keystore文件和keystore密码。所以我们要先把证书导入keystore

1
$JAVA_HOME/bin/keytool -import -alias oschina -trustcacerts -file oschina.p7s  -keystore oschina.keystore

上面的命令中 alias “oschina” 和之前申请证书的时候输入的 alias 要一致。

修改Tomcat配置

1
2
3
4
5
<Connector SSLEnabled="true" acceptCount="100" clientAuth="false"
disableUploadTimeout="true" enableLookups="false" maxThreads="25"
port="8443" keystoreFile="/oschina/webapp/oschina.keystore" keystorePass="xxxxxxx"
protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https"
secure="true" sslProtocol="TLS" />