trim 在ie8内是没有的。所以有了这个讨论:
http://stackoverflow.com/questions/11219731/trim-function-doesnt-work-in-ie8
但是,是原生的trim还是使用正则表达式的快呢?
有人跑了几个测试
http://jsperf.com/javascript-trim-string/2
http://jsperf.com/jquery-trim-vs-string-prototype-trim/5
证明还是正则快点。
trim 在ie8内是没有的。所以有了这个讨论:
http://stackoverflow.com/questions/11219731/trim-function-doesnt-work-in-ie8
但是,是原生的trim还是使用正则表达式的快呢?
有人跑了几个测试
http://jsperf.com/javascript-trim-string/2
http://jsperf.com/jquery-trim-vs-string-prototype-trim/5
证明还是正则快点。
禁止数据删除,数据一旦增加不允许数据被任何人删除
禁止数据修改,数据一旦建立不允许对数据做修改操作
很多时候我们的数据是只增加,不会删除数据。有些敏感子段一旦数据家里是不允许再修改的,例如银行账户表中的资金子段。
另一个原因是我们防止误操作
我认为在数据库设计时就应该考虑倒这些问题,如果发现数据被删除或者被撰改,亡羊补牢也不晚,我们不能允许再次发生。
你可以取消用户的 DELETE 权限,使之只能做查询操作,但修改(UPDATE)呢?你就无能为力!如果取消UPDATE程序将不能正常运行。
程序设计之初你就应该想到这些问题,如果没有考虑倒,你只能修改现有逻辑。通常的做法是所有表增加一个删除状态子段,删除操作即是更新状态。这种方式也有弊端就是垃圾数据会不停地膨胀。
我认为可以分为两种人,一种是DBA,一种是开发者。这里主要将数据库部分。
CREATE DEFINER=`dba`@`192.168.%` TRIGGER `account_before_delete` BEFORE DELETE ON `account` FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Permission denied', MYSQL_ERRNO = 1001; END
对account表中的记录做删除操作,数据库抛出异常 Permission denied
禁止所有修改操作
DELIMITER $$ CREATE DEFINER=`dba`@`192.168.%` TRIGGER `logging_before_update` BEFORE UPDATE ON `logging` FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Permission denied', MYSQL_ERRNO = 1001; END
限制部分子段修改,其他子段扔允许修改
CREATE DEFINER=`dba`@`192.168.%` TRIGGER `members_before_update` BEFORE UPDATE ON `members` FOR EACH ROW BEGIN SET NEW.`id` = OLD.id; SET NEW.`name` = OLD.name; SET NEW.`chinese_name` = OLD.chinese_name; SET NEW.`english_name` = OLD.english_name; SET NEW.`sex` = OLD.sex; SET NEW.`address` = OLD.address; SET NEW.`zipcode` = OLD.zipcode; SET NEW.`country_code` = OLD.country_code; SET NEW.`mobile` = OLD.mobile; SET NEW.`email` = OLD.email; SET NEW.`qq` = OLD.qq; SET NEW.`question` = OLD.question; SET NEW.`answer` = OLD.answer; SET NEW.`ctime` = OLD.ctime; END
在数据库修改前我们覆盖掉修改的数据,使之更新后数据保持不变。
我们通常使用一个数据库开发,该数据库包含了前后台所有的功能,我建议将前后台等等功能进行分库然后对应各种平台分配用户权限,例如
我们创建三个数据库cms,frontend,backend 同时对应创建三个用户 cms,frontend,backend 三个用户只能分别访问自己的数据库,注意在系统的设计之初你要考虑好这样的划分随之系统需要做相应的调整。
CREATE DATABASE `cms` /*!40100 COLLATE 'utf8_general_ci' */; CREATE DATABASE `frontend` /*!40100 COLLATE 'utf8_general_ci' */; CREATE DATABASE `backend` /*!40100 COLLATE 'utf8_general_ci' */;
backend 负责后台,权限最高
mysql> SHOW GRANTS FOR 'backend'@'localhost'; +--------------------------------------------------------------------------------------+ | Grants for backend@localhost | +--------------------------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'backend'@'localhost' | | GRANT SELECT, INSERT, UPDATE, DELETE ON `cms`.* TO 'backend'@'localhost' | | GRANT SELECT, INSERT, UPDATE, DELETE ON `frontend`.* TO 'backend'@'localhost' | | GRANT SELECT, INSERT, UPDATE, DELETE, CREATE ON `backend`.* TO 'backend'@'localhost' | +--------------------------------------------------------------------------------------+ 4 rows in set (0.04 sec)
frontend 是前台权限,主要是用户用户中心,用户注册,登录,用户信息资料编辑,查看新闻等等
mysql> SHOW GRANTS FOR 'frontend'@'localhost'; +------------------------------------------------------------------------+ | Grants for frontend@localhost | +------------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'frontend'@'localhost' | | GRANT SELECT, INSERT, UPDATE ON `frontend`.* TO 'frontend'@'localhost' | | GRANT SELECT ON `cms`.`news` TO 'frontend'@'localhost' | +------------------------------------------------------------------------+ 3 rows in set (0.00 sec)
cms 用户是网站内容管理,主要负责内容更新,但登陆CMS后台需要`backend`.`Employees`表用户认证,所以他需要读取权限,但不允许修改其中的数据。
mysql> SHOW GRANTS FOR 'cms'@'localhost'; +----------------------------------------------------------------------+ | Grants for cms@localhost | +----------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'cms'@'localhost' | | GRANT SELECT, INSERT, UPDATE, DELETE ON `cms`.* TO 'cms'@'localhost' | | GRANT SELECT ON `backend`.`Employees` TO 'cms'@'localhost' | +----------------------------------------------------------------------+ 3 rows in set (0.00 sec)
cms与backend 通常我们会限制IP地址来源,安全相对好控制。
frontend 主要对外提供服务,我们假设一旦被骇客入侵,所波及的范围被限制在frontend权限下,至少`backend`.`Employees`不会被撰改,CMS内容也得到了保护。
想100%解决数据的安全是非常空难的,但我们至少保护了一部份数据的安全。使其安全不会进一步扩散影响。
数据记录每一次修改我们都需要保留之前的数据,这样可以随时调出历史数据,用户审计等等。
主表
CREATE TABLE `article` ( `article_id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT, `cat_id` SMALLINT(5) NOT NULL DEFAULT '0', `title` VARCHAR(150) NOT NULL DEFAULT '', `content` LONGTEXT NOT NULL, `author` VARCHAR(30) NOT NULL DEFAULT '', `keywords` VARCHAR(255) NOT NULL DEFAULT '', PRIMARY KEY (`article_id`), INDEX `cat_id` (`cat_id`) ) ENGINE=MyISAM ROW_FORMAT=DEFAULT AUTO_INCREMENT=1
本版控制表,用于记录每次变动
CREATE TABLE `article_history` ( `id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT, `article_id` MEDIUMINT(8) UNSIGNED NOT NULL, `cat_id` SMALLINT(5) NOT NULL DEFAULT '0', `title` VARCHAR(150) NOT NULL DEFAULT '', `content` LONGTEXT NOT NULL, `author` VARCHAR(30) NOT NULL DEFAULT '', `keywords` VARCHAR(255) NOT NULL DEFAULT '', PRIMARY KEY (`id`), INDEX `article_id` (`article_id`) ) ENGINE=MyISAM ROW_FORMAT=DEFAULT AUTO_INCREMENT=1
版本控制触发器
DROP TRIGGER article_history; DELIMITER // CREATE TRIGGER article_history BEFORE update ON article FOR EACH ROW BEGIN INSERT INTO article_history SELECT * FROM article WHERE article_id = OLD.article_id; END; // DELIMITER;
任何数据的变化都会复制一份到历史表,我们可以随时比较两个版本数据的变化,我还为此开发了一个类似diff的工具,可以逐行比较,通过色彩变化现实数据的不同。
我有一个表,里面只有固定行数的行记录,这些数据就是配置参数,我们将配置文件保存在数据库中,因为需要做负载均衡而不能使用文件配置文件。
有这样一个需求,这个记录每次修改都要保存历史记录,用于审计等等。我是这样设计该表的
CREATE TABLE `config_fee` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`level` INT(11) NULL DEFAULT NULL COMMENT '层级',
`type` ENUM('Deposit','Withdrawing') NOT NULL DEFAULT 'Withdrawing' COMMENT '类型,存款,取款',
`min_fee` FLOAT(10,2) NOT NULL COMMENT '最低手续费',
`max_fee` FLOAT(10,2) NOT NULL COMMENT '最高手续费',
`ratio` FLOAT(10,2) NOT NULL COMMENT '手续费比例',
`operator` VARCHAR(10) NOT NULL COMMENT '操作者',
`status` ENUM('Current','Trash') NOT NULL DEFAULT 'Current',
`ctime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`mtime` TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)
COMMENT='手续费管理'
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
数据记录的形态
mysql> select type,operator,status,ctime,mtime from config_mtf_fee; +---------+----------+---------+---------------------+---------------------+ | type | operator | status | ctime | mtime | +---------+----------+---------+---------------------+---------------------+ | Deposit | jam | Trash | 2014-07-20 11:10:17 | 2014-07-20 11:10:57 | | Deposit | lucy | Trash | 2014-08-24 11:10:17 | 2014-08-24 11:10:57 | | Deposit | lily | Trash | 2014-08-25 11:10:17 | 2014-08-25 11:10:57 | | Deposit | kitty | Trash | 2014-08-27 11:10:17 | 2014-08-27 11:10:57 | | Deposit | neo | Current | 2014-08-28 11:10:54 | 2014-08-28 11:10:59 | +---------+----------+---------+---------------------+---------------------+ 2 rows in set (0.00 sec)
如上图所示,状态 Current 是当前记录,而Trash是废弃的历史记录。
每次修改数据,首先将Current改为Trash,然后插入一条新数据状态为Current,我们只会使用最后一条状态为current的数据。
我们使用更新触发器控制除了status,mtime意外的字段修改
CREATE DEFINER=`root`@`%` TRIGGER `config_fee_before_update` BEFORE UPDATE ON `config_fee` FOR EACH ROW BEGIN SET NEW.`id` = OLD.id; SET NEW.`level` = OLD.level; SET NEW.`type` = OLD.type; SET NEW.`min_amount` = OLD.min_amount; SET NEW.`min_fee` = OLD.min_fee; SET NEW.`max_fee` = OLD.max_fee; SET NEW.`ratio` = OLD.ratio; SET NEW.`operator` = OLD.operator; SET NEW.`ctime` = OLD.ctime; END;
限制删除的触发器
CREATE DEFINER=`dba`@`192.168.%` TRIGGER `config_fee_before_delete` BEFORE DELETE ON `config_fee` FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Permission denied', MYSQL_ERRNO = 1001; END
[原文连接]http://blog.6park.com/article.php?type=blog&itemid=190323
受骗的快乐
李公尚
在洛杉矶的一个商业中心,有家中国自助餐厅,导游每天带领大量中国游客前往就餐。旅游旺季,前去就餐的中国游客要排很长的队,在餐厅外候餐。
比尔先生开的化妆品商店,紧靠这家中国餐厅。他注意到:中国游客吃完饭,毫无例外地都爱到周围的各个商店转。甚至中国游客排队候餐时,也只有少数人排队,大多数人都去逛商店。他很奇怪,这周围很多商店,除了一家中国人开的卖化妆品和保健品的商店外,其他都很难做中国游客的生意。
很多商店把无法做中国人的生意,归结为语言不通,但比尔不以为然。中国游客进了他的商店,大都转转看看,最多问问价钱,就推门离去。旁边的中国商店和他卖同样的商品,标价比他商店的高,中国游客却购买踊跃,让他觉得不合情理。为了弄清原因,他经常跟着中国游客,进入中国商店观察。
经过一段时间的调查研究,他发现中国游客看到新奇或名牌商品,总是先问价钱,即便明码标价,也明知故问。一旦听说可以打折,不管有用没用,都抢着买。很多人说自己没用,可以带回去送人。对于早就打算要买的目标商品,找到后看了标价,照样问价,然后讨价还价。如砍价成功,就大量购买。一旦买到了“便宜货”,就向同伴炫耀,显示自己精明。于是一传十,十传百,没买那种商品的人就感觉自己吃亏了,于是也去买。有时那种商品甚至会卖到脱销。
比尔想了很久,终于明白了。中国游客所以这样做,是因为相信在美国买的商品,质量全都合格,所以根本不问质量,只问价格。美国人做不了中国人的生意,是因为中国游客喜爱讨价还价,而美国商店却无权随便打折。
他了解到,中国游客到中国商店去,是因为导游和商店有利益关系,把商店的价格说成全美国最低,而商店却趁机标高价格,当中国游客津津乐道讨价还价时,商店再逐步降低价格。
这是个了不起的发现。比尔惊喜自己想出了吸引中国游客的办法。一天,一位中国游客进门,他热情接待,耐心细致地介绍产品特点和功效。他知道那位游客根本听不懂他的话,只是出于礼貌不好拂袖而去。果然,想尽快离去的游客打断他,指着感兴趣的商品询问价钱。他装作老眼昏花,戴上花镜看了半天,又摘掉花镜,似乎不放心,再戴上,最后拿过计算器,按下数字:63.33元。
游客看了,瞪大眼睛看着他,再看看商品下面的标价,明明写着89.99元。便问商品是不是打折。比尔耸耸肩帮,双手一摊,坚定地表示决不打折。说打折损害厂商和店铺的信誉,行业规定正品不许打折。游客狡诘地指着计算器,比划着手势,用生硬的英语问:“真的?这上面的价格没错?”比尔坚定地点点头。
于是游客立即把那款商品拿到柜台,急急忙忙付款。比尔给他结帐时,他不失时机又尽可能多地去拿一些,一起付款。他出门时,比尔见他沾沾自喜,毫无半点愧疚。
游客走后不久,商店里一下进来好几位中国游客,点名要买这款商品。这几位游客尽自己的最大能量,静悄悄地取了商品,大气不敢出地走到柜台付款。一出门,哈哈大笑,议论纷纷。比尔知道,他们在笑他眼老昏花,自豪于“骗了老外”。
很快,他的商店一下涌进了几十名中国游客。很多顾客在抢购那款商品时,还询问其它商品价格,比尔拿着计算器,有的按出正确价格,有的故意按错。按错价格的商品,多被抢购一空。
那天,最后几位旅客离去时,比尔故意问:“你们为什么喜欢这款商品?”几位顾客听了,互使眼色都不说话。一名想练说英语的游客自以为是地说:“美国人开的商店一般不会骗人。旁边那家中国店卖的东西可能是假货,正品哪有打折的?”比尔问:“既然知道他们卖假货,为什么还买?”游客答:“图个便宜,回去送人。我们从美国商店买的东西,一般都留着自己用。”
比尔的这个小招数,屡试不爽,让他的生意大有起色。一次,他故意离开商店,让他女儿替他照看一会儿。她女儿为游客结帐时,按照标价结帐。几名顾客付款后,发现和前面游客付的钱不一样,就质问为什么多收他们的钱。前面几位付了钱的游客听了,赶紧悄悄离开商店。比尔的女儿拿着标价告诉他们并没有错,让他们把前面那些付了款的人找回来问问。这几名游客知道前面的顾客不会回来,为了面子,他们不好退货,只好悻悻离去。出了商店不久,他们和那些等在门外,取笑奚落他们的游客吵骂起来。
比尔和中国游客从此建立了双赢的情谊,共享骗人和受骗的快乐。看到洋洋自得的中国游客从他的店里乘兴而去,他常对人说:中国人真像一群顽皮的孩子,非常可爱。即便受了骗,也欢天喜地。
2015年3月22日
于美国加利福尼亚
http://phantomjs.org/
是一个带有JavaScript宿主环境的webkit引擎封装
比如删除所有特定分辨率的图片
rm -f `identify c*/desire_image.png | grep “282×88” | cut -d'[‘ -f1`
这算是通俗易懂的说明了
http://songqi.sinaapp.com/blog/2012/12/26/php_depend/
关于Tratis是好是坏的讨论已经很多。
http://www.sitepoint.com/php-traits-good-or-bad/
http://www.whitewashing.de/2013/04/12/traits_are_static_access.html
我的意见是,必要的时候就用,但不要滥用。
这个是有国际标准的,即 ISO 8601 标准。
PHP 5.3 引入了 DateInterval 支持该标准,就不用计算时间戳了,优雅多了。
new DateInterval(‘PT4H’) 表示4小时
转自:http://ginew.blog.163.com/
今天阅读 wordpress代码时,发现一个MYSQL的关键词 SQL_CALC_FOUND_ROWS 查看手册后发现此关键词的作用是在查询时统计满足过滤条件后的结果的总数(不受 Limit 的限制)
例如: SELECT SQL_CALC_FOUND_ROWS tid FROM cdb_threads WHERE fid=14 LIMIT 1,10;
假设满足条件的有1000条,这里返回10条。立即使用 SELECT found_rows() AS rowcount; 则返回的 rowcount 为1000;
这样节省了SELECT count(*) AS rowcount的重复查询,可以节省比较可观的时间.
From: http://blog.csdn.net/maray/article/details/5525296
From: http://coolshell.cn/articles/5353.html
Web上的用户登录功能应该是最基本的功能了,可是在我看过一些站点的用户登录功能后,我觉得很有必要写一篇文章教大家怎么来做用户登录功能。下面的文章告诉大家这个功能可能并没有你所想像的那么简单,这是一个关系到用户安全的功能,希望大家能从下面的文章中能知道什么样的方法才是一个好的用户登录功能。以下内容,转载时请保持原文一致,并请注明作者和出处。
首先,我们先来说说用户名和口令的事。这并不是本站第一次谈论这个事了。如何管理自己的口令让你知道怎么管理自己的口令,破解你的口令让你知道在现代这样速度的计算速度下,用穷举法破解你的口令可能会是一件很轻松的事。在这里我想告诉从开发者的角度上来做设计这个用户名和口令的事。下面一几件规则:
首先,我想告诉大家的是,因为HTTP是无状态的协议,也就是说,这个协议是无法记录用户访问状态的,其每次请求都是独立的无关联的,一笔是一笔。而我们的网站都是设计成多个页面的,所在页面跳转过程中我们需要知道用户的状态,尤其是用户登录的状态,这样我们在页面跳转后我们才知道是否可以让用户有权限来操作一些功能或是查看一些数据。
所以,我们每个页面都需要对用户的身份进行认证。当然,我们不可能让用户在每个页面上输入用户名和口令,这会让用户觉得我们的网站相当的SB。为了实现这一功能,用得最多的技术就是浏览器的cookie,我们会把用户登录的信息存放在客户端的cookie里,这样,我们每个页面都从这个cookie里获得用户是否登录的信息,从而达到记录状态,验证用户的目的。但是,你真的会用cookie吗?下面是使用cookie的一些原则。
1)在cookie中,保存三个东西——用户名,登录序列,登录token。
用户名:明文存放。
登录序列:一个被MD5散列过的随机数,仅当强制用户输入口令时更新(如:用户修改了口令)。
登录token:一个被MD5散列过的随机数,仅一个登录session内有效,新的登录session会更新它。
2)上述三个东西会存在服务器上,服务器的验证用户需要验证客户端cookie里的这三个事。
3)这样的设计会有什么样的效果,会有下面的效果,
a)登录token是单实例登录。意思就是一个用户只能有一个登录实例。
b)登录序列是用来做盗用行为检测的。如果用户的cookie被盗后,盗用者使用这个cookie访问网站时,我们的系统是以为是合法用户,然后更新“登录token”,而真正的用户回来访问时,系统发现只有“用户名”和“登录序列”相同,但是“登录token” 不对,这样的话,系统就知道,这个用户可能出现了被盗用的情况,于是,系统可以清除并更改登录序列 和 登录token,这样就可以令所有的cookie失效,并要求用户输入口令。并给警告用户系统安全。
4)当然,上述这样的设计还是会有一些问题,比如:同一用户的不同设备登录,甚至在同一个设备上使用不同的浏览器保登录。一个设备会让另一个设备的登录token和登录序列失效,从而让其它设备和浏览器需要重新登录,并会造成cookie被盗用的假象。所以,你在服务器服还需要考虑- IP 地址,
a) 如果以口令方式登录,我们无需更新服务器的“登录序列”和 “登录token”(但需要更新cookie)。因为我们认为口令只有真正的用户知道。
b) 如果 IP相同 ,那么,我们无需更新服务器的“登录序列”和 “登录token”(但需要更新cookie)。因为我们认为是同一用户有同一IP(当然,同一个局域网里也有同一IP,但我们认为这个局域网是用户可以控制的。网吧内并不推荐使用这一功能)。
c) 如果 (IP不同 && 没有用口令登录),那么,“登录token” 就会在多个IP间发生变化(登录token在两个或多个ip间被来来回回的变换),当在一定时间内达到一定次数后,系统才会真正觉得被盗用的可能性很高,此时系统在后台清除“登录序列”和“登录token“,让Cookie失效,强制用户输入口令(或是要求用户更改口令),以保证多台设备上的cookie一致。
找回口令的功能一定要提供。但是很多朋友并不知道怎么来设计这个功能。我们有很多找回口令的设计,下面我逐个点评一下。
另外这里有文章说的类似的问题: http://blog.jobbole.com/46724/