API:接口安全(權限)驗證、JWT


下午研究了一下JWT,又回顧了了下工作中碰到的一些接口安全性驗證,寫個小結吧:

 

一、后端對后端

1、IP過濾

因為很多都是內網項目,直接簡單粗暴配置下可訪問的IP地址也湊合着夠用(局域網沒高手潛伏着搞破壞就好。。。),具體就是攔截器里

通過Web.Config參數判斷是否要驗證IP或賬號密碼,直接寫死在Web.Config里,各地上線時去配置,基本是配置了內網IP段即可

public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
    bool isVerifyIP = Utiliy.Tools.Text.ToBool(ConfigurationManager.AppSettings["IsVerifyIP"]);
    bool isVerifyUser = Utiliy.Tools.Text.ToBool(ConfigurationManager.AppSettings["IsVerifyUser"]);

    if (isVerifyIP == false && isVerifyUser == false)
    {
        return;
    }

    if (isVerifyIP == true)
    {
        string clientIP = WebApiConfig.GetClientIp(actionContext.Request);
        if (Utiliy.Tools.Text.CheckInChar(ConfigurationManager.AppSettings["AssignIP"], clientIP) == false
            && Utiliy.Tools.Text.VerifyIsLanIp(clientIP) == false)
        {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
    }

    if (isVerifyUser == true)
    {
        if (actionContext.Request.Headers.Authorization != null)
        {
            string userInfo = Encoding.Default.GetString(Convert.FromBase64String(actionContext.Request.Headers.Authorization.Parameter));
            if (string.Equals(userInfo, string.Format("{0}:{1}", "admin", "XXX")))
            {
                IsAuthorized(actionContext);
            }
            else
            {
                HandleUnauthorizedRequest(actionContext);
            }
        }
        else
        {
            HandleUnauthorizedRequest(actionContext);
        }
    }
}

 

2、直接在URL里獲取登錄賬號拼個&UseName=XX,然后根據這個用戶名看是否有權限

呃這個嘛,完全沒安全性(傳個admin咋整。。。),只是判斷內部管理的權限,配合內網IP限制,又是后端對后端的,基本不會暴露出去,湊合吧。

 

3、參數拼上MD5同時傳進來,比如有個public GetXX(string a, int b, string md5Key),那就是拿 a + b.ToString(),通過公司公共MD5加密(里面封裝好了Salt),對方拿到后也同樣把 a + b.ToString()進行同樣公共方法的加密,看雙方md5Key是否一致。

這個安全性倒是有一定保證,但有好幾個衍生問題:

  a.參數順序不能亂,只能按a、b這樣,如果反過來,md5也不一樣了

  b.如果不是這種拼簡單有限的幾個參數,是用實體(實體可能定義了冗余的十幾個字段)接收的話,要封裝公共方法進行反射循環取值,排序不能亂。按理也是可以,但沒試過。

  c.md5也可以反身暴力破解,網上都有免費直接用的,但拼上Salt后應該還好。此方案里不好加時間戳。或者加個分鍾的時間戳,后台還要弄個時間段循環比對?比如取前3分鍾后3分鍾,以分鍾字符串去md5,分別比對?效率太低了,不過有些小接口也湊合用就是了。

  d.最大問題,這個封裝的md5加密公共方法,是公司內部定義的,和其它合作伙伴或公司等對接,雙方無法達成一致的加密,主要是Salt不能共用,自家md5的鹽總不能給別人吧。

雖說有各種問題,但因這個簡單有效,所以很多有一定安全要求的內部簡單接口,還是會采用這個方案。

 

4、直接傳個約定好的字符串,該字符串可以用GUID生成,雙方都存着,固定附在參數后面進來,固定驗證

和前面參數拼MD5類似,但雙方代碼更簡單,也可以和其它公司對接,聊勝於無。實際也基本沒安全保證,一旦被抓包或怎么泄露了,所有接口都能調用了。。。但因為是后端對后端,又多是內網項目,所以有時也會這樣用,別人只知道接口幾個業務參數,比如單號、名稱什么的,這個隨機串,沒有專業點的抓包什么的,是不會知道。也是寄希望於內網沒高手搞破壞吧(有中級IT水平就可能會泄露了。。。)。還是因其簡單粗暴,項目試運行期間也偶爾會這樣約定,后期再改進(實際一忙可能就一直這樣用下去了。。。)

 

5、公鑰私鑰,比較正規,但雙方開發都較麻煩,一般用於和其它公司對接(很多是其它公司約定好這種方案,我們沿用)。

 

二、前端對后端

因前后端分離等,有很多專為前端服務的接口(如手機移動端),回憶下日常工作中都是怎么驗證的

因手機端基本都是互聯網了,基本不存在內網IP判斷了,所以安全性要比后端對后端考慮多些

1、微信入口(企業號、公眾號、訂閱號、小程序等)

先有一層后端,先驗證好微信的授權,那一層再調API相當於后端對后端了,具體就用上面幾種后端對后端方案了

 

2、普通移動端H5頁面,手機號注冊/驗證后,調API取數據

也有用雙方md5后傳加密串驗證的

前端加密有個缺點,就是不管用什么加密方式,因為JS最終都是暴露出去的,雖然經過混淆后會較難破譯些,也不保險。

項目有采用這種把數據用JS的md5插件加密,又用了一些數組什么的組成鹽,各種混淆,看着基本也沒問題,數據又不是什么特別敏感或涉及金融的,所以這種方案也不時有用。

后來同事還改進過,可傳時間戳,后端循環驗證,3分鍾還是5分鍾內有效

 

3、先登錄,不管是賬號密碼,還是掃碼,還是短信驗證碼,總之登錄完會返回一個token,請求時把這個token附上就可以。每次后台會判斷,如果快過期了,會生成個新的token,返回給前端,下次前端就要用新的token訪問了。只要若干天內有登錄過就會重新刷新,不影響正常操作的流暢性。

其實oAuth2、JWT這些基於Token(AccessToken、RefreshToken)的,基本也是這套路了,只不過封裝得更好。

 

4、JWT

本想着重寫下JWT的心得的,但其實是差不多的:

JWT 標准的 Token 有三個部分:
   header(頭部)
   payload(數據)
   signature(簽名)
   中間用點分隔開,並且都會使用 Base64 編碼,所以真正的 Token 看起來像這樣:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

就是把單純的驗證,能多傳一些其它參數,而且更正規

header只是聲明是什么樣的加密方式

payload是客戶端自已想傳的一些額外參數,通常是賬號相關信息

signature就是把上面兩個的base64進行加密(用的算法就是header中聲明的那個)

最后生成的token就是把這三個依次拼起來,分隔符是 . (英文的點)

客戶端使用用戶名密碼進行認證
服務端生成有效時間較短的 Access Token(例如 10 分鍾),和有效時間較長的 Refresh Token(例如 7 天)
客戶端訪問需要認證的接口時,攜帶 Access Token
如果 Access Token 沒有過期,服務端鑒權后返回給客戶端需要的數據
如果攜帶 Access Token 訪問需要認證的接口時鑒權失敗(例如返回 401 錯誤),則客戶端使用 Refresh Token 向刷新接口申請新的 Access Token
如果 Refresh Token 沒有過期,服務端向客戶端下發新的 Access Token
客戶端使用新的 Access Token 訪問需要認證的接口

作者:simpleapples
鏈接:https://www.jianshu.com/p/25ab2f456904
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。

前后端要注意封裝和判斷下

后端:1、驗證AccessToken,如過期,則用RefreshToken去獲取新的AccessToken

  2、每次都判斷下RefreshToken,如果快過期了,則刷新下RefreshToken,返回新的RefreshToken、AccessToken。每次去數據庫取有效期不值得,可用Redis緩存着  

  3、過期不過期干脆由前端判斷。一種是前端自已判斷快過期了,先調下刷新token的接口。另種是前端把過期時間通過payload傳進來,因為每次返回實體數據、token時,也可以順便返回token過期時間。

  不用擔心偽造過期時間,畢竟前端提醒過期只是為了方便,即使偽造未過期,不刷新token,到時也是用不了的,自已騙自已

 

前端:1、調接口的公共方法里封裝好,如果401授權失敗等,要重新去調token接口獲取新的token,並重新請求數據,這個要做到用戶無感知(最多因為多調了兩次接口顯得慢了一些,但整體流程未中斷)

2、返回的Token,要緩存在cookie或localstorage等,每個請求都要用的,如果要在payload里多傳參數,也可放緩存里一並打包

 

JWT有個小問題,就是后端要禁用客戶時,會刪除RefreshToken,以后就無法更新AccessToken了,但在過期間AccessToken仍然可用。可以搭配着其它驗證(比如Redis緩存個黑名單,禁用時把賬號加進去,每次都驗證下黑名單,因為黑名單不多,所以效率損失可以忽略不計)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM