Skip to content

Commit

Permalink
添加验证码 + 放灌水设置。
Browse files Browse the repository at this point in the history
  • Loading branch information
zven21 committed Jul 17, 2018
1 parent 9c32fd7 commit f87d527
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 14 deletions.
3 changes: 3 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Times from './app/components/times'
import Utils from './app/components/utils'
import Topic from './app/components/topic'
import Editor from './app/components/editor'
import Session from './app/components/session'

// Decorate
Times.humanize()
Expand All @@ -18,3 +19,5 @@ Topic.hookPreview($('.editor-toolbar'), $('.topic-editor'))
Topic.hookReply()
// Editor
window._editor = new Editor()
// 刷新 Captcha
Session.refreshRucaptcha()
9 changes: 9 additions & 0 deletions assets/js/app/components/session.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default class Session {
static refreshRucaptcha() {
$('a.rucaptcha-image-box').click(function(e) {
const img = $(e.currentTarget).find('img:first')
const currentSrc = img.attr('src')
img.attr('src', currentSrc.split('?')[0] + '?' + new Date().getTime())
})
}
}
Binary file removed assets/static/images/phoenix.png
Binary file not shown.
3 changes: 2 additions & 1 deletion lib/mipha/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ defmodule Mipha.Application do
# Start the endpoint when the application starts
supervisor(MiphaWeb.Endpoint, []),
# Start your own worker by calling: Mipha.Worker.start_link(arg1, arg2, arg3)
worker(Cachex, [:app_cache, []])
worker(Cachex, [:app_cache, []]),
worker(PlugAttack.Storage.Ets, [Mipha.PlugAttack.Storage, [clean_period: 60_000]])
# worker(Mipha.Worker, [arg1, arg2, arg3]),
]

Expand Down
4 changes: 2 additions & 2 deletions lib/mipha_web/controllers/auth_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule MiphaWeb.AuthController do

def delete(conn, _params) do
conn
|> put_flash(:info, "You have been logged out!")
|> put_flash(:info, "退出成功。")
|> configure_session(drop: true)
|> redirect(to: "/")
end
Expand Down Expand Up @@ -70,7 +70,7 @@ defmodule MiphaWeb.AuthController do

defp ok_login(conn, user) do
conn
|> put_flash(:info, "Successfully authenticated.")
|> put_flash(:info, "登录成功。")
|> put_session(:current_user, user.id)
|> redirect(to: "/")
end
Expand Down
38 changes: 33 additions & 5 deletions lib/mipha_web/controllers/session_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,49 @@ defmodule MiphaWeb.SessionController do

alias Mipha.Accounts.User
alias MiphaWeb.Email
alias Captcha

def new(conn, _params) do
changeset = User.register_changeset(%User{}, %{})
render(conn, :new, changeset: changeset)
render conn, :new, changeset: changeset
end

def create(conn, %{"user" => user_params}) do
def rucaptcha(conn, _) do
{:ok, text, img_binary} = Captcha.get()

conn
|> put_session(:rucaptcha, text)
|> put_resp_header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")
|> put_resp_header("Pragma", "no-cache")
|> put_resp_content_type("image/gif")
|> send_resp(200, img_binary)
end

def create(conn, %{"user" => user_params, "_rucaptcha" => captcha}) do
# 确定验证码是否正确。
is_true_captcha =
conn
|> get_session(:rucaptcha)
|> String.equivalent?(captcha)

unless is_true_captcha do
changeset = User.register_changeset(%User{}, user_params)

conn
|> put_flash(:danger, "验证码错误,请重新输入")
|> render(:new, changeset: changeset)
end

case Accounts.register_user(user_params) do
{:ok, user} ->
sent_welcome_email(user)

conn
|> ok_login(user)

{:error, %Ecto.Changeset{} = changeset} ->
render(conn, :new, changeset: changeset)
conn
|> put_flash(:danger, "有一个错误就在你注册的时候不小心发生了,请重新注册一下。")
|> render(:new, changeset: changeset)
end
end

Expand All @@ -36,7 +64,7 @@ defmodule MiphaWeb.SessionController do

defp ok_login(conn, user) do
conn
|> put_flash(:info, "Successfully authenticated.")
|> put_flash(:info, "注册成功。如果遇到 BUG,欢迎提 Issue :-)")
|> put_session(:current_user, user.id)
|> redirect(to: "/")
end
Expand Down
6 changes: 5 additions & 1 deletion lib/mipha_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule MiphaWeb.Endpoint do
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Poison
json_decoder: Jason

plug Plug.MethodOverride
plug Plug.Head
Expand All @@ -37,6 +37,10 @@ defmodule MiphaWeb.Endpoint do
key: "_mipha_key",
signing_salt: "7l+jtdHA"

# 安全处理,限制流量。
plug RemoteIp
plug MiphaWeb.Plug.Attack

plug MiphaWeb.Router

@doc """
Expand Down
67 changes: 67 additions & 0 deletions lib/mipha_web/plugs/attack.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
defmodule MiphaWeb.Plug.Attack do
@moduledoc """
安全应对,防灌水。
"""

import Plug.Conn
use PlugAttack

# 注册限制, 每10分钟最多注册一个用户
rule("throttle by ip every 2 minutes only allow register 1 person", conn) do
if conn.method == "POST" and conn.path_info == ["join"] do
throttle conn.remote_ip,
period: 120_000, limit: 1,
storage: {PlugAttack.Storage.Ets, Mipha.PlugAttack.Storage}
end
end

# 发帖限制, 每小时只允许发一个帖。
rule("throttle by ip every 1 hours only allow create 20 topic", conn) do
if conn.method == "POST" and conn.path_info == ["topics"] do
throttle conn.remote_ip,
period: 3_600_000, limit: 20,
storage: {PlugAttack.Storage.Ets, Mipha.PlugAttack.Storage}
end
end

# 评论限制,每2分钟只允许发2个评论
rule("throttle by ip every 2 minutes only allow create 2 reply", conn) do
if conn.method == "POST" and conn.path_info == ["topics"] do
throttle conn.remote_ip,
period: 120_000, limit: 2,
storage: {PlugAttack.Storage.Ets, Mipha.PlugAttack.Storage}
end
end

def allow_action(conn, {:throttle, data}, opts) do
conn
|> add_throttling_headers(data)
|> allow_action(true, opts)
end

def allow_action(conn, _data, _opts) do
conn
end

def block_action(conn, {:throttle, data}, opts) do
conn
|> add_throttling_headers(data)
|> block_action(false, opts)
end

def block_action(conn, _data, _opts) do
conn
|> send_resp(:forbidden, "Forbidden \n")
|> halt()
end

defp add_throttling_headers(conn, data) do
# in seconds
reset = div(data[:expires_at], 1_000)

conn
|> put_resp_header("x-ratelimit-limit", to_string(data[:limit]))
|> put_resp_header("x-ratelimit-remaining", to_string(data[:remaining]))
|> put_resp_header("x-ratelimit-reset", to_string(reset))
end
end
2 changes: 1 addition & 1 deletion lib/mipha_web/plugs/current_user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule MiphaWeb.Plug.CurrentUser do
import Plug.Conn
import MiphaWeb.Session, only: [current_user: 1]

def init(options), do: options
def init(opts), do: opts

def call(conn, _opts) do
conn
Expand Down
1 change: 1 addition & 0 deletions lib/mipha_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ defmodule MiphaWeb.Router do
get "/markdown", PageController, :markdown
get "/join", SessionController, :new, as: :join
post "/join", SessionController, :create, as: :join
get "/rucaptcha", SessionController, :rucaptcha
get "/login", AuthController, :login
get "/logout", AuthController, :delete, as: :logout

Expand Down
12 changes: 9 additions & 3 deletions lib/mipha_web/templates/session/new.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,21 @@
</div>
<% end %>
<div class="form-group">
<%= text_input f, :username, class: "form-control", required: true, placeholder: "Username", tabindex: 1 %>
<%= text_input f, :username, class: "form-control", required: true, placeholder: "请输入用户名", tabindex: 1 %>
<%= error_tag f, :username %>
</div>
<div class="form-group">
<%= text_input f, :email, class: "form-control", required: true, placeholder: "Email", tabindex: 2 %>
<%= text_input f, :email, class: "form-control", required: true, placeholder: "请输入邮箱", tabindex: 2 %>
<%= error_tag f, :email %>
</div>
<div class="form-group">
<%= password_input f, :password, class: "form-control", required: true, placeholder: "Password", tabindex: 3 %>
<%= password_input f, :password, class: "form-control", required: true, placeholder: "请输入密码", tabindex: 3 %>
</div>
<div class="form-group">
<div class="input-group">
<input class="form-control input-lg" placeholder="验证码" name="_rucaptcha" type="text" autocorrect="off" autocapitalize="off" pattern="[a-zA-Z]*" maxlength="5" autocomplete="off">
<span class="input-group-addon input-group-captcha"><a class="rucaptcha-image-box" href="#"><img class="rucaptcha-image" src="<%= session_path(@conn, :rucaptcha) %>"></a></span>
</div>
</div>
<div class="form-group text-center">
<%= submit class: "btn btn-primary btn-block btn-lg", tabindex: 4 do %>
Expand Down
5 changes: 4 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ defmodule Mipha.Mixfile do
{:ecto_enum, "~> 1.1"},
{:cachex, "~> 3.0"},
{:jason, "~> 1.1"},
{:poison, "~> 3.0", override: true}
{:poison, "~> 3.0", override: true},
{:captcha, "~> 0.1.0"},
{:remote_ip, "~> 0.1.4"},
{:plug_attack, "~> 0.3.1"},
]
end

Expand Down
4 changes: 4 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"bcrypt_elixir": {:hex, :bcrypt_elixir, "1.0.8", "940ce8156519574142a3c6e925e7a0eea7367c55b2817b3bd18806b0bbd4254e", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
"captcha": {:hex, :captcha, "0.1.0", "ded2b60e400598b1c6c90ec2b493a08369cc325ae82c20c3f2731dacef38a512", [:make, :mix], [], "hexpm"},
"certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"},
"comeonin": {:hex, :comeonin, "4.1.1", "c7304fc29b45b897b34142a91122bc72757bc0c295e9e824999d5179ffc08416", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
Expand All @@ -27,6 +28,7 @@
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "5.1.1", "cbc3b2fa1645113267cc59c760bafa64b2ea0334635ef06dbac8801e42f7279c", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"inet_cidr": {:hex, :inet_cidr, "1.0.3", "53becd1255198cc4be79435581205803ec6dd21ae92a30bc1967676db641fde0", [:mix], [], "hexpm"},
"jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm"},
Expand All @@ -40,11 +42,13 @@
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.1.5", "8d4c9b1ef9ca82deee6deb5a038d6d8d7b34b9bb909d99784a49332e0d15b3dc", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.6.1", "c62fe7623d035020cf989820b38490460e6903ab7eee29e234b7586e9b6c91d6", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"plug_attack": {:hex, :plug_attack, "0.3.1", "2bffce1cd36267e4c4d2a25ef5e807ff724e0755a37ad5e95da391edabeff692", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
"qiniu": {:hex, :qiniu, "0.4.0", "e9f1b57779d42baca1bb37944e9e01d602939566365dc06754bb7059cb2d856f", [:mix], [{:httpoison, "~> 0.10", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 2.2", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"},
"remote_ip": {:hex, :remote_ip, "0.1.4", "c34e79cdf6058ccff824f6d6ed7ac5282b953c6aa0cf62f949cfe1c3b6119f35", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:inet_cidr, "~> 1.0", [hex: :inet_cidr, repo: "hexpm", optional: false]}, {:plug, "~> 1.2", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"scrivener": {:hex, :scrivener, "2.5.0", "e1f78c62b6806d91cc9c4778deef1ea4e80aa9fadfce2c16831afe0468cc8a2c", [:mix], [], "hexpm"},
"scrivener_ecto": {:hex, :scrivener_ecto, "1.3.0", "69698428e22810ac8a47abc12d1df5b2f5d8f6b36dc5d5bfe6dd93fde857c576", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.11.0 or ~> 0.12.0 or ~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm"},
"scrivener_html": {:hex, :scrivener_html, "1.7.1", "afa35128fb36184bc469e4531bb1ef61b2d91bb29e373157068c62332291485f", [:mix], [{:phoenix, "~> 1.0-pre and < 1.4.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.2", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.1", [hex: :plug, repo: "hexpm", optional: false]}, {:scrivener, "~> 1.2 or ~> 2.0", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm"},
Expand Down

0 comments on commit f87d527

Please sign in to comment.