<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>zhaozijie (失控)</title>
    <link>https://ruby-china.org/zhaozijie</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>[上海] 招聘 Rails 工程师 (全职，实习)</title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/74f0748ccf327b7fd1152a073812d74f.jpg" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="公司背景"&gt;公司背景&lt;/h4&gt;
&lt;p&gt;我们是一家崇尚硅谷文化的创业公司，专注于开发企业应用，为企业带来便捷。&lt;/p&gt;
&lt;h4 id="职位描述"&gt;职位描述&lt;/h4&gt;
&lt;p&gt;使用 Ruby on Rails 开发新网站，以及现行项目的维护。&lt;/p&gt;
&lt;h4 id="待遇相关"&gt;待遇相关&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;工作时间：5 天/周；&lt;/li&gt;
&lt;li&gt;工作地点：上海，张江，张江创意大厦，下地铁走路 10 分钟；&lt;/li&gt;
&lt;li&gt;配显示器 (Dell U Series )&lt;/li&gt;
&lt;li&gt;五险一金&lt;/li&gt;
&lt;li&gt;技术书籍全部报销&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="我们的协作方式"&gt;我们的协作方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Slack 做即时通讯&lt;/li&gt;
&lt;li&gt;Gitlab 代码管理&lt;/li&gt;
&lt;li&gt;Tower 做任务分发&lt;/li&gt;
&lt;li&gt;Dropbox 文件共享&lt;/li&gt;
&lt;li&gt;phabricator 代码审查&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="职位要求"&gt;职位要求&lt;/h4&gt;&lt;h6 id="[ 职位一：Ruby 高级开发工程师 2 人 ] 待遇 10 K - 20 K(具体看能力订)"&gt;[ 职位一：Ruby 高级开发工程师 2 人 ] 待遇 10 K - 20 K(具体看能力订)&lt;/h6&gt;
&lt;p&gt;职位描述&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;负责团队技术方案的制定；&lt;/li&gt;
&lt;li&gt;带领开发工程师负责产品开发；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;职位要求&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;2 年以上 Ruby 或 Rails 实际工作经验；&lt;/li&gt;
&lt;li&gt;熟悉 MySQL 关系数据库和 MongoDB、Redis 等 NoSQL 数据库；&lt;/li&gt;
&lt;li&gt;良好的代码书写和编程习惯、非常强的学习能力，解决问题的能力；&lt;/li&gt;
&lt;li&gt;有团队管理经验，能够带领团队高质量快速交付产品；&lt;/li&gt;
&lt;/ol&gt;
&lt;h6 id="[ 职位二：Ruby 初级开发工程师 2 人 ] 待遇 5 K - 10 K"&gt;[ 职位二：Ruby 初级开发工程师 2 人 ] 待遇 5 K - 10 K&lt;/h6&gt;
&lt;p&gt;职位描述：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;参与、协助产品的需求分析、设计、实现、测试、集成以及维护；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;职位要求：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;计算机及相关专业大专或大专以上学历&lt;/li&gt;
&lt;li&gt;1 年以上 Ruby 或 Rails 开发经验；&lt;/li&gt;
&lt;li&gt;熟练使用 SQL，并且了解常见的数据库系统&lt;/li&gt;
&lt;li&gt;熟悉 Ruby 编程和 Rails 编程，追求编写优雅的代码；&lt;/li&gt;
&lt;/ol&gt;
&lt;h6 id="[ 职位三：Ruby 初级开发工程师 1 人 ] 待遇 100-200/每天"&gt;[ 职位三：Ruby 初级开发工程师 1 人 ] 待遇 100-200/每天&lt;/h6&gt;
&lt;p&gt;职位描述：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;参与、协助产品的需求分析、设计、实现、测试、集成以及维护；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;职位要求：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;在 Mentor 的指导下 完成业务模块的开发&lt;/li&gt;
&lt;li&gt;了解 html css js 能用编辑器编写简单的 html 页面&lt;/li&gt;
&lt;li&gt;了解过任何一种 MVC 服务端开发框架&lt;/li&gt;
&lt;li&gt;了解 sql 语句&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;加分项：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;熟悉 git 任意版本控制工具;&lt;/li&gt;
&lt;li&gt;有 Github;&lt;/li&gt;
&lt;li&gt;学过 Rails 或有别的语言开发 Web 应用的经验 (需要附上作品)&lt;/li&gt;
&lt;li&gt;对 Rails 充满热情;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="联系方式"&gt;联系方式&lt;/h4&gt;
&lt;p&gt;发邮件到 &lt;strong&gt;hi@bringstudio.com&lt;/strong&gt; 。&lt;/p&gt;

&lt;p&gt;上班地点距离广兰路地铁站或者金科路步行 12 分钟左右：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/9919d83ce4664c695248742ec88fdb0e.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;工作环境的图片：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/c3f751d3227f52a1b9c58e613076fa8a.jpeg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2015/ce7eaa3f5e783f33a07f0d9cf55ac6bf.jpeg" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>zhaozijie</author>
      <pubDate>Mon, 24 Aug 2015 08:34:00 +0800</pubDate>
      <link>https://ruby-china.org/topics/27048</link>
      <guid>https://ruby-china.org/topics/27048</guid>
    </item>
    <item>
      <title>Docker 入门</title>
      <description>&lt;h4 id="Docker是什么"&gt;Docker 是什么&lt;/h4&gt;
&lt;p&gt;Docker 是一个开源的应用容器引擎，让开发者可以打包他们的应用以及依赖包到一个可移植的容器中，然后发布到任何流行的 Linux 机器上，也可以实现虚拟化。&lt;/p&gt;

&lt;p&gt;上面是官方的解释，用通俗一点的话来形容就是用下面这样的一个集装箱 (container) 把运行的程序装进去，放到任何 Linux 机器上都可以。
&lt;img src="https://l.ruby-china.com/photo/2015/1d1dc201701c3716d80b3016f8ae546f.png" title="" alt=""&gt;&lt;/p&gt;
&lt;h5 id="docker 和 virtual machine不一样的地方"&gt;docker 和 virtual machine 不一样的地方&lt;/h5&gt;
&lt;p&gt;docker 更加轻量，需要更少的资源。
但是 vm 的隔离更加好。&lt;/p&gt;
&lt;h5 id="优点"&gt;优点&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;快速交付和部署&lt;/li&gt;
&lt;li&gt;更容易扩展&lt;/li&gt;
&lt;li&gt;更高效的虚拟化&lt;/li&gt;
&lt;li&gt;更加简单的管理&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="docker 的架构"&gt;docker 的架构&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Daemon&lt;/li&gt;
&lt;li&gt;Client&lt;/li&gt;
&lt;li&gt;Docker.io Registry&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="Docker的几个重要概念"&gt;Docker 的几个重要概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;镜像 (Image)

&lt;ul&gt;
&lt;li&gt;Docker 镜像就是一个可读的模板。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;容器 (Container)

&lt;ul&gt;
&lt;li&gt; Docker 利用容器来运行应用。&lt;/li&gt;
&lt;li&gt;容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;仓库 (Repository)

&lt;ul&gt;
&lt;li&gt;仓库是集中存放镜像文件的场所。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="安装"&gt;安装&lt;/h4&gt;
&lt;p&gt;ubuntu14.04 默认的源有 docker，但是版本太老了，我们演示下通过 Docker 源安装最新版本:
首先确认你的 APT 系统能够处理 https 的 URLs：如果你的主机不存在 /usr/lib/apt/methods/https 这个文件，先安装 apt-transport-https 包：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; /usr/lib/apt/methods/https &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  apt-get update
  apt-get &lt;span class="nb"&gt;install &lt;/span&gt;apt-transport-https
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后添加 Docker 镜像的 key 到你的本地 local keychain。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-key adv &lt;span class="nt"&gt;--keyserver&lt;/span&gt; hkp://keyserver.ubuntu.com:80 &lt;span class="nt"&gt;--recv-keys&lt;/span&gt; 36A1D7869245C8950F966E92D8576A8BA88D21E9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加 Docker 镜像到 apt 软件源，更新和安装 lxc-docker 包。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"echo deb https://get.docker.com/ubuntu docker main&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&amp;gt; /etc/apt/sources.list.d/docker.list"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;lxc-docker
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后使用下面的命令查看是否安装成功。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker version
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="Docker起步"&gt;Docker 起步&lt;/h4&gt;
&lt;p&gt;如果搞明白 Docker 的基本概念，使用起来非常简单。下面我们开始创建我们的第一个容器：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run ubuntu:14.04 /bin/echo &lt;span class="s1"&gt;'Hello world'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果是第一次使用的话，应该会出现如下类似的输出：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Unable to find image &lt;span class="s1"&gt;'ubuntu:14.04'&lt;/span&gt; locally
511136ea3c5a: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;511136ea3c5a: Download &lt;span class="nb"&gt;complete
&lt;/span&gt;f3c84ac3a053: Download &lt;span class="nb"&gt;complete
&lt;/span&gt;a1a958a24818: Download &lt;span class="nb"&gt;complete
&lt;/span&gt;9fec74352904: Download &lt;span class="nb"&gt;complete
&lt;/span&gt;d0955f21bf24: Download &lt;span class="nb"&gt;complete
&lt;/span&gt;Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;ubuntu:14.04
Hello world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;来解释一下这个其中的过程，docker run 的作用是创建一个容器。如果不熟悉一个命令的作用可以使用 --help 参数。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--help&lt;/span&gt;
Usage: docker run &lt;span class="o"&gt;[&lt;/span&gt;OPTIONS] IMAGE &lt;span class="o"&gt;[&lt;/span&gt;COMMAND] &lt;span class="o"&gt;[&lt;/span&gt;ARG...]

Run a &lt;span class="nb"&gt;command &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;a new container
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ubuntu:14.04 是我们的镜像，docker 会先去本地查找是否有 docker 镜像，如果没有会去 docker 仓库下载。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Unable to find image &lt;span class="s1"&gt;'ubuntu:14.04'&lt;/span&gt; locally &lt;span class="c"&gt;# 本地没有找到&lt;/span&gt;

Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;ubuntu:14.04 &lt;span class="c"&gt;# 成功下载 可以使用docker images 来查看本地已经有的&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/bin/echo 'Hello world'&lt;/code&gt;  是我们想在容器运行的命令。如果我们看到终端输出 Hello world.&lt;/p&gt;
&lt;h5 id="守护进程 docker"&gt;守护进程 docker&lt;/h5&gt;
&lt;p&gt;我们上一个 docker 容器运行 echo 命令后就停掉了，这一般都不用于现实生活中。我们创建一个容器让它以守护进程的模式运行。&lt;/p&gt;

&lt;p&gt;我们可以这样运行&lt;code&gt;docker run&lt;/code&gt;命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; ubuntu:14.04 /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"while true; do echo hello world; sleep 1; done"&lt;/span&gt;
3f5f9f691bcc8fc509e5844ca4e9ad1fcbdd59279505b24d74722da9f7dce547
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们运行 docker 但是我们指定了一个&lt;code&gt;-d&lt;/code&gt;标识。&lt;code&gt;-d&lt;/code&gt;标识告诉 docker 运行容器在后台模式运行。所以返回了这个 container 的 id。&lt;/p&gt;

&lt;p&gt;我们也指定了一个相同的镜像:ubuntu:14.04&lt;/p&gt;

&lt;p&gt;我们指定命令行运行：&lt;/p&gt;

&lt;p&gt;/bin/sh -c "while true; do echo hello world; sleep 1; done"&lt;/p&gt;

&lt;p&gt;这是一个输出的 hello word 进程：一个脚本会一直输出"hello word"&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker ps&lt;/code&gt;命令可以查询 docker 进程的所有容器。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps
    CONTAINER ID        IMAGE               COMMAND                CREATED              STATUS              PORTS               NAMES
3f5f9f691bcc        ubuntu:14.04        &lt;span class="s2"&gt;"/bin/sh -c 'while t   About a minute ago   Up About a minute                       nostalgic_perlman
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在我们知道它运行。我们需要在我们容器内使用&lt;code&gt;docker logs&lt;/code&gt;命令来查看产生的 log。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker logs insane_babbage
hello world
hello world
hello world
&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样不断的出现 hello world，我们可以推断出刚才那个那个容器一直在运行。&lt;/p&gt;
&lt;h4 id="Docker 在中国"&gt;Docker 在中国&lt;/h4&gt;
&lt;p&gt;下载 docker 镜像的时候可能比较慢，可以考虑使用 daocloud 来加速。&lt;/p&gt;
&lt;h4 id="总结"&gt;总结&lt;/h4&gt;
&lt;p&gt;docker 的镜像 (image) 就像一个一个的集装箱，容器 (container) 基于镜像运行的进程，&lt;/p&gt;
&lt;h4 id="推荐阅读"&gt;推荐阅读&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://stackoverflow.com/questions/16047306/how-is-docker-io-different-from-a-normal-virtual-machine" rel="nofollow" target="_blank"&gt;http://stackoverflow.com/questions/16047306/how-is-docker-io-different-from-a-normal-virtual-machine&lt;/a&gt;
&lt;a href="https://docs.docker.com/" rel="nofollow" target="_blank"&gt;https://docs.docker.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原文链接：&lt;a href="http://blog.bringstudio.com/dockerru-men/" rel="nofollow" target="_blank"&gt;http://blog.bringstudio.com/dockerru-men/&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;这周末参加 ruby 分享会，分享 docker 相关的，不知道大家有啥建议吗？&lt;/p&gt;</description>
      <author>zhaozijie</author>
      <pubDate>Mon, 23 Mar 2015 01:14:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/24785</link>
      <guid>https://ruby-china.org/topics/24785</guid>
    </item>
    <item>
      <title>Rails 与安全</title>
      <description>&lt;h2 id="简介"&gt;简介&lt;/h2&gt;
&lt;p&gt;作为一个 Web 开发者，经常要面临各种安全问题（SQL 注入，跨站攻击等）。虽然 Rails 默认帮我们做了很多防护，但是如果新手不了解机制，修改默认等配置，可能会导致比较严重的安全隐患。
我们来一个一个介绍下：&lt;/p&gt;
&lt;h2 id="SQL Injection"&gt;SQL Injection&lt;/h2&gt;
&lt;p&gt;SQL 注入至今都是非常常见的 Web 安全漏洞。主要原理是伪造特殊的输入作为参数传给 Web 应用程序，而这些输入大都是 SQL 中都一些组合，通过执行 SQL 语句进而执行攻击者要进行都操作，主要原因是没有过滤用户输入的数据。&lt;/p&gt;

&lt;p&gt;不使用内插的查询，Rails 就自动过滤用户输入的数据。&lt;/p&gt;
&lt;h2 id="Cross Site Scripting"&gt;Cross Site Scripting&lt;/h2&gt;
&lt;p&gt;Rails 默认是做了 XSS 保护的。默认情况下 template 中的所有字符串过滤掉了。比如：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;xss_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;script&amp;gt;alert('test')&amp;lt;/script&amp;gt;"&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= xss_code %&amp;gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面这个代码是安全的。但是下面这个代码就不安全了。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= xss_code.html_safe %&amp;gt;
&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="n"&gt;xss_code&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Cross-site request forgery"&gt;Cross-site request forgery&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;protect_from_forgery&lt;/span&gt; &lt;span class="ss"&gt;with: :exception&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认的情况下，Rails 就通过 protect_from_forgery 来解决 XSRF 漏洞。&lt;/p&gt;
&lt;h2 id="Redirects and Forwards"&gt;Redirects and Forwards&lt;/h2&gt;
&lt;p&gt;这是一个很常见，也比较容易忽略的问题。
比如说 &lt;a href="http://example.com/redirect_to=http://evilwebsite.com" rel="nofollow" target="_blank"&gt;http://example.com/redirect_to=http://evilwebsite.com&lt;/a&gt; 的网站。从一个正常的网站跳转到一个坏的网站，一般到用户都以为是正常的网站，后期如果让用户输入一些账号密码，都是可能导致用户的信息被窃取的。
解决方案如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:url&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;
    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InvalidURIError&lt;/span&gt;
  &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;获取 PATH 部分，确保不跳出自己的站点。&lt;/p&gt;
&lt;h2 id="Mass Assignment"&gt;Mass Assignment&lt;/h2&gt;
&lt;p&gt;可以通过 Strong Parameters 来设置白名单防止 Mass Assignment 攻击。&lt;/p&gt;
&lt;h2 id="暴力破解"&gt;暴力破解&lt;/h2&gt;
&lt;p&gt;这是最常见的攻击方式之一，无限穷举账号密码。这个的解决方案也很简单，Rails 也有比较好的方案。
使用 rack-attack 来保护，当某一个 IP 短时间大量访问你的网站，就把他加到黑名单中。&lt;/p&gt;
&lt;h2 id="敏感数据"&gt;敏感数据&lt;/h2&gt;
&lt;p&gt;不要把 database.yml secrets.yml 放到版本库中，使用 env.yml 来存放。&lt;/p&gt;
&lt;h2 id="总结："&gt;总结：&lt;/h2&gt;
&lt;p&gt;总结一下几点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;不要使用内插式查询。&lt;/li&gt;
&lt;li&gt;不用 html_safe 和 raw。&lt;/li&gt;
&lt;li&gt;redirect 的时候就跳转传来参数的 path 部分。&lt;/li&gt;
&lt;li&gt;使用 Strong Parameters 设置白名单来防止 Mass Assignment 攻击。&lt;/li&gt;
&lt;li&gt;使用 rack-attack 来防止暴力破解。&lt;/li&gt;
&lt;li&gt;不保存敏感数据。&lt;/li&gt;
&lt;li&gt;用 brakeman 来检查代码的安全性。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;参考资料：
&lt;a href="https://www.owasp.org/index.php/Ruby_on_Rails_Cheatsheet" rel="nofollow" target="_blank"&gt;https://www.owasp.org/index.php/Ruby_on_Rails_Cheatsheet&lt;/a&gt;
&lt;a href="https://github.com/presidentbeef/brakeman" rel="nofollow" target="_blank"&gt;https://github.com/presidentbeef/brakeman&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原文：
&lt;a href="http://blog.bringstudio.com/raillsyu-an-quan/" rel="nofollow" target="_blank"&gt;http://blog.bringstudio.com/raillsyu-an-quan/&lt;/a&gt;&lt;/p&gt;</description>
      <author>zhaozijie</author>
      <pubDate>Fri, 13 Mar 2015 15:21:18 +0800</pubDate>
      <link>https://ruby-china.org/topics/24635</link>
      <guid>https://ruby-china.org/topics/24635</guid>
    </item>
    <item>
      <title>Spree 源码导读</title>
      <description>&lt;p&gt;最近在研究 Spree 的代码，写了系列的分析文章。&lt;/p&gt;
&lt;h3 id="前言"&gt;前言&lt;/h3&gt;
&lt;p&gt;Spree 是 rails 社区比较有名的开源电商平台，这里是一些个人在阅读其源码时所做的笔记（本篇主要基于 spree_core 模块，后续会增加其他模块）。&lt;/p&gt;
&lt;h3 id="代码组织"&gt;代码组织&lt;/h3&gt;
&lt;p&gt;spree 采用 rails engines 来实现模块化，将功能划分成以下几个独立的组件（可以通过源码根目录下的 spress.gemspec 证实）：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s.add_dependency 'spree_core', version
s.add_dependency 'spree_api', version
s.add_dependency 'spree_backend', version
s.add_dependency 'spree_frontend', version
s.add_dependency 'spree_sample', version
s.add_dependency 'spree_cmd', version
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;spree_core: 基础功能（模型、必不可少的核心代码）&lt;/li&gt;
&lt;li&gt;spree_api: RESTful API 接口实现&lt;/li&gt;
&lt;li&gt;spree_backend: 后台管理模块&lt;/li&gt;
&lt;li&gt;spree_frontend: 前台展现&lt;/li&gt;
&lt;li&gt;spree_sample: 数据样例&lt;/li&gt;
&lt;li&gt;spree_cmd: 一系列命令行工具&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;开发人员可以根据需要选择使用这些不同的组件，比如自己来构建 frontend 展现，但 spree_core 是必选的。&lt;/p&gt;
&lt;h3 id="spree_core"&gt;spree_core&lt;/h3&gt;&lt;h4 id="概述"&gt;概述&lt;/h4&gt;
&lt;p&gt;spree_core 核心模块是一个 rails engine，通过查看其入口文件 lib/spree_core.rb 得知，初始化代码的逻辑是由 lib/spree/core.rb 来完成的，在这个文件里面定义了 Spree 命名空间模块，以及一些全局的配置方法，比如通过 Spree.user_class=来设置用户模型使用哪一个类（也就是说 spree 并不提供默认的用户模型实现）。&lt;/p&gt;

&lt;p&gt;当开发者提供了具体的用户模型之后，spree 是如何做到扩展的呢？奥秘就在初始化时执行了 config/initializers/user_class_extensions.rb：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Spree&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_prepare&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Spree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user_class&lt;/span&gt;
        &lt;span class="no"&gt;Spree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

        &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Spree&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserApiAuthentication&lt;/span&gt;
        &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Spree&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserReporting&lt;/span&gt;

        &lt;span class="c1"&gt;#用户-角色 多对多关联&lt;/span&gt;
        &lt;span class="n"&gt;has_and_belongs_to_many&lt;/span&gt; &lt;span class="ss"&gt;:spree_roles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="ss"&gt;join_table: &lt;/span&gt;&lt;span class="s1"&gt;'spree_roles_users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="ss"&gt;foreign_key: &lt;/span&gt;&lt;span class="s2"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s2"&gt;"Spree::Role"&lt;/span&gt;
        &lt;span class="c1"&gt;#订单&lt;/span&gt;
        &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;foreign_key: :user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s2"&gt;"Spree::Order"&lt;/span&gt;
        &lt;span class="c1"&gt;#发货地址、账单地址&lt;/span&gt;
        &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:ship_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'Spree::Address'&lt;/span&gt;
        &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:bill_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'Spree::Address'&lt;/span&gt;

        &lt;span class="c1"&gt;# has_spree_role? simply needs to return true or false whether a user has a role or not.&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;has_spree_role?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role_in_question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;spree_roles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;role_in_question&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;last_incomplete_spree_order&lt;/span&gt;
          &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incomplete&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at DESC'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="模型层分析"&gt;模型层分析&lt;/h4&gt;
&lt;p&gt;首先，我们围绕电商的核心模型 Product 来探索 spree 的 model 设计，部分关联如下图所示：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/44513a655f8e37ea4197069a9455992d.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;产品表存储了最基本的信息，使用 Property 来扩展额外的键值对属性。&lt;/p&gt;

&lt;p&gt;采用 Variant 来实现同一产品的不同套餐型号（每个型号的名称来自于全局设定的 OptionType，值采用 OptionValue 存储），关联多个价格是因为它支持针对不同地域定制不同的价格。&lt;/p&gt;

&lt;p&gt;基于不同的 Zone，可以预先设置好特定于区域的发货方式，以便针对多国用户提供差异化的服务；不同的区域可以设置不同的运输方式，然后每个产品的所有版本都需要关联运输方式，在用户下单时系统会根据这个来计算运费。如果一张订单中所购买的商品运输方式不是同一种的话，在执行发货操作的时候，还会自动生成多条对应的发货记录（以便支持类似京东的拆单发货功能）。&lt;/p&gt;

&lt;p&gt;运费作为订单调整金额计算入总价，同时还可以针对订单项进行价格微调，他有个“价格调整”的概念。&lt;/p&gt;

&lt;p&gt;打折促销抽象出了独立的模型，可以设置规则（涵盖了优惠码等情况），通过触发动作来实现具体的金额调整。&lt;/p&gt;

&lt;p&gt;支付功能基于 activemerchant 来集成支付网关，需要在后台维护好支付方式；另外他也支持线下支付的功能，也就是说可以在收到用户的钱之后，由管理员在后台输入相关信息完成支付。&lt;/p&gt;

&lt;p&gt;很多模型都使用 acts_as_paranoid 这个 gem 包来做假删除，另外订单的状态维护是通过 state_machines-activerecord 基于有限状态机原理来实现的。&lt;/p&gt;

&lt;p&gt;关于库存方面，模型关系如下图所示：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/f88293c8c79512ac686fc88b46d4291e.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;需要配置好仓库位置信息（类似京东那种多仓库就近发货概念），然后在维护产品不同版本的时候，可以选择一个位置将其加入到仓库，库存数据是自动计算的，库存动态表记录了变化信息；库存管理还支持不同仓库之间的移货操作，通过 StockTransfer 来完成。&lt;/p&gt;

&lt;p&gt;再来看看实现退货功能的模型：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/9205a5b5fd74c4404618adfcbf6b0865.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;spree 的退货流程，需要管理员手动创建一条退货许可（ReturnAuthorization），然后将需要退回的货物关联到退货项；在实际收到用户寄回的货品时，还要手工创建对应的用户退货（CustomerReturn）记录。&lt;/p&gt;
&lt;h4 id="总结"&gt;总结&lt;/h4&gt;
&lt;p&gt;通过这几天的源码阅读，对 spree 的 core 模块有了大体的了解，具体的 API 细节还需要结合 frontend 以及 backend 这两个上层模块的实现来看才行。从目前理解到的模型层面的知识，对比 D3 的实现之前个人开发的网店应用，可以看到 spree 的设计有如下优势：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;支持多区域：有 Zone 的概念，这样便引申出了一系列的差异化服务，比方说针对不同国家、地区、城市的定价，运费，以及仓储管理（可以参考京东商城的就近发货功能）；&lt;/li&gt;
&lt;li&gt;促销功能比较灵活：促销抽象出了规则跟动作两个概念，前者用于检查某张订单是否满足促销条件，后者用来决定应用优惠的时机，这样两者组合起来就能实现很丰富的玩法了；比方说，你可以创建一个促销算法，在新用户下第一张订单，并购买了指定商品的时候给予优惠。&lt;/li&gt;
&lt;li&gt;订单功能比较完善：spree 不仅支持正常的用户下单操作，还能由管理员在后台直接生成订单；另外就是考虑到了线上、线下两种支付情况。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;缺点的话，个人主观认为，如果想在天朝使用，有些功能不太符合国情，比如支付方式，默认支持的网关都是国外的，针对支付宝、财付通、微信支付等都没有相应的实现；另外，他的某些概念就算实际用不上，比如区域仓储功能，你还是得老老实实的创建一个默认的仓库，然后才能创建产品（这个属性是必须的）；还有产品的套餐属性是全局设定的，虽然说某些情况下可以重用，但我总觉得这个信息应该是独立于每个产品更加合适（或者说，独立于产品类型），灵活度会更高。&lt;/p&gt;</description>
      <author>zhaozijie</author>
      <pubDate>Thu, 05 Mar 2015 12:24:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/24472</link>
      <guid>https://ruby-china.org/topics/24472</guid>
    </item>
  </channel>
</rss>
