From ac6ea78aa41b461bc8f075e06a4e0f5538bc5852 Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Tue, 10 Sep 2024 15:59:19 +0800 Subject: [PATCH] fix api_handler --- .../njau_bot/src/http_handler/api_handler.erl | 64 +++++++++++-- .../src/http_handler/http_protocol.erl | 96 ------------------- apps/njau_bot/src/njau_bot_app.erl | 2 +- apps/njau_bot/src/njau_bot_signer.erl | 28 ++++++ config/sys.config | 4 +- docs/Sign.md | 10 ++ 6 files changed, 100 insertions(+), 104 deletions(-) delete mode 100644 apps/njau_bot/src/http_handler/http_protocol.erl create mode 100644 apps/njau_bot/src/njau_bot_signer.erl create mode 100644 docs/Sign.md diff --git a/apps/njau_bot/src/http_handler/api_handler.erl b/apps/njau_bot/src/http_handler/api_handler.erl index d443299..ca8f95a 100644 --- a/apps/njau_bot/src/http_handler/api_handler.erl +++ b/apps/njau_bot/src/http_handler/api_handler.erl @@ -10,22 +10,74 @@ -author("licheng5"). %% API --export([handle_request/4]). +-export([init/2]). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% helper methods -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +init(Req0, Opts) -> + Method = binary_to_list(cowboy_req:method(Req0)), + Path = binary_to_list(cowboy_req:path(Req0)), + GetParams0 = cowboy_req:parse_qs(Req0), + GetParams = maps:from_list(GetParams0), + + {ok, ReqBody, Req1} = parse_body(Req0), + Sign = cowboy_req:header(<<"sign">>, Req1, <<>>), + case njau_bot_signer:sign(ReqBody) =:= Sign of + true -> + {ok, StatusCode, Resp} = handle_request(Method, Path, GetParams, ReqBody), + lager:debug("[http_protocol] request path: ~p, get_params: ~p, post_params: ~p, response: ~ts", + [Path, GetParams, ReqBody, Resp]), + Req2 = cowboy_req:reply(StatusCode, #{ + <<"Content-Type">> => <<"application/json">> + }, Resp, Req1), + {ok, Req2, Opts}; + false -> + Req2 = cowboy_req:reply(500, #{ + <<"Content-Type">> => <<"text/html;charset=utf-8">> + }, <<"Internal Server Error">>, Req1), + {ok, Req2, Opts} + end. handle_request("POST", "/api/device_info", _, Params) -> njau_bot_logger:write(jiffy:encode(Params, [force_utf8])), - {ok, 200, http_protocol:json_data(<<"hello world">>)}; + {ok, 200, json_data(<<"hello world">>)}; handle_request(_, Path, _, _) -> Path1 = list_to_binary(Path), - {ok, 200, http_protocol:json_error(-1, <<"url: ", Path1/binary, " not found">>)}. + {ok, 200, json_error(-1, <<"url: ", Path1/binary, " not found">>)}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% helper methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +parse_body(Req0) -> + ContentType = cowboy_req:header(<<"content-type">>, Req0), + case ContentType of + <<"application/json", _/binary>> -> + {ok, Body, Req1} = read_body(Req0), + {ok, Body, Req1}; + _ -> + {ok, #{}, Req0} + end. + +%% 读取请求体 +read_body(Req) -> + read_body(Req, <<>>). +read_body(Req, AccData) -> + case cowboy_req:read_body(Req) of + {ok, Data, Req1} -> + {ok, <>, Req1}; + {more, Data, Req1} -> + read_body(Req1, <>) + end. + +json_data(Data) -> + Json = jiffy:encode(#{<<"result">> => Data}, [force_utf8]), + iolist_to_binary(Json). + +json_error(ErrCode, ErrMessage) when is_integer(ErrCode), is_binary(ErrMessage) -> + jiffy:encode(#{ + <<"error">> => #{ + <<"code">> => ErrCode, + <<"message">> => ErrMessage + } + }, [force_utf8]). \ No newline at end of file diff --git a/apps/njau_bot/src/http_handler/http_protocol.erl b/apps/njau_bot/src/http_handler/http_protocol.erl deleted file mode 100644 index d442016..0000000 --- a/apps/njau_bot/src/http_handler/http_protocol.erl +++ /dev/null @@ -1,96 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @author licheng5 -%%% @copyright (C) 2020, -%%% @doc -%%% -%%% @end -%%% Created : 26. 4月 2020 3:36 下午 -%%%------------------------------------------------------------------- --module(http_protocol). --author("licheng5"). - -%% API --export([init/2]). --export([json_data/1, json_error/2]). - -init(Req0, Opts = [Mod|_]) -> - Method = binary_to_list(cowboy_req:method(Req0)), - Path = binary_to_list(cowboy_req:path(Req0)), - GetParams0 = cowboy_req:parse_qs(Req0), - GetParams = maps:from_list(GetParams0), - {ok, PostParams, Req1} = parse_body(Req0), - - try Mod:handle_request(Method, Path, GetParams, PostParams) of - {ok, StatusCode, Resp} -> - lager:debug("[http_protocol] request path: ~p, get_params: ~p, post_params: ~p, response: ~ts", - [Path, GetParams, PostParams, Resp]), - AcceptEncoding = cowboy_req:header(<<"accept-encoding">>, Req1, <<>>), - Req2 = case iolist_size(Resp) >= 1024 andalso supported_gzip(AcceptEncoding) of - true -> - Resp1 = zlib:gzip(Resp), - cowboy_req:reply(StatusCode, #{ - <<"Content-Type">> => <<"application/json;charset=utf-8">>, - <<"Content-Encoding">> => <<"gzip">> - }, Resp1, Req1); - false -> - cowboy_req:reply(StatusCode, #{ - <<"Content-Type">> => <<"application/json;charset=utf-8">> - }, Resp, Req1) - end, - {ok, Req2, Opts} - catch - throw:Error -> - ErrResp = json_error(-1, Error), - Req2 = cowboy_req:reply(404, #{ - <<"Content-Type">> => <<"application/json;charset=utf-8">> - }, ErrResp, Req1), - {ok, Req2, Opts}; - _:Error:Stack -> - lager:warning("[http_handler] get error: ~p, stack: ~p", [Error, Stack]), - Req2 = cowboy_req:reply(500, #{ - <<"Content-Type">> => <<"text/html;charset=utf-8">> - }, <<"Internal Server Error">>, Req1), - {ok, Req2, Opts} - end. - -%% 判断是否支持gzip -supported_gzip(AcceptEncoding) when is_binary(AcceptEncoding) -> - binary:match(AcceptEncoding, <<"gzip">>) =/= nomatch. - -parse_body(Req0) -> - ContentType = cowboy_req:header(<<"content-type">>, Req0), - case ContentType of - <<"application/json", _/binary>> -> - {ok, Body, Req1} = read_body(Req0), - {ok, catch jiffy:decode(Body, [return_maps]), Req1}; - <<"application/x-www-form-urlencoded">> -> - {ok, PostParams0, Req1} = cowboy_req:read_urlencoded_body(Req0), - PostParams = maps:from_list(PostParams0), - {ok, PostParams, Req1}; - _ -> - {ok, #{}, Req0} - end. - -%% 读取请求体 -read_body(Req) -> - read_body(Req, <<>>). -read_body(Req, AccData) -> - case cowboy_req:read_body(Req) of - {ok, Data, Req1} -> - {ok, <>, Req1}; - {more, Data, Req1} -> - read_body(Req1, <>) - end. - - -json_data(Data) -> - Json = jiffy:encode(#{<<"result">> => Data}, [force_utf8]), - iolist_to_binary(Json). - -json_error(ErrCode, ErrMessage) when is_integer(ErrCode), is_binary(ErrMessage) -> - jiffy:encode(#{ - <<"error">> => #{ - <<"code">> => ErrCode, - <<"message">> => ErrMessage - } - }, [force_utf8]). \ No newline at end of file diff --git a/apps/njau_bot/src/njau_bot_app.erl b/apps/njau_bot/src/njau_bot_app.erl index 3fcc041..fc73f3b 100644 --- a/apps/njau_bot/src/njau_bot_app.erl +++ b/apps/njau_bot/src/njau_bot_app.erl @@ -35,7 +35,7 @@ start_http_server() -> Dispatcher = cowboy_router:compile([ {'_', [ - {"/api/[...]", http_protocol, [api_handler]} + {"/api/[...]", api_handler, []} ]} ]), diff --git a/apps/njau_bot/src/njau_bot_signer.erl b/apps/njau_bot/src/njau_bot_signer.erl new file mode 100644 index 0000000..1292a35 --- /dev/null +++ b/apps/njau_bot/src/njau_bot_signer.erl @@ -0,0 +1,28 @@ +%%%------------------------------------------------------------------- +%%% @author anlicheng +%%% @copyright (C) 2024, +%%% @doc +%%% +%%% @end +%%% Created : 10. 9月 2024 15:08 +%%%------------------------------------------------------------------- +-module(njau_bot_signer). +-author("anlicheng"). + +%% API +-export([sign/1]). + +%% 数据签名 +-spec sign(S :: binary()) -> binary(). +sign(S) when is_binary(S) -> + {ok, Token} = application:get_env(njau_bot, token), + md5(iolist_to_binary([Token, S, Token])). + +-spec md5(Str :: binary()) -> binary(). +md5(Str) when is_binary(Str) -> + list_to_binary(lists:flatten([hex(X) || <> <= erlang:md5(Str)])). + +hex(N) when N < 10 -> + $0 + N; +hex(N) -> + $a + (N - 10). \ No newline at end of file diff --git a/config/sys.config b/config/sys.config index 78b348d..6e9772e 100644 --- a/config/sys.config +++ b/config/sys.config @@ -6,7 +6,9 @@ {acceptors, 500}, {max_connections, 10240}, {backlog, 10240} - ]} + ]}, + + {token, "aB3$dEfGhiJkLmNoPqRsTuVwXyZ!@#4f5e6d7c8b9a0f1e2d"} ]}, %% 系统日志配置,系统日志为lager, 支持日志按日期自动分割 diff --git a/docs/Sign.md b/docs/Sign.md new file mode 100644 index 0000000..794ebb4 --- /dev/null +++ b/docs/Sign.md @@ -0,0 +1,10 @@ +# 签名算法的说明 + +## 约定token的值为 + aB3$dEfGhiJkLmNoPqRsTuVwXyZ!@#4f5e6d7c8b9a0f1e2d + +## 所有接口调用都必须传递签名值,签名值算法如下: + 参数签名算法,算法逻辑如下: + 1. 将对象转换成JSON字符串str1 + 2. 将str1进行和给定的token值进行拼接后,然后对字符串进行md5加密, 格式如何: md5_str = md5($token + str1 + $token) + 3. md5_str放到post请求的header里面,格式如下: sign: md5_str \ No newline at end of file