## Basic Auth
Nginx 的 basic auth , 基于 ngx_http_auth_basic_module 模塊。該模塊是 builtin 模塊,就是你安裝 Nginx 的時候,它就一起裝好了。
Basic Auth ,是一種簡單的鑒權方式,不怎麼安全。一般的 Basic Auth header 長這樣:
Authorization: Basic $(base64_encode(username:password))
客戶端通過對 username 和 password 進行 base64 加密, 放入 header 中。服務端獲取 header 中的 Authorization , 然後 decode 鑒權。
在 nginx 中,通過 auth_basic 和 auth_basic_user_file 進行配置,該語法支持的上下文有
http, server, location, limit_except
具體配置如下:
server {
listen 8088;
location / {
auth_basic "closed site";
auth_basic_user_file conf/htpasswd;
root /xxx/html;
index index.html index.htm;
}
error_page 404 /404.html;
access_log logs/blog.access.log;
}
關于 auth_basic 後面的字符串,要看各大浏覽器是不是給面子了。我用 chrome 就不會顯示這個,用 edge 會顯示。
auth_basic_user_file 是用來存儲賬号密碼的。官方支持使用 "HTTP Basic Authentication" 協議來驗證用戶名和密碼。
emmm...
現在我們通過 htpasswd 生成一個:
# 直接在控制帶輸出
htpasswd -nbd iyuhp admin
# 輸出到文本
htpasswd -bdc passwd iyuhp admin
# 再次追加到文本
htpasswd -nbd iyuhp admin | tee -a passwd
這個時候,優雅的重啟一波 nginx:
sudo nginx -s reload
然後訪問一波, 發現已經會彈出一個用來登錄的彈窗了。
如果通過命令行訪問,可以直接通過 username:password@url訪問:
curl 'localhost:8088' --header 'Authorization: Basic aXl1aHA6YWRtaW4='
// or
curl iyuhp:admin@localhost:8088
可能我有一個後端程序,所以我完全可以把這些 "繁重" 的用戶密碼管理,交給後台就好了。
哦,我并不是說搞個程序來生成賬号密碼,然後寫到 auth_basic_user_file 文件中。我的意思是,這個校驗的工作,讓我來吧,Nginx 你去搞别的去!
這樣做的好處是, 我可以方便的管理我的賬号體系(這裡所謂的賬号體系,是一個誇張的說法,你一個小小的 blog ,還賬号體系...)
emmm... 不管怎樣,這看起來是件有趣的事情。
Nginx 自是能考慮到這些,已然提供了一個語法 auth_request , 意思是,你如果要訪問這裡,可以,得先通過我的驗證才行。
auth_request uri | off; # off 表示關閉上文的驗證,就是我這裡我說了算,上級不要說話了
# 該語法支持的上下文為:
http, server, location
那麼我們要怎麼搞呢? 别急, 我先整張圖:
通過上述步驟, 我們成功通過和 Backend 交互,并獲得了指定資源的訪問權。
關于 auth_request 的更多内容請看 這裡 。 附上一份配置:
location / {
auth_request /auth;
root /xxx/html
}
location /auth {
proxy_pass http://localhost:1234/auth; # 處理 auth 的後端 URL
# 關閉不必要的數據傳遞
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
# proxy cache 設置
proxy_cache auth_cache;
proxy_cache_valid 200 2h;
proxy_cache_key $host$request_uri$cookie_session;
}
使用 proxy_cache 前,需要先定義下 proxy_cache_path , 該語法的上下文是 http ,也就說,你需要在 http 層先定義該值, 比如:
proxy_cache_path path levels=1:2 keys_zone=one:10m;
我現在又覺得, 搞啥賬号體系,我一個小小的 blog , 太耗時間了!
于是你想到了用驗證碼來搞好了。
那自然而然的想到了微信,想到利用公衆号, 來搞個自動獲取驗證碼呗。整個流程如下:
下面再來看下更詳細的流程圖:
這張圖就很詳細了,這裡主要說幾個注意的點:
貼下具體的配置:
location / {
auth_request /auth;
error_page 401 =200 /login;
root /xxx/html;
}
location /auth {
proxy_pass http://localhost:1234/auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_cache auth_cache;
proxy_cache_valid 200 2h;
# proxy key, 參見 reference [3]
proxy_cache_key $host$request_uri$cookie_session;
# 如果有 session 丢失的問題, 可以參考這條配置
# proxy_cookie_path ~*^/.* /;
}
location /login {
proxy_pass http://localhost:1234/login;
proxy_set_header X-Original-URI $request_uri;
}
關于 測試賬号 的東西,我也了解的不多,因為我隻對接了一個 。
登錄微信公衆号後, [開發] - [基本配置] - [服務器配置] ,配置服務器地址、令牌、消息加解密密鑰(optional)
提交時, 微信會先發一個驗證請求,看看你的 server 是不是好的:
GET /handle?signature=xxx&echostr=123455×tamp=123456&nonce=1234
驗證部分的文檔, 在 這裡 。主要步驟是:
# golang 部分代碼
ary := []string(token, timestamp, nonce)
sort.Strings(ary)
encryptor := sha1.New()
io.WriteString(encryptor, strings.Join(ary,""))
mySignature := fmt.Sprintf("%x", encryptor.Sum(nil))
// compare
文檔說的是, 如果一緻,就把 URI 中 echostr 原樣返回。
嗯, 我返回了, 你特麼說我驗證失敗, Token 有問題啥的???
為啥, 因為我返回了一個 string 類型的字符串! F**k , 這導緻微信接收的時候, 在原值上多了一對雙引号。
哎, 我最終返回了一個 int 類型的 echostr ,雖然我不知道這個 echostr 是不是會存在 str ...
這一步完成後, 當你通過公衆号發送消息時, 微信就會把這個消息轉發給你的 server 。它的格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
這就是在 server 端定義幾個結構體,解析下就好了:
type CdataString struct {
Value string `xml:",cdata"`
}
type MsgXml struct {
XMLName xml.Name `xml:"xml"`
ToUserName CdataString `xml:"ToUserName"`
FromUserName CdataString `xml:"FromUserName"`
CreateTime int64 `xml:"CreateTime"`
MsgType CdataString `xml:"MsgType"`
Content CdataString `xml:"Content"`
MsgId int64 `xml:"MsgId,omitempty"`
}
需要注意三個點:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<Encrypt>
<![CDATA[QC1jqzkTmPbaCkcB7ruVHe6k=...]]>
</Encrypt>
</xml>
你需要用你的 EncodingAESKey 先解密才行, 這裡 是微信的文檔, 不再詳述。至此, 你就可以愉快的通過微信公衆号,來玩轉你的 blog 了。是不是很有趣? 可憐我在五一的美好假期裡,熬了一個通宵搞這東西...
請參考 proxy_cache_path
請參考 這裡
開始通過 POST /login 進行重定向時, 總是用 POST 方法調用 Location 的地址,導緻 Method Not Allowed. 此時使用的 http code 是 307,後來用 302 , 最後改為 303 搞定
對于使用哪種變量作為 proxy_cache 的 key ,我想了很久。
最開始用一堆諸如 $host$remote_addr$request_uri , 後來發現一個問題,對于同一個局域網下的用戶, 隻要 $request_uri 一緻, 那這個 cache 就是公用的, 這明顯不科學啊!
後來 google 了一把, 決定用 session 作為 proxy_cache 的 key 。這要求你在鑒權的時候,在 cookie 中寫入你的 session,然後通過 $cookie_session 獲取即可。
當然,你可以往 cookie 裡寫 anything else ,如 wtf ,然後你就可以用 $cookie_wtf 作為你的 proxy key 了, 完美。
所以, 這裡的 cookie_xxx , 值得是 client 的端 cookie 中的一個 key 。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!