cowboy添加验证码

参考的http://beebole.com/blog/erlang/how-to-implement-captcha-in-erlang-web-application/,移到cowboy,修改了下;废话不多说,直接贴代码

注意,需要cowboy版本1.0.1

需要imagemagick

sudo apt-get install imagemagick

mac

brew install imagemagick

mac下如果使用convert命令找不到字体,则需要安装下这个

brew install ghostscript

创建工程

rebar-creator create-app testCaptcha

testCaptcha_app

-module(testCaptcha_app).

-behaviour(application).

-export([start/2, stop/1]).

-define(C_ACCEPTORS,  100).

start(_StartType, _StartArgs) ->

    simple_captcha_ets:init(),

    application:start(crypto),
    application:start(cowlib),
    application:start(ranch),
    application:start(cowboy),

    Routes    = route_helper:get_routes(),
    Dispatch  = cowboy_router:compile(Routes),
    Port      = 8080,
    TransOpts = [{port, Port}],
    ProtoOpts = [{env, [{dispatch, Dispatch}]}],
    cowboy:start_http(http, ?C_ACCEPTORS, TransOpts, ProtoOpts).

stop(_State) ->
    simple_captcha_ets:destroy(),
    ok.


route_helper
-module(route_helper).

-export([get_routes/0]).

get_routes() ->
    [
        {'_', [
            {"/captcha", captcha_handler, []},
            {"/captcha_check", captcha_check_handler, []}
        ]}
    ].


simple_captcha
-module(simple_captcha).

-export([create/0,check/2]).

create() ->
    CryptKey  = mochihex:to_hex(crypto:rand_bytes(16)),
    Code = generate_rand(5),

    FileName = lists:flatmap(fun(Item) -> integer_to_list(Item) end, tuple_to_list(now())),
    File = io_lib:format("/tmp/~s.png",[FileName]),

    Cmd = io_lib:format("convert -background 'none' -fill '#222222' -size 175 -gravity Center -wave 5x100 -swirl 50 -font DejaVu-Serif-Book -pointsize 28 label:~s -draw 'Bezier 10,40 50,35 100,35 150,35 200,50 250,35 300,35' ~s", [Code, File]),
    os:cmd(Cmd),

    {ok, BinPng} = file:read_file(File),
    file:delete(File),

    simple_captcha_ets:insert(Code,CryptKey),

    {erlang:list_to_bitstring(CryptKey),BinPng}.

check(CryptKeyBitString,CodeBitString) ->
    CryptKey = erlang:bitstring_to_list(CryptKeyBitString),
    Code = erlang:bitstring_to_list(CodeBitString),
    CryptKeyFromEts = simple_captcha_ets:find(Code),

    case string:equal(CryptKeyFromEts,CryptKey) of
        true ->
            simple_captcha_ets:remove(code),
            true;
        _ ->
            false
    end.


%private
generate_rand(Length) ->
    Now = now(),
    random:seed(element(1, Now), element(2, Now), element(3, Now)),
    lists:foldl(fun(_I, Acc) -> [do_rand(0) | Acc] end, [], lists:seq(1, Length)).

do_rand(R) when R > 46, R < 58; R > 64, R < 91; R > 96 ->  
    R;

do_rand(_R) ->
    do_rand(47 + random:uniform(75)).
simple_captcha_ets
-module(simple_captcha_ets).

-export([init/0,insert/2,find/1,remove/1,destroy/0]).

init()->
  TableName = ets:new(simple_captcha, [public,named_table]),
  {ok, TableName}.

insert(Key,Value) ->
  ets:insert(simple_captcha, {Key, Value}),
  ok.

find(Key) ->
  Result = (
      try ets:lookup_element(simple_captcha, Key, 2) of
        Value ->
          Value
      catch
        _:_ ->
          ""
      end
  ),
  Result.

remove(Key) ->
  try ets:delete(simple_captcha, Key)
  catch
    _:_ ->
      ok
  end.

destroy()->
  try ets:delete(simple_captcha)
  catch
    _:_ ->
      ok
  end.
mochihex
自己下载
captcha_handler
-module(captcha_handler).

-export([init/3]).
-export([handle/2]).
-export([terminate/3]).

init(_Transport, Req, []) ->
  {ok, Req, undefined}.

handle(Req, State) ->
  %CryptKey用于验证的时候用,需本地保存,CapCode为用户提交的数据
  %simple_captcha:check(CryptKey, CapCode)
  {CryptKey,BinPng} =  simple_captcha:create(),

  Req2 = cowboy_req:set_resp_cookie(<<"cap">>, CryptKey, [{path, <<"/">>}], Req),
  {ok, Req3} = cowboy_req:reply(200, [{<<"content-type">>, <<"image/png">>}],BinPng, Req2),
  {ok, Req3, State}.

terminate(_Reason, _Req, _State) ->
  ok.
captcha_check_handler
-module(captcha_check_handler).

-export([init/3]).
-export([handle/2]).
-export([terminate/3]).

init(_Transport, Req, []) ->
  {ok, Req, undefined}.

handle(Req, State) ->
  {CryptKey,_} = cowboy_req:cookie(<<"cap">>, Req,<<"">>),
  {ok, PostVals, Req2} = cowboy_req:body_qs(Req),
  CaptchaCode = proplists:get_value(<<"captchaCode">>, PostVals),

  case simple_captcha:check(CryptKey, CaptchaCode) of
    true ->
      {ok, Req3} = cowboy_req:reply(200, [{<<"content-type">>, <<"text/html">>}],<<"ok">>, Req2),
      {ok, Req3, State};
    _ ->
      {ok, Req3} = cowboy_req:reply(200, [{<<"content-type">>, <<"text/html">>}],<<"error">>, Req2),
      {ok, Req3, State}
  end.

terminate(_Reason, _Req, _State) ->
  ok.

rebar.config

% -*- erlang -*-
{erl_opts, [debug_info]}.
{deps, [
  {cowboy,".*", {git, "https://github.com/ninenines/cowboy", {tag,"1.0.1"}}}
]}.
{cover_enabled, true}.

{eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}.
{sub_dirs, ["apps/testCaptcha", "rel"]}.

run.sh

#!/bin/sh
rebar clean;
rebar compile;
erl -pa apps/*/ebin deps/*/ebin -eval "application:start(testCaptcha)."

captcha_test.html

<img src="http://127.0.0.1:8080/captcha" width="100" height="100">

<form action="http://127.0.0.1:8080/captcha_check" method="post">
  <p>captcha<input type="text" name="captchaCode" /></p>
  <input type="submit" value="Submit" />
</form>

 收工

原文地址:https://www.cnblogs.com/ziyouchutuwenwu/p/4424499.html