Archive for the ‘ Geek Tweak ’ Category

我是怎么管理密码的

应推友要求,介绍下我现在是怎么管理密码的 :) 。

这个方法不是我首创,我也是受别人启发,但可以简单论述下为什么这个方法好~。

 

这个问题的背景,是这个国家的互联网行业,开发者素养差、管理者道德低的普遍现状(当然,国外也有很多不负责任/不懂如何负责任的网站)。我们对于所有网络服务,普遍处于不信任的状态(特别是国内还多了官僚监管这一层),但是有的时候又不得不用。那么,作为用户,我们如何保护自己的信息安全(这里只谈密码)?

 

理想状况:

  1. 每个网站都使用不同的密码;
  2. 而且都很复杂。

2 保证了如果某个网站技术不太行,保护措施做得不够好,你的密码也不会轻易被破解;而 1 保证了即使在极端情况下,也只会被泄露这一个网站的密码,不影响你在其它网站上的账户。

 

然而,要用人脑来记忆不同网站的不同的复杂的密码,显然是不可行的。所以我们要借助一些辅助措施。有一类软件提供这样的功能,需要的时候,它可以帮你生成一个随机的很复杂的密码,同时把它存起来。你可以注明这是什么网站的密码,然后用一个主密码把这些随机密码都加密起来。所以,软件负责记住不同网站不同的复杂密码,而我们人脑只需要记住一个主密码。

 

这是个好主意,但是还不够好。问题在于那些不同网站的密码都是随机生成的,这也就注定了我们的焦虑(我们失去了把控,只能完全依赖于一个黑盒)。我们总会担心记录弄丢了、损坏了、出门在外忘带了(同步?没网络呢、别人的机器呢?),或者某种平台这个软件不支持等等。总之,我们被 lock-in 了!

 

我现在的方法是,不使用随机密码,而是用主密码+网站名,然后算 sha1。即:

$ sha1sum

password+csdn

^D

对,出来一串“乱码”,就用这串乱码做密码。这样做的好处,只要密码后面的网站名不同,生成的“乱码”就完全不同,即使有几个网站同时不靠谱、同时把你的密码(即刚刚生成的“乱码”)泄露了,你的主密码是绝对安全的(当然,要搞复杂一点)。另一方面,你知道这些“乱码”是怎么生成的,所以你不再依赖于某一个单一的软件,因为 sha1 算法是整个计算机系统的重要基础设施之一,哪里都有它的踪影、久经考验。

 

哦,你会说,这是程序员的玩意儿,非程序员怎么办?非也,算 sha1 的小软件,可是比密码管理软件分布广泛多了。Windows/Linux/Mac OS、手机、平板⋯哪个平台上没有?不但有,而且很简单、很好用,绿色环保!就算没有,照着公开的算法写一个也不是什么难事。

所以,我们与其小心翼翼地备一个密码管理系统,不如大大方方地随便哪放一个算 sha1 的小软件。而且你可以下一个在手机上,真是走到哪用到哪啊。不是有很多人说,非硬件级的方案不用么?手机,这是最好的硬件方案了。:)

还有很重要的,如果因为种种原因,你要用一个公用/别人的电脑,就会对配置 1Password + Dropbox 感觉不爽,毕竟有你重要/隐私的数据在里面,同步下来总不太好。算 sha1 的小软件就无所谓了,随便下一个、随便算一下,然后叉掉就行了(要找一个熟悉的,确保不会保存历史记录)。

 

当然,算法只是一方面,为了方便起见,我们不希望每次都算一遍。所以,可以配合浏览器原生的记住密码、自动填写、自动同步的功能一起使用,我觉得堪称完美了(特别是 Firefox 还可以设置主密码保护已记住的密码)。

我已经使用这个方法一小段时间了,个人感觉良好。唯一的不爽是,有些 SB 网站居然对密码长度有限制,要求不超过 16/20/25/… 位。遇到这种情况,只能一边骂 SB 的同时,一边截短。。。

 

关于 1Password:

1Password 做得太优雅了(只用过 Mac 版),以致于我很乐意帮它做个广告(虽然我没再用它了⋯)。1Password 几个很好的功能:

  • 自动生成随机复杂密码:跟上面主要的思路相冲突,这功能等于没用;
  • 自动记忆、自动填写:浏览器原生支持,相比之下没有优势,等于没用;
  • 密码整理/管理非常方便:这是它比浏览器原生的功能强很多的地方。但是密码之所以需要整理,是因为每一个都很独特、都很重要,我们需要搞得它井井有条。但是当我们用了上面 sha1 的算法之后,会突然觉得没必要整理了。我们让浏览器记住,只是为了它能帮我们自动填写,方便一点而已。实际上,那些密码存在数据库里到底是什么鬼样子,我们一点都不担心,反正需要的时候再算一遍好咯。所以,不是 1Password 做得不够好,而是这功能对我已不再重要。

呵呵,好像说得它一点用处没有的样子,其实我们也可以用 1Password 记上面 sha1 产生的密码,它的好处比如:

  • 虽然密码整理已不再重要,但是它毕竟满好用的,说不定我们什么时候想整理整理呢?
  • 1Password 是跨浏览器的。如果用浏览器原生的记忆密码的功能,并且 Firefox、Chrome 同时用的话,同一个网站的 sha1 可能需要算两遍呵呵。而 1Password 是跨浏览器的,算一次就可以。
  • 除了网站的密码,1Password 还可以用来记信用卡都资料。

另外,如果不太喜欢上面说的所谓的算 sha1 那么 geek 的做法,也是用 1Password 的一个很好的理由,它是非常用户友好的。与 1Password 类似的,可以用开源且恰好免费(好吧,我是想给你一个印象:开源不一定免费)的 KeePass。

(完)

怎样保护用户密码才靠谱

CSDN 密码泄露,把我的也给泄掉了⋯⋯好几年没用了,躺着也中枪啊。幸好只是一个低级别的密码。忙着改密码的同时,也在考虑要不要启用 sha1(master password + domain name) 的密码方案。。。

 

好吧,还是说点有趣的,怎样保护用户密码才靠谱?

1、数据库存储

原始:

save2db(username, password)

进化:

save2db(username, md5(password))

再进化:

random salt = GUID

save2db(username, salt, md5(password.salt))

靠谱方案:

const globalSalt = [a-z]+[A-Z]+[0-9]+[%$#@!~*…]+

random privateSalt = GUID

save2db(username, privateSalt, sha1(globalSalt.password.privateSalt))

可接受的简化方案:

save2db(username, sha1(domain.password.username)

 

为什么要搞个 globalSalt?

globalSalt 写在代码中,也就是说,万一你的数据库泄露了(代码还没泄露),仍然有一道安全防线在保护你的用户。只有一个 salt 的情况下,虽然使得字典攻击变得更难,但如果用户密码不够长、不够复杂,暴力攻击仍是很轻松的事(因为既然你的数据库已经泄露了,用户的 salt 自然也就露掉了)。而有了 globalSalt,只要代码仍然安全(攻击者不知道你 globalSalt 的值为多少的情况下),它就能保证跟用户的密码组合起来变得非常长、非常复杂,超出了暴力破解的实际可能。

 

可不可以只用一个 globalSalt?

不可以。否则,好事者就可能在知道你 globalSalt 的情况为你这个网站专门生成一个字典,一次性搞定所有用户。privateSalt 的存在,使各个用户的 salt 都不一样,因此需要为每个用户生成一个专用的字典,不具备实际可行性。简单地说,globalSalt 防暴力攻击、privateSalt 防字典攻击。

 

简化方案中,可不可以不要 domain,只用 username?

不可以。一、有的用户,即使是用户名 + 密码仍然不够长,抗不了暴力;二、我们不仅是要(尽可能)保证 salt 的全站唯一,而是要全球唯一。有些用户名如 admin, root, webmaster 等非常通用,起不到 salt 应有的作用。

 

2、修改密码

    • 改密码,必先验证老密码,而不是登录着就让改。否则,就给 Session Hijacking 变成进一步的灾难修了条大道。
    • 如果老密码输错了,启用验证码保护。否则,就是 Session Hijacking 后暴力破解的后门。
    • 清 session,使所有登录的(如果支持一个帐号同时多处登录)cookie 失效。改了密码,就应该要求该用户所有会话重新验证。因为用户密码已经改了,表示原密码不再是有效的,甚至可能已经泄露了。

     

    3、其它

      • 传输密码时用 HTTPS;
      • 最好全程 HTTPS,杜绝 Session Hijacking(WIFI 什么的,最容易中招了);
      • 避免 Session Fixation(永远重新生成,不通过 URL 传输 SID);
      • 除特别需要,httpd 进程对代码目录只应该有读权限,而不应有写权限。你懂的,后门啊什么的最讨厌了~;
      • 定期 $ git status(.git 目录为 root 专属),如果不是 working directory clean,那就该报警了喂!
      • Injection、XSS 什么的,呃⋯超纲了,打住~。

       

      虽然都是很简单的事,但如果一条条验证,估计国内绝大部分网站都能找出问题,包括那些人们觉得很牛逼的(CSDN,程序员的社区啊,“全球最大”啊…存储密码用的是最原始的方法)。关于保护用户信息安全,你还有什么想法?

      Git 系列之四:Git 进阶功能

      【TIP】在我们的《Windows 下 Git 配置与使用指南》 中,有介绍大家使用 $ git go 命令。其实,这并非 Git 的原生命令,它是我们自定义的一个 alias(别名),由 $git add、$git commit、$git push 和 $git pull 四个命令组合而成。待熟悉之后,你可以直接使用这些原生命令,或者自定义更适合自己的 alias。

      add

      添加新文件到 Git 代码仓库的索引中

      $ git add filename

      mv

      移动或重命名文件

      $ git mv old-filename new-filename

      rm

      从工作目录和 Git 代码索引中删除文件

      $ git rm filename

      status

      查看目前工作目录的代码状态,自上次提交以来的添加、修改和删除等

      $ git status

      diff

      查看自上次提交以来,本地代码改动的具体情况

      $ git diff

      commit

      提交修改的代码(只是提交到本地的代码库,不会推送到服务器)

      $ git commit -am '修改说明'

      如果觉得刚提交的“修改说明”写得不够好,可输入以下命令调整

      $ git commit --amend

      push

      将自上次 push 以来的,本地历次 commit,推送到服务器

      结合我们的实际,应该这样写:

      $ git push origin master:your-id

      其中,master 是本地的分支名;your-id 填你在服务器上的 id,服务器的版本库里会有以你的 id 为名称的分支。

      pull

      将别人推送到服务器的代码,拉到你的机器里

      $ git pull

      log

      查看修改记录,含作者、时间、修改说明等

      $ git log

      show

      显示具体的代码改动情况

      显示最后一次 commit 修改的内容:

      $ git show

      显示指定 commit 修改的内容:

      【TIP】git log 命令中,每条 commit 会有一长长的字符串,此即 commid id,取其前面五六位即可。

      $ git show commit-id

      branch

      分支管理

      列出所有分支(当前所在分支前会有“*”号):

      $ git branch

      新建分支:

      $ git branch 新分支名

      删除分支:

      $ git branch -d 欲删除的分支名

      【注意!】不要把 ‘-d’ 写成了 ‘-D’,危险!

      • -d:要求:被删除分支的所有修改,已经合并到当前分支;
      • -D:直接删除,未合并的代码,将被丢弃!

      checkout

      恢复某个已修改的文件(撤销未提交的修改):

      $ git checkout file-name

      切换到另外的分支,进行开发:

      $ git checkout branch-name

      【注意!】该命令可能伴随大量的文件增删/修改。Windows 下,改动已被占用的文件可能会被拒绝,导致版本库出现严重问题。如果确实要这样做,安全起见,最好先注销一次。

      merge

      合并指定分支到当前分支:

      $ git merge branch-name

      revert

      还原已提交的修改(已经提交过的修改,可以反悔~)

      还原最近一次提交的修改:

      $ git revert HEAD

      还原指定版本的修改:

      $ git revert commit-id

      stash

      先将未提交的修改暂存起来,接着清除所有改动,使之与没修改时一样。

      若你正在开发功能 A,又需立即去开发功能 B。A 的代码正改到一半,未认真整理,你不想立即提交。此时……请呼叫 stash ~。

      它会使你所有未提交的修改瞬间不见了:

      $ git stash

      它会使刚刚不见了的修改,瞬间又回来了:

      $ git stash pop

      【TIP】以上命令皆有更多参数,另有一些 Git 命令我们此处没有介绍。但是,这已足令你使用 Git 时游刃有余,你会觉得,Git 简直是一件神器!:-)

      【TIP】’$ git help’ 与 ‘$ git help 命令名’ 会在你需要的时候,无私地帮助你。:-)

      附:git push 失败的解决办法

      假设执行操作:

      1. 修改代码
      2. git commit
      3. git push

      此时 push 失败(错误提示:! [rejected] master -> master (non-fast-forward) )

      解决办法:

      $ git pull

      若成功,则:

      $ git push origin master:your-id

      完事。

      若失败(提示:CONFLICT (content): Merge conflict in 文件名),则:

      冲突的文件会有类似下面的代码块:

      <<<<HEAD
      你修改的代码
      ============
      其他人修改的代码
      >>>>>commit id of others'

      考虑你和他人对代码的修改,更新成合适的内容,并删除 <<<、===、>>> 3行标记符号,保存文件。

      $ git commit -am "resolve conflict"
      $ git push origin master:your-id

      更详细的说明,可以阅读 $git push –help 该文档的 NOTE ABOUT FAST-FORWARDS 一节。

      Git 系列之三:Windows 下 Git 配置与使用指南

      一、安装

      默认安装:msysGit

      二、配置

      1、C:\Program Files\Git\etc\gitconfig 添加:
      【注意!】请将第二行最后的 “your-id” 修改成你在服务器上的实际 id,默认是姓名拼音。

      [alias]
          go = "! bash -c \"git pull && git add .; if [ \\\"$*\\\" == \\\"\\\" ]; then git commit -a; else git commit -am \\\"$*\\\"; fi; git push origin master:your-id;\""
      [core]
          autocrlf = false
      [gui]
          encoding = utf-8
      [i18n]
          commitencoding = GB2312
      [user]
          email = xxx@gmail.com
          name = 某某某

      2、C:\Program Files\Git\etc\inputrc 修改两行为:

      set output-meta on
      set convert-meta off

      3、C:\Program Files\Git\etc\git-completion.bash 末尾增加:

      alias ls='ls --show-control-chars --color=auto'

      4、C:\Program Files\Git\etc\profile 末尾增加:

      export LESSCHARSET=utf-8

      【TIP】以上文件最好使用支持 unix 格式的编辑器修改(如 Notepad++、NetBeans),最次也用“写字板”而非“记事本”。

      【TIP】若想了解为什么这样设置,请参见:Windows 下 Git 客户端的选择,及 msysGit 各种中文问题的解决

      三、生成密钥

      安装完后,需要生成一对 Key(这里指密钥),然后才能通过加密的方式和服务器的代码库取得同步。

      到开始菜单,找到“Git Bash”,运行之,并执行以下命令:

      $ ssh-keygen -t rsa

      程序会提示您输入密钥的文件名,直接按回车即可。
      然后会要求你输入一个密码,将来在使用密钥的时候需要提供这个密码。可以输入,也可以不输入直接回车(无论输入还是不输入,都会要求你确认一次)。
      确认完毕后,程序将生成一对密钥存放在以下文件夹:

      C:\Users\Administrator[这里替换成你的用户名]\.ssh

      密钥分成两个文件,一个私钥(id_rsa)、一个公钥(id_rsa.pub)。
      私钥保存在您的电脑上,公钥交项目负责人添加到服务器上。用户必须拥有与服务器公钥所配对的私钥,才能访问服务器上的代码库。

      【注意!】为了项目代码的安全,请妥善保管你的私钥!因为一旦私钥外泄,将可能导致服务器上的代码被泄漏!

      四、使用

      1、克隆代码库

      使用 Windows 资源管理器,打开你打算存放项目代码的文件夹,点右键选择 Git Bash。

      在我们的项目管理系统中,每个项目的首页,都有写明代码克隆的地址,比如我们用于测试目的的沙盒项目:

      $ git clone your-name@testing.aysaas.com:/var/projects/sandbox

      在 Git Bash 中运行这条命令就能将沙盒项目中的所有代码(其实只是几个随便测试的文件)克隆到本地。

      接着您就可以打开习惯的 IDE(如 NetBeans),投入到项目的开发中啦~!

      【TIP】上面命令中的 your-name 要改成你在服务器上实际的用户名。

      2、查看修改差异

      开发过程中,如果你想了解修改了哪些代码,总览所有代码的改动情况,可以在 Git Bash 中输入此命令:

      $ git diff

      【TIP】Git Bash diff 的时候有两个缺点:一、窗口太窄,可能显示不下整行的代码;二、如果代码中有中文,会乱码。如果你碰到这两个问题,可以在项目文件夹下点右键,选择 Git Gui。

      3、提交修改

      每当完成一个阶段的代码,就需要提交代码以记录进展,方便日后查找问题以及团队协作。

      $ git go aaa 修改说明(改动了什么?为什么这样改?)

      【TIP】别忘了 go 后面的 aaa,关于 ‘git go’ 命令的详细说明,请参见 Windows 下 Git 客户端的选择,及 msysGit 各种中文问题的解决

      【TIP】请尽量养成勤提交的好习惯。当代码不幸出现问题时,比较容易找出从什么时刻开始出现问题,并回退到该时刻进行调试,最大限度保护已完成的阶段性工作。

      【TIP】以上命令,都需要在项目目录下运行。Git Bash 在命令提示符前,会显示当前所在的目录。如果当前不在项目目录之下,需要用 cd 命令切换到项目所在目录。
      简单的办法,就是先在资源管理器里打开项目文件夹,再点右键,选择 Git Bash。

      五、总结

      至此,从获取代码、查看差异、到提交代码,整个流程都熟悉了。Git 还有比较高级的技巧,大家可以参考 Git 进阶功能 或在线找进一步的资料学习。

      Git 系列之二:Windows 下 Git 客户端的选择,及 msysGit 各种中文问题的解决

      在 Windows 下用 NetBeans 做 PHP 开发,首先想到的是 NetBeans 的插件:NBGit。
      评价:能用;若需没有的功能,可以自定义菜单调用自定义 bat 脚本;开发不活跃,使用没有信心。

      第二个则是:TortoiseGit,SVN 小乌龟的 Git 版本。
      评价:该有的功能基本都有了,还是不错的。

      另外,TortoiseGit 只是 GUI 工具,使用它需要先安装 msysGit,这是正宗的 Git 之 Windows 版本。msysGit 有个简单的 GUI 工具,及简单的 Explorer 集成;但它自带的 Bash 非常好用,深得 Linux 的真传。

      选择:msysGit。
      理由:
      NBGit 不用说,功能都不完善,还需要自己定制 bat 脚本(若此,则它同样要依赖 msysGit);开发不活跃,很可能 NetBeans 下个版本更新就不能用了;况且,我们还有别的项目,不使用 NetBeans。

      TortoiseGit 从功能上说是完善的,但它只是功能的堆砌而已,使用时完全体会不到 GUI 带来的便利。相反,它让人感觉很繁琐,一个劲地点鼠标,点来点去全是跟菜单打交道,远离了 Git 命令、远离了 Git 输出提示、远离了真相。

      msysGit 的 Bash 非常好用;加上 Git 强大的 alias 功能,我们完全可以自定义一个 $ git go,使得 90% 的情况下只需要这一个命令,即使是不熟悉命令行的 Windows 用户也会觉得很好玩;因为 NBGit、TortoiseGit 都需要 msysGit 做底层,我们直接用底层工具也避免了上层 GUI 带来的额外的 bug。

      需要的配置:

      1、C:\Program Files\Git\etc\git-completion.bash:

      alias ls='ls --show-control-chars --color=auto'

      说明:使得在 Git Bash 中输入 ls 命令,可以正常显示中文文件名

      2、C:\Program Files\Git\etc\inputrc:

      set output-meta on
      set convert-meta off

      说明:使得在 Git Bash 中可以正常输入中文,比如中文的 commit log。

      3、C:\Program Files\Git\etc\profile:

      export LESSCHARSET=utf-8

      说明:$ git log 命令不像其它 vcs 一样,n 条 log 从头滚到底,它会恰当地停在第一页,按 space 键再往后翻页。这是通过将 log 送给 less 处理实现的。以上即是设置 less 的字符编码,使得 $ git log 可以正常显示中文。其实,它的值不一定要设置为 utf-8,比如 latin1 也可以……。还有个办法是 $ git –no-pager log,在选项里禁止分页,则无需设置上面的选项。

      4、C:\Program Files\Git\etc\gitconfig:
      [alias]
      go =
      “! bash -c \”git pull && git add .; if [ \\\”$*\\\” == \\\”\\\” ]; then git commit -a; else git commit -am \\\”$*\\\”; fi; git push origin master:your-id;\””

      说明:强大的 alias,有了这个,我们 90% 的情况下只需要输入 $ git go 这一个命令,免去了先拉后提交再推的繁琐步骤。

      两种用法:
      $ git go

      $ git go aaa 修订说明

      命令后带修订说明时,会直接提交。需要注意的是,在“修订说明”之前,有还个“aaa”,这是个 bug,参数中的第一个会被忽略,所以随便写一个凑数的……。

      若命令行里没有提供修订说明,则会自动弹出一个编辑器,等待输入。默认的编辑器是 Vim。Vim 的使用是很简单的,首先要明白它有两个模式,一个是命令模式、一个是输入模式。Vim 启动的时候默认的是命令模式,需要先按’i’键,进入输入模式;然后就正常编辑;编辑完成之后,将输入法切换回英文状态,按 Esc 重新进入命令模式;此时按 ‘(Shift):wq‘ 并回车,w 表示写入保存、q 表示退出。完毕!

      若实在不习惯 Vim,也可以设置为其它编辑器:

      $ git config --global core.editor "notepad"

      其中 notepad 可以替换为更好用的 wordpad、notepad++ 等(不过它们在命令行里无法直接访问,得先设置 PATH 变量)。

      以上 alias 是为 Windows 定制的,Linux 下可以写得更优雅,不过鉴于使用上没分别,就保持一致吧~。

      [gui]
      encoding = utf-8

      说明:我们的代码库是统一用的 utf-8,这样设置可以在 git gui 中正常显示代码中的中文。

      [i18n]
      commitencoding = GB2312

      说明:如果没有这一条,虽然我们在本地用 $ git log 看自己的中文修订没问题,但,一、我们的 log 推到服务器后会变成乱码;二、别人在 Linux 下推的中文 log 我们 pull 过来之后看起来也是乱码。这是因为,我们的 commit log 会被先存放在项目的 .git/COMMIT_EDITMSG 文件中;在中文 Windows 里,新建文件用的是 GB2312 的编码;但是 Git 不知道,当成默认的 utf-8 的送出去了,所以就乱码了。有了这条之后,Git 会先将其转换成 utf-8,再发出去,于是就没问题了。

      以上,给 Windows 下的同事在 Git Bash 里推代码就比较完美了。不过仍然有 3 个问题:

      1、上面的 alias $ git go 有 bug,代码修订说明之前要输入一串字符凑数;

      2、$ git diff,如果代码里有中文,会显示乱码;

      3、$ git checkout 有时候需要修改/增删很多文件,如果某些文件被占用,会被 Windows 拒绝,导致失败,甚至可能造成版本库出现无法修复的问题。

      这 3 个都是可承受的问题,前两个应该有办法解决;第 3 个归功于文件系统,只能尽量避免 checkout,实在需要的时候先注销一次,就不会有问题了。

      【TIP】该文只是解释说明,具体操作请按《Windows 下 git 配置与使用指南》Wiki 执行。

      Git 系列之一:版本控制的概念、分布式、Git 简介及其工作流程

      UPDATE:这里记录的“Git 工作流程”比较适合于团队大部分人没有使用过 Git 的情况,能够简单无障碍入门;以及,适合于系统比较大,不同的人工作于不同的部分,彼此进度不一的情况。日志写作时,我们正好符合这两个情况,所以采用了所述的流程。随着时间的推移,团队成员使用 Git 已经比较熟练,而我们系统的开发也逐渐成熟,现已转用了另一种流程。大体而言,跟这个思路比较接近:http://nvie.com/posts/a-successful-git-branching-model/ 。

      ——

      注:

      Git 的强大、灵活、好用,毋庸置疑。

      但也正是 Git 的灵活性,在公司推行时,如何执行统一的流程成为一个问题。我想了不少时间才制订出一个办法。

      目的是规范、统一。还有就是,Windows 下的同事,特别是不熟悉命令行的同事,怎样才能使他们好理解,并且觉得简单(之前大家觉得概念太多,难以理解;步骤多,记不住,不小心就搞错,冲突频发)。

      说到 Windows,Git 在 Windows 下不如 Linux 下好使,这也是一个需要考虑的问题。

      同样是在公司 Wiki 上写的,再次拿到 Blog 来凑数呵呵~。

      版本控制

      ——————
      简单地说,就是将在本地开发的代码,定时推送到服务器。每一次修改,记录下它的作者、时间及修改说明等。

      相对的,我们也可以从服务器下拉其他人推送的代码,并了解它的作者、时间、修改说明及其具体的修改内容。

      这样,版本控制给团队协作开发提供了极大的方便。即使是一个人开发,因为它记录下了我们整个的开发历史,也是极有帮助和价值的。

      比如,如果某次修改甚至整个系统出现问题,它也能帮助找回我们珍贵的代码。

      分布式版本控制

      ——————————–
      更进一步,分布式版本控制工具使得我们在本机上即拥有完整的功能,不依赖于服务器,使用更为方便。它们往往也提供其它更好用或更强大的功能,比如灵活的分支管理。

      Git

      ——–
      Git 是 Linux 之父 Linus Trovalds,为管理 Linux 内核代码而建立的,被认为是分布式版本控制工具中的顶级水准。智能、友好、强健、高效。

      Git 工作流程

      —————————-
      1、使用中央服务器辅助协作;

      2、每人在服务器拥有一个以自己 id 为名称的分支;

      3、各人只许推送更新到自己的分支,不允许推送到别人的分支或者 master;

      4、master 由专人管理,在合适时 merge 其它分支(开发初期每日自动 merge 各人分支,生产化后则由人工 merge 经过 review 的分支);

      5、代码修改 merge 到 master 后,将同步到所有终端。

      【TIP】:熟悉之后,你可以创建类似 myId_branchName 的其它分支。

      【TIP】:以上只是概念介绍,至于具体的操作,请参考:《Windows 下 Git 配置与使用指南》《Git 进阶功能》

      Debian Squeeze/sid + Apache + Redmine 0.9.3/trunk + MySQL + git/hg

      Update: 已可直接从库里安装了~。

      # apt-get install redmine redmine-mysql libapache2-mod-passenger
      _________________________________________________________________________

      按:这是在公司 WIKI 上写的文章,难得这么认真,拿出来分享一下 :)。对比过十多个项目管理系统,然后在比较好的 Redmine, Trac, Mantis 三个当中选择了 Redmine。文中以 Debian Squeeze 为例,但其它 GNU/Linux 特别是 Debian Lenny / Sid、Ubuntu 应该基本一致。

      _________________________________________________________________________

      一、废话

      Debian 的库里有最新的 redmine 发布版(0.9.3),一个命令就可以搞定:

      #apt-get install redmine

      However……,安装后运行会出现 http 500 service unavailable 等问题。因为 Debian 只有 2.2.3 的 rails,而 redmine 0.9.3 要求 rails 的版本为 2.3.5。

      我第一个想法是,既然库里有 redmine,就不折腾了,删掉 rails,再手工装一个新版本。可是 redmine 是依赖于 rails 的,要删得一起删。然后我就想,好,保留它,再装一个新版本覆盖之!就是这个想法……,最后遇到了很多问题,又因为对 ror 不熟,在这上面浪费了不少时间。

      没办法,全 purge 掉,实实在在从头来吧。以下以 redmine-trunk 版为例(解决了 0.9.3 中存在的 wiki+php 等一些 bug)。

      注:以下 # 开头的命令需以 root 身份执行。

      二、依赖关系

      #apt-get install ruby rubygems rake mysql-server mysql-client libmysql-ruby libopenssl-ruby
      #gem install rails
      

      三、下载 redmine

      /var/projects#svn checkout http://redmine.rubyforge.org/svn/trunk redmine

      注:稳定发行版本下载 http://rubyforge.org/frs/?group_id=1850

      四、创建数据库

      # mysql -u root -p
      mysql> create database redmine character set utf8;
      mysql> grant all privileges on redmine.* to redmine@localhost identified by 'PASSWORD';
      

      五、数据库配置文件

      /var/projects/redmine#cat config/database.yml
      production:
        adapter: mysql
        database: redmine
        host: localhost
        username: redmine
        password: PASSWORD
        encoding: utf8
      

      六、生成 session key

      #rake generate_session_store
      

      会将结果写入 config/initializers/session_store.rb

      七、初始化数据库结构

      #RAILS_ENV=production rake db:migrate
      

      八、导入默认设置到数据库

      #RAILS_ENV=production rake redmine:load_default_data
      

      九、预览!

      #ruby script/server webrick -e production
      

      然后可以用浏览器访问 http://server:3000/ ,待测试没问题再继续。

      十、用 apache 运行 redmine

      WEBrick 虽然能跑,但是超慢!慢到无法忍受的地步(好奇的是,内存和cpu利用率都超低,它在干嘛呢……)。

      安装:

      #apt-get install apache2 libapache2-mod-passenger

      权限:

      #chmod -R g+w redmine
      #chown -R root:www-data redmine
      

      符号链接:

      #ln -s /var/projects/redmine /var/www/redmine
      

      配置文件:

      #cat /etc/apache2/conf.d/redmine
      RailsEnv production
      RailsBaseURI /redmine
      

      Done:

      #/etc/init.d/apache reload
      $firefox http://server/redmine/
      

      十一、安装/修改 主题/样式(可选)

      Redmine 支持基本的主题自定义(可以覆盖默认的 css)。

      可以到 http://www.redmine.org/wiki/redmine/Theme_List 下载别人的主题包,解压后将包目录放到 public/themes/ 下面。

      #/etc/init.d/apache reload

      之后,即可访问 http://server/redmine/settings?tab=display 选择新的主题。

      不过我觉得还是默认的耐看。我直接对默认的主题做了一点修改:

      --- public/stylesheets/application.css    (revision 3628)
      +++ public/stylesheets/application.css    (working copy)
      
      +tr.priority-1, table.list.issues tr.priority-1 a { color: black; }
      +
      +tr.priority-2, table.list.issues tr.priority-2 a { color: #1c1cf3 /*similar to blue*/; }
      +tr.priority-3, table.list.issues tr.priority-3 a { color: #1c1cf3; font-weight: bold; }
      +
      +tr.priority-4, table.list.issues tr.priority-4 a { color: red; }
      +tr.priority-5, table.list.issues tr.priority-5 a { color: red; font-weight: bold; }
      +
      +tr.status-3, table.list.issues tr.status-3 a,
      +tr.status-4, table.list.issues tr.status-4 a { color: green; font-weight: normal; }
      +
      +tr.status-5, table.list.issues tr.status-5 a,
      +tr.status-6, table.list.issues tr.status-6 a { color: gray; font-weight: normal; }
      

      以上加的几行,使得以不同的颜色区分不同优先级(低/高/紧急等)、不同状态(新建/进行中/反馈/已关闭等)的 issue。

      十二、安装插件(可选)

      很方便,下面是安装 email tls 插件的例子:

      #ruby script/plugin install git://github.com/collectiveidea/action_mailer_optional_tls.git

      更新数据库:

      #RAILS_ENV=production rake db:migrate:upgrade_plugin_migrations
      #RAILS_ENV=production rake db:migrate_plugins
      

      十三、配置 email 发送功能 —— 任务建立/变更时,发送邮件提醒(可选)

      以 GMail 为例,先要安装上一步提到的 email 插件,然后:

      #cat config/email.yml
      production:
        delivery_method: :smtp
        smtp_settings:
          tls: true
          address: "smtp.gmail.com"
          port: 587
          domain: "smtp.gmail.com" # 'your.domain.com' for GoogleApps
          authentication: :plain
          user_name: "xxxxx@gmail.com"
          password: "xxxxxxxxxxxxxxxx"
      

      完了 reload apache 即可。这里 http://server/redmine/settings?tab=notifications 有一些设置选项。我去掉了 bcc,选中了纯文本。

      修改 1:

      --- app/models/mailer.rb    (revision 3628)
      +++ app/models/mailer.rb    (working copy)
      -    subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
      +    subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] #{issue.subject}" 
      
           s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
      -    s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
           s << issue.subject
      

      修改的目的,是使得 redmine 发送的邮件提醒在标题中不要包含 issue 的状态(新建/进行中/已关闭等),从而使得关于同一个问题的所有提醒邮件在 GMail 中都能自动组织成一个会话,非常有利于上下文的理解。

      redmine 默认在邮件标题中包含 issue 的状态信息,使得每一次状态变更的邮件都分散到不同的会话中,很讨厌。

      修改 2:

      --- config/locales/zh.yml    (revision 3628)
      +++ config/locales/zh.yml    (working copy)
      -  text_journal_changed: "{{label}} 从 {{old}} 变更为 {{new}}"
      -  text_journal_set_to: "{{label}} 被设置为 {{value}}"
      -  text_journal_deleted: "{{label}} 已删除 ({{old}})"
      -  text_journal_added: "{{label}} {{value}} 已添加"
      +  text_journal_changed: "{{label}}:{{old}} -> {{new}}"
      +  text_journal_set_to: "{{label}}: -> {{value}}"
      +  text_journal_deleted: "已删除:{{label}} ({{old}})"
      +  text_journal_added: "已添加:{{label}} {{value}}"
      

      对中文翻译的一点修改,这是邮件提醒中对 issue 变更的说明,我觉得太啰嗦了,不直观明了,没有重点。

      注:如果你发现发出去的邮件,issue 链接都是带 3000 端口号的,而你现在用 apache 跑 redmine 不需要端口号,可以去 http://server/redmine/settings 改“主机名称”。这是因为你第九步的时候无意中保存过这个设置。

      十四、打 patch(可选)

      以下是作者提供的一个 patch,使得在 redmine 中,可以将任务指派给一个组/group,该组的所有成员都会收到邮件提醒(只是,该任务不会显示在“我的工作台”里)。

      redmine#wget http://www.redmine.org/attachments/download/2965/0001-Allow-issues-to-be-assigned-to-a-Group.-2964.patch
      redmine#patch -p1 < 0001-Allow-issues-to-be-assigned-to-a-Group.-2964.patch
      

      注一:执行 patch 的时候,它会有一点小抱怨,可不予理会。

      注二:http://server/redmine/groups ,此处可以增删/编辑小组及其成员。

      十五、与版本控制软件集成(Git / Mercurial)

      配置系统(使得只有组内的成员才可以查看/修改项目代码,不同的项目使用不同的组):

      #groupadd saas
      #echo 'if [ "saas" == "$(id -gn $(whoami))" ]; then umask 007; fi' >> /etc/profile
      

      Update:
      在 /etc/profile 中设置 umask 只会影响 login shell,而我们通过 git push 过来的文件仍然是默认的 755 权限(修改 .bashrc 等亦无效)。

      要设置 git push 到服务器的文件的权限为 770(同组成员可写),正确的做法是:

      # echo "session optional pam_umask.so umask=007" >> /etc/pam.d/common-session

      此外,以下方法应该也可以(没有测试):

      # echo "Subsystem git /bin/bash -c 'umask 007; /usr/bin/git'" >> /etc/ssh/sshd_config

      安装 git:

      #apt-get install git-core

      初始化 git 版本库:

      #mkdir /var/projects/saas
      #cd /var/projects/saas
      #git --bare init
      #chown -R root:saas ../saas
      #chmod -R 770 ../saas
      

      版本库初始化完成,若要增加新的开发者(各自使用自己的帐号),由管理员执行:

      #useradd -m -s /bin/bash -g saas username # 注意,若不指定,默认的 dash 会出错
      

      并将其 pub key 添加到 /home/username/.ssh/authorized_keys 之内。

      开发人员在客户端的操作:

      $git config --global user.name "xxx yyy"
      $git config --global user.email "xxx@gmai.com"
      $git clone username@server:/var/projects/saas
      

      测试:

      $cd saas
      $echo hello > world
      $git add .
      $git commit -a -m 'hello world'
      $git push origin master
      

      以后再 push,就不需要 “origin master” 这两个参数了。

      最后,因为初始化版本库的时候我们用了 –bare 参数,在 /var/projects/saas 下是看不到项目代码文件的。

      可以生成一份代码拷贝,用来在 apache 下进行测试:

      #cd /var/www
      #git clone /var/projects/saas
      #chown -R root:saas saas
      #chmod -R 770 saas
      #addgroup www-data saas
      

      开发者 push 完后,apache 下的代码并不会自动更新,若想测试(这个测试相当于预览,因为开发人员 push 之前是需要先在自己的机器上认真测试的)更新后的代码:

      /var/www/saas$git pull # 注意,各用自己的帐号执行,不要用 root
      

      至此,版本管理系统已经正常工作,还剩最后一件事:那就是告诉 Redmine,我们版本库的路径。

      浏览器里打开 Redmine,新建一个项目,然后在“设置”里会有个标签叫“版本库”,选择 git,并在 path 里填 /var/projects/saas 就搞定了!

      另,hg 的设置与 git 非常相似,大部分参数都一样,不另述。

      十六、升级 redmine svn trunk(可选)

      #svn update
      #rake tmp:cache:clear
      #rake tmp:sessions:clear
      

      可能还要执行第七步和第十二步提到的更新数据库的操作。升级之前应该先备份,在测试实例上确认没有问题再升级。

      十七、备份redmine数据

      一是数据库:

      #mysqldump -u redmine -p redmine > redmine.$(date +%F).sql

      二是上传的文件:

      /var/projects/redmine#tar cvzf ../redmine.uploaded.files.$(date +%F).tar.gz files/

      (完)

      两个小问题(lcrypto, Py_InitModule4),记录一下~

      一、cannot find -lcrypto
      编译 fitx 时遇到的。一番 Google 之后,原来是指 libcrypto.so,而这个库属于 libssl-dev 包。
      依赖里没有提这个包,装上后就没事了。

      PS:因为 scim-python 是直接 make install 的,没有打包。fitx 依赖于 scim-python,我没有改依赖关系,而是用 dpkg -i –force- 安装。然后dpkg给出提示说:

      dpkg:fitx:尽管有依赖关系的问题,但不管怎样,还是按照您的要求,继续配置:

      不知怎的,我觉得这段话很好玩。有一种幽默感、亲切感、尊重感,还有……~~~

      二、undefined symbol: Py_InitModule4
      运行 awn-manager 时遇到的。这是配置错误,/usr/bin/awn-manager:

      SITE_PKG = ‘/usr/lib/python2.4/site-packages’

      将其中的2.4改成2.5就OK了。

      原因是,python2.5 中,将 64bit 系统下的 Py_InitModule4 改名为 Py_InitModule4_64 了。改名目的是为了阻止2.4的模块调用2.5的解析器。因为2.5针对64bit机做了不兼容于2.4的修改,以支持 4GB+ 内存。

      #if SIZEOF_SIZE_T != SIZEOF_INT
      /* On a 64-bit system, rename the Py_InitModule4 so that 2.4 modules cannot get loaded into a 2.5 interpreter */
      #define Py_InitModule4 Py_InitModule4_64
      #endif

      http://svn.python.org/projects/python/trunk/Include/modsupport.h

      In Python 2.4, indices of sequences are restricted to the C type int. On 64-bit machines, sequences therefore cannot use the full address space, and are restricted to 2**31 elements. This PEP proposes to change this, introducing a platform-specific index type Py_ssize_t.

      As the proposed change will cause incompatibilities on 64-bit machines, it should be carried out while such machines are not in wide use (IOW, as early as possible).

      http://www.python.org/dev/peps/pep-0353/

      Netscape/Mozilla 趣史

      按:今天被“Mozilla 的圣经”勾起了兴趣,进而了解了更多一些历史。现简单记录一下,也算是对 Netscape 历经14年风雨动荡并终于在2008年初寿终正寝的一点纪念和致敬吧。

      一、Netscape/Mozilla 是谁?

      对于年纪不大且不大关心 WEB 的人来说,这个问题是很正常的。

      非严格来说,Netscape(网景)是一个公司及其出品的 WEB 浏览器的名字。正是这个浏览器吸引并教育了最早的一批万维网用户,并且创造和引进了许多至今仍在应用的 WEB 技术和标准。

      众所周知的是,Netscape 最终被微软的 IE(Internet Explorer)干掉了。

      1998年,Netscape 被 AOL(美国在线)以4百20万美元的价格收购(微软败诉于反垄断案后,AOL 籍此获得了7.5亿的赔偿),后 AOL 又被 TimeWarner(时代华纳)收购。

      AOL 在获得巨额赔偿的同时,还吃了微软一个“7年内可以无限制使用和散布IE”的糖衣炮弹。
      2003年,网景终于被宣布解散,只剩少部分人继续维护和开发。

      2008年初,Netscape 的开发被宣布彻底终止,并将于 2008.2.1 停止所有安全与技术支持。

      如果只是这样,那也无甚好说。传奇的是,Netscape 在卖掉的前夕创立了开源的 Mozilla 社区,而后者诞生了另一款革命性的浏览器(firefox)。形象地说,Netscape 的躯体已经死了,而血脉流传到了 Mozilla。

      二、Mozilla 吉祥物

      Mozilla 在作为开源计划的名称之前,早已在 Netscape 使用多年,包括作为开发代号、作为吉祥物(那是一只类暴龙,原先是绿色,现在进化到了红色)等。

      以下有很多 Mozilla 吉祥物的卡通图,有些满 funny 的。

      The Mozilla Museum:
      http://home.snafu.de/tilman/mozilla/

      Dave Titus(Mozilla 卡通形象的设计者) T-Shirt collection:
      http://www.ex-mozilla.org/teeshirtart/

      三、图形网页浏览器的鼻祖 & Mozilla 名字的来源

      虽然 Netscape 在早期很有影响力,但史上第一个图形化的 Web 浏览器应该是 Mosaic。它由 NCSA(National Center for Supercomputing Applications,美国国家超级计算应用中心,设立于伊利诺大学)发布于1993年。早年的领头羊 Netscape 以及现时的霸主 IE 虽没有直接使用它的源码,但都与之有很深的渊源。

      对此感兴趣的话,可以看看 IE 原作者的回忆文章。
      Memoirs From the Browser Wars:http://www.ericsink.com/Browser_Wars.html

      而 Mozilla 的名字即是由 Mosaic 杀手(Mosaic killer)变化而来(Moz+illa)。

      四、Mozilla 的圣经

      显然,Mozilla 并没有出版过这么一本书。然而,如果你使用 Netscape 或者 firefox 的话,只需在地址栏输入“about:mozilla”即会看到一段很有启示文学风格的引文,引文的最后注明“from The Book of Mozilla”,使人误以为真有其书。最后会注明一个时间,这个时间代表一个 Mozilla 的重要日期。不同版本的浏览器可能会有不同的引文。

      比如,我现在的版本(IceWeasel 2.0.0.11)是这样的的:

      The Book of Mozilla

      最后野兽终于沒落,异教徒们欢欣鼓舞。不过一切并没有结束,因为灰烬中飞出一只巨鸟。那只鸟望向不信者,并将加诸他们身上。野兽已经重生,力量更甚强大玛门的追随者仓皇逃逸。

      來自 Mozilla 之書,7:15

      7:15 这章节指代2003年7月15日,即 Netscape 被解散、Mozilla 基金会成立的日期。
      野兽的没落指 Netscape 的解散;Mozilla 这只巨鸟在浴火中重生;火与雷指代 Mozilla 的两个旗舰产品:Firebird(Firefox 的原名)、Thunderbird;Netscape 的理念在 Mozilla 基金会“重生”,不再像从前那么依赖AOL,力量变得“更强”;玛门指代微软。

      细细读来,觉得确实蛮坎坷顽强的。如果有兴趣可以在下面站点找到其它章节。

      Mozilla 官方站点,注意看源代码,有注释:
      http://www.mozilla.org/book/

      英文维基页面,比官方站点多两个,并且有浏览器截图:
      http://en.wikipedia.org/wiki/The_Book_of_Mozilla

      中文维基页面,目前比英文维基少两个,没有截图但有中文解释:
      http://zh.wikipedia.org/wiki/Mozilla之书

      五、User Agent String,IE 的童年

      以前一直有个疑问,为什么 IE 的特征串会包含 “Mozilla” 这个词,难不成 IE 初始时用了 Netscape 的代码?

      原来,因为早先 Netscape 非常 NB,很多网站都 Designed Only for Netscape。通过检测客户端特征串识别 Netscape 的浏览器,对其它的浏览器要么不理,要么只给点阉割的功能(怎么这么熟悉,风水轮流转呵)。IE 为了能访问这些站点,就搞起了特征串欺骗,说“我是 Netscape 啊,不要歧视我啊”。。。

      即使后来 IE 成了新的霸主,”Mozilla” 的字眼依然保留在 IE 的特征串中,微软还是满坦然的~。

      IE 还有一个对 Netscape 的恶搞动作。因为 Netscape 能通过 “about:Mozilla” 阅读 “The Book of Mozilla”,据说,在早期的 IE 中输入 “about:Mozilla” 也有个页面——象征死机的蓝屏。

      呵呵,史海轮回,Netscape 一路走好,Firefox 加油!

      杂碎笔记:DOM中的Text节点

      DOM中的Text节点,has no properties错误

      考虑下面一段代码:

      <script type=”text/javascript”>
      function switch_nextSibling_display(hdl){
      hdl=hdl.nextSibling;
      if(hdl.style.display==”none”) hdl.style.display=”block”;
      else hdl.style.display=”none”;
      }
      </script>
      <input type=”button” value=”Click Me!” onclick=”switch_nextSibling_display(this)”/>
      <input type=”button” value=”Hidding Me!”/>

      能得到我们想要的结果吗?否!如果用firebug调试的话(有了firebug太爽了哈~),会得到“……has no properties”的提示。这是因为:两个 input 控件之间有个换行符。在DOM树中,包括换行符、空格及其他普通字符都属于Text节点。不能因为换行符和空格看不见就当它不存在,实际上,前 input 节点的 nextSibling 正是这个Text节点,应该nextSibling.nextSibling才对。不过,在IE中,这个Text节点是被忽略的。为了兼容,有两个方法,一就是不要换行;或者,加上这么一句:if(hdl.nodeType==3) hdl=hdl.nextSibling,nodeType等于3表示Text节点。

      P的问题~
      同样是上面的script,下面的click能预期执行吗?

      <input type=”button” value=”Click Me!” onclick=”switch_nextSibling_display(this)”/><p>I want to hide<p>all this!</p></p>

      同样不能,”all this”是不受影响的。虽然看起来,后一个p只是前一个p的子节点,好像前面的p包括整句话才能称的上是input的“兄弟”。但实际上,p是不能自包含的,当后一个p出现的时候,前一个p就被关闭了,所以那句话等价于:<p>I want to hide</p><p>all this!</p>。同理,p也不能包含div等块级元素。虽然手工不大可能写出这样的语句,但一些半自动的场合难免,这时用JS读/写DOM树可能会碰到一些意想不到的问题。

      var与非var
      JavaScript中,在函数外用不用var定义的变量都是全局的,在函数内部,var定义局部变量,不用var定义全局变量,这似乎没什么问题。但下面一段代码会alert出什么呢?

      <script type="text/javascript">var a=1;function f(){   alert(a);   var a=2;}f();</script>

      1吗?No。因为f()里面声明了与全局变量同名的局部变量,代码在运行前先处理变量声明,所以此a已非彼a了,但是alert的时候a还没有被赋值,所以结果是undefined。倘若把f()里面的声明:var a=2 改成赋值:a=2,此a就仍然是彼a,此时因为a的值尚未被改变,所以结果是1。另,全局的JS变量还能被VBScript访问。