计算时间差并格式化

12 092016
/**
 * 传入时间 计算时间差并格式化
 * @param date time
 * @return string
 */
function timeDiff($time){
	
	$second = time() - strtotime($time); //获取和现在相差分钟数
	//小于一小时
	if($second <= 3600){
		
		$min = ceil($second / 60);
		return $min . '分钟';
	}elseif($second < 86400){
	//小于 24小时
		$hour   = floor($second / 3600);
		$min	= ceil(($second % 3600) / 60);
		
		return $hour . '小时' . $min . '分钟';
		
	}else{
		
		$day 	= floor($second / 86400);   //天数
		$hour	= ceil(($second % 86400) / 3600); //小时数
		
		return $day . '天' . $hour . '小时';
	}
	
}


Api 设计规范 (转载)

09 252016

1. 拼写要准确

接口函数一旦发布就不能改了,要保持兼容性,拼写错误也不能改了,所以要仔细检查拼写,否则会被同行嘲笑很多年。
著名悲剧:unix 的 creat

2. 不仅是英文单词不要拼错,时态也不要错。
比如:
返回bool的判断函数,单数要用 is 复数要用are,这样你的命名就和文档中的描述保持了一致性。
表示状态的变量或者函数要注意时态,比如 onXxxxChanged 表示xxx已经变化了,isConnecting表示正在连接。
正确的时态可以给使用者传递更丰富的信息。

3. 函数最好是动宾结构
动宾结构就是  doSomething,这样的函数命名含义明确
比如: openFile, allocBuffer, setName
如果这个函数的动词宾语就是这个对象本身,那么可以省略掉宾语

4. 属性命名最好是定语+名词
比如 fileName, maxSize, textColor

5. 不要用生僻单词,这不是秀英语的地方,也不要用汉语拼音
比如:rendezvous,估计大多数人要去查词典才知道什么意思,这个词源自法语,是约会的意思。
Symbian OS里有个用它命名的函数,开发Symbian的是英国人,也许人家觉得很平常吧,反正我是查了词典才知道的。

6. 不要自己发明缩写
除非是约定俗成已经被广泛使用的缩写,否则老老实实用完整拼写。
坏例子:  count->cnt, manager->mngr password->pw button->btn
现代的IDE都有很好的自动完成功能,名字长一点没关系的,可读性更重要。

7. 保持方法的对称性,有些方法一旦出现就应该是成对的,
比如 有open就要有close,有alloc就要有free,有add就要有remove,这些单词基本是固定搭配的,使用者就很容易理解。
如果 open对应clear就有点让人困惑了。

8. 站在使用者的角度去思考,API设计也要讲究用户体验。
好的API设计应该是符合直觉,能望文生义的,让使用者能用尽量简洁的代码完成调用。


作者:姚冬
链接:http://www.zhihu.com/question/31363461/answer/51984535
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

APP后端开发系列:登陆系统设计中的注意问题

09 212016

1.APP后端开发系列:登陆系统设计中的注意问题

    app登陆设计·后端开发


想写这个系列很久了,因为之前做这个东西花费了大量的精力,有必要分享出来与大家共享。以前也写了一些关于 APP后端开发的系列文章 由于当初功力不够,很多问题描述不清楚或者解决方案过于复杂、不严谨等。

这一次查了很多资料,问了很多相关人士。准备再结合自己实际工作中的问题再次进行一些补充。就先从登陆的设计开始吧!

越想越糊涂

之前再做这一部分的时候,总想着复杂的技术,说出去多调炸天呀。一般来说登陆的流程是:

image

当时对于安全性过度痴迷,确走偏了道路。首先提交的时候爬信息被人劫持,因此客户端在上传时,进行AES加密,服务端解密出结果。服务端返回的信息也会AES加密,然后客户端解密。

然后这里又带来另外一个问题:加密信息放在了客户端,那么一但客户端被反编译,hacker拿到秘钥,那么对于服务端来说加密就没有任何意义了。又为了不在客户端保存这么敏感的信息,就像秘钥由服务端下发。这样子服务端可随时对秘钥进行变更。

到这里又带来了一个新的问题,感脚一切又回到了起点:下发秘钥要走http,那么依然可能被人劫持。这时候该是加密还是怎么弄呢?如果加密,客户端又放了一个秘钥过去。那这个秘钥依然可能被人反编译。不能再从服务端获取这个秘钥吧?为了确保上个秘钥的安全,生产下一个秘钥……

当时就是陷入了这样的死循环,不可自拔。现在想想真是too young too simple!

简单、有效

首先在这里还是先说一下:如果你的产品刚刚起步,不要过于纠结性能、安全

先说性能:你的产品才推出的时候,冷启动的用户数一般来说不会超过1000人(这已经是很不得了的冷启动人数了)。然后你的并发也不会超过100。这种级别的访问,相信机器硬件就可以帮助你解决。如果你的条件远远超出以上规模,那么你的实力绝对足以应付即将发生的事情。
谈谈安全:安全这个事情,从一开始就要考虑,但是不能过于纠结(我之后可以讲讲我在做短信验证码这一部分的遇到的一个经理)。过早介入,会导致系统开发速度降低,过早做了一些不需要的事情(hacker来搞你也是需要成本的,在你没价值的时候,没人愿意来搞你)。所以早期应该重视开发成本,抓紧时间,早日上线。

另外,安全与性能有时候也是鱼与熊掌。

演化之路

这一部分会有一些代码与图来进行说明。在安全方面逐步演进。前面说前期开发只要快就好,但是这里也要注意一个问题,就是后续升级能够弥补前面的错,要给未来升级留下余地。因为否则你的系统始终留下了一个隐患。

实现功能就好

这是最开始的阶段,重点考虑功能实现。用户提交username + pwd 服务端验证通过后,返回一个令牌token。

这里需要注意的几个部分是要为未来的升级做好准备。我经常遇到的几个初期设计是:

  1. 验证通过后,把用户uid+username+salt等md5后,作为token返回到客户端。

  2. 对token加入时间戳,过期后客户端重新提交username + pwd验证后再发一个token到客户端

  3. 服务端生成一个token后下发到客户端,客户端按照约定的规则加密后请求服务端。

先说第一种带来的问题:生成的token永久不变,那么别人获取到一个token就可以无限制的进行请求。直到你关闭了这个接口为止。为后续安全设计增加了成本。

第二种问题就有点老火了,虽然看似token只在一定时间范围内有效了,但是其实更不安全了。首先客户端需要保存用户的用户名与密码,如果用户手机平时不注重安全,很容易被人窃取。

第三种设计方案,这是我原先干过的一件事,是这三种方案中最垃圾的设计。得出的教训就是:绝不能把任何加密的事情交给客户端。这样子灵活性大打折扣。举例:还是升级接口了,现在本来token生成只是服务端的事情,服务端随时可动态改变规则,现在由于客户端也参与进来了,这事儿就麻烦了,你一改,客户端也要跟着改。没有任何灵活性可言。切记:客户端就接收,然后转发回服务端就好了。别再客户端进行加密!!!

经过这些坑的历练,参考oauth2.0,我现在采用以下方案:

用户提交username + pwd后,服务端返回以下信息:

1
2
3
4
5
{
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "expires_in":3600,
   "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
}

access_token 是用来进行访问的接口的,expires_in 是他的过期时间,到达过期时间后,需要用 refresh_token 来请求服务端刷新 access_token

这里几个重点是:refresh_token 仅能使用一次,使用一次后,将被废弃。另外这个 access_token 只在 expires_in 有效期内有效。

注意: 这里的 expires_in 仅返回秒数就好了。别返回时间戳。因为各个平台计算s的时间戳,不一致,这样子做更方便处理。

访问频率控制

上面我们简单实现了功能,现在app的流量上来了,有些功能也很复杂,如果某个接口访问量太大,会导致服务器崩溃,需要分别对每个接口每次访问设置频率(也可以统一设置每个接口访问的频率)。

一般我的做法是加入一个中间件。每一个接口的访问频率做好一个对应的配置文件。比如:

  • a接口 5s内可访问1次

  • b接口 10s内可访问1次(可能非常耗时,如果同时过多请求会导致服务器崩溃)

那么就把 access_token 与这些关联起来。这里需要用到redis。当用户A进来访问了 a接口 那么设置这个token 5s内不能再次访问。

1
2
3
4
5
6
7
8
9
10
if ($redis->get($key)) {
   // 无法访问,还未到时间
   
   return ;
}

// 设置频率控制key
$redis->setex($key, $expires, $value);

// 访问接口

这里需要考虑几个问题:

设置的访问时间要合理。举例:客户端一般启动的时候会请求多个接口,那么当这些请求到达后,服务端可能拒绝其中一部分访问(因为在频率控制内)

一般来说不需要对所有的接口都进行频率控制,仅仅针对重要的内容以及性能上有要求的接口进行频率控制。

账号安全考虑

现在又进一步了,需要考虑用户账号安全的问题。比如:QQ,有时候会提醒我们你的账号在香港登陆了。如果不是自己所为,赶快修改密码之类的。

实现这个功能,你需要记录每次登陆、启动时每个token对应的ip地址。如果ip地址与上次的ip不在同一个范围(这个规则由自己定,因为有的运营商ip经常变化,比如:长城)。就提醒用户是不是他自己所为,如果不是,就赶快修改密码。

现在很多app在开发之初,都是可以多个设备同时登陆。这样带来的安全问题也很多。如果要做成单个设备登陆,需要每个token对应一个deviceToken。

这一部分就不继续深入讨论下去了。

防DNS劫持

安全工作做得再好,如果有人能够获得大量合法用户的token,来请求你的借口,你也无法识别,因为从行为来看,这一切都是合法用户再进行。

以前为了防止别人获取到合法的信息,我才弄出了很狗血的客户端加密方法。导致后期升级的时候,诸多问题。这个东西其实很简单,使用https来进行请求(可以个人关键接口使用)

token

才开始做app服务端的时候,总想着token的设计。怎么才能生成一个好的token呢?现在想想真不知道当初怎么想的。

token的生成

首先搞明白这个token的作用就是一个令牌,用来标记一个用户的身份。那么首先他要唯一。其次他从客户端上传后,服务端能够验证这个token是由服务端生成的。

所以token生成只要满足以上目的,你随意就好了。当然别把敏感信息暴露出去了。

常用的一种生成方式:

  1. 该用户的uid,如:8888

  2. 该用户的口令,如: 123123

  3. 用户对应的salt,如:abcd

  4. 过期时间戳,如:1468293948

把上面几部分拼接起来:888:123123:abcd:1468293948

token = md5(‘888:123123:abcd:1468293948’);

token的验证

对于token也有两种方法进行验证。一是:服务端生成后,将token保存起来(redis或者mysql中)。客户端穿上来之后,检查是否有该token,如果有取出对应的信息,比如uid,验证是否匹配。

另一种方法是:根据上传的uid,生成对应的token,然后进行比较token结果是否一致(要保障该算法如果给定的值一定,结果必须唯一。常用md5)。

对于个人而言更倾向于第二种方案。第一种方案效率更高(可使用redis存储这个token),但是如果redis一但雪崩,就会造成所有用户登录失效,一定时间内不可登陆。初期越简单、越可靠更好。

总结

这一部分没有太多代码,主要是思路。还有涉及到H5的登陆问题也没有说到。下篇文章会把APP中登陆后,如果搞定H5登陆的问题进行阐述。


转自:https://helei112g.github.io/2016/07/12/1-APP%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91%E7%B3%BB%E5%88%97%EF%BC%9A%E7%99%BB%E

9%99%86%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1%E4%B8%AD%E7%9A%84%E6%B3%A8%E6%84%

8F%E9%97%AE%E9%A2%98/

thinkphp5.0 隐藏 index.php

09 182016
//apache 2.4
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?s=/$1 [QSA,PT,L]
</IfModule>


php7 安装redis 扩展

09 182016

1、下载redis扩展,windows下事比较多x86,x64,ts(线程安全),nts(非线程安全),一定要下载相应的版本,否则加载不上,血淋淋的教训找了2个小时才解决。

2、php_redis.dll 拷贝到ext目录

3、配置php.ini

  extension="D:\app\php7\ext\php_redis.dll"

4、重启环境

phpinfo();看到下面的图片表示成功

blob.png



附:扩展下载地址:http://pecl.php.net/package/redis/3.0.0/windows