XGoServer一個基礎性、模組完整且安全可靠的服務端框架

林冠巨集發表於2018-02-19

作者:林冠巨集 / 指尖下的幽靈

掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8

部落格:http://www.cnblogs.com/linguanh/

GitHub : https://github.com/af913337456/

騰訊雲專欄: https://cloud.tencent.com/developer/user/1148436/activities


一個基礎性、模組完整且安全可靠的服務端框架

開源地址:https://github.com/af913337456/XGoServer

第一版介紹文章:基於 xorm 的服務端框架 XGoServer


新新增模組

  • 自定義路由介面
  • Token模組,jwt
  • 加解密模組,cipher-AES,可自行擴充其他
  • 各模組對應的單元測試例子

共具備的

  • 自定義路由介面
  • Token模組,jwt
  • 加解密模組,cipher-AES,可自行擴充其他
  • 日誌模組,alecthomas/log4go
  • 路由模組,gorilla/mux
  • 硬儲存 / 軟儲存 採用 xorm 框架
  • 服務端通用的輸出資料結構的整合,例如 json
  • 各模組對應的單元測試例子

自定義路由介面

requireToken 標識該路由是否需要 token 驗證

TokenData 是我們要組合到 token 裡面的資料結構 struct

type Context struct {
    TokenData TokenData
    TokenStr  string    `json:"tokenStr"`
    IpAddress string    `json:"ipAddress"`  // client 的 ip
    RoutePath string    `json:"routePath"`
}
type XHandler struct {
    handleFunc   func(*Context, http.ResponseWriter, *http.Request)
    requireToken bool
}
func (x XHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)  {
    tokenStr := r.Header.Get(TokenAuth)
    c := &Context{}
    c.IpAddress = util.GetIpAddress(r)
    c.RoutePath = r.URL.Path
    if x.requireToken {
        tokenData,err := ParseToken(tokenStr) // 使用 token 模組進行解析
        if err != nil {
            util.RenderJson(w,util.GetCommonErr(err.Error()))
            return
        }
        c.TokenData = *tokenData
        c.TokenStr  = tokenStr
        // util.RenderJson(w,c)
        x.handleFunc(c,w,r)
        return
    }
    // 不需要 token
    util.LogInterface(c)
    x.handleFunc(c,w,r)
}

用法

  • router.Handle(“/get1”,core.ApiNormalHandler(getToken)).Methods(“GET”)
  • router.Handle(“/get2”,core.ApiRequestTokenHandler(handleToken)).Methods(“GET”)
  • 搭配使用:
router.Handle("/getToken",core.ApiNormalHandler(getToken)).Methods("GET")
router.Handle("/handleToken",core.ApiRequestTokenHandler(handleToken)).Methods("GET")
router.HandleFunc("/enc",encOutput).Methods("GET")
router.HandleFunc("/dec",decOutput).Methods("POST")

例子集合

  • 直接輸出一條 json 給客戶端
  • 登陸成功生成 token 返回
  • 直接輸出解析後的 Context
  • 加密輸出
  • 與資料庫互動 —– important

直接輸出一條 json 給客戶端

func main()  {
    router := new (mux.Router)
    router.HandleFunc("/",test2).Methods("GET")
    core.HttpListen(router)
}
func test2(w http.ResponseWriter,r *http.Request)  {
    // 非常簡單的例子, 操作放在內部 , 可以使用 request 來獲取自己的引數,再直接組織輸出
    core.HandlerMapWithOutputJson(w, func() map[string]interface{} {
        m :=  map[string]interface{}{}
        m["msg"] = "blow me a kiss"
        return m
    })
}
// 結果 : {"msg":"blow me a kiss"}

登陸成功生成 token 返回

token 是具備過期時間的,皆可自定義

func getToken(c *core.Context, w http.ResponseWriter, r *http.Request)  {
    core.HandlerMapWithOutputJson(w, func() map[string]interface{} {
        tokenStr,err := core.BuildDefaultToken(func(tokenData *core.TokenData) {
            tokenData.UserId = "123456"  // 使用者 id
            tokenData.Roles  = "normal"  // 使用者角色
        })
        if err != nil {
            return util.GetCommonErr(err.Error())
        }
        return util.GetCommonSuccess(tokenStr)
    })
}
結果:
{
    "msg": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2Iiwicm9sZXMiOiJub3JtYWwiLCJwcm9wcyI6bnVsbCwiZXhwIjoxNTE5MDA2Njg4LCJpYXQiOjE1MTkwMDY2Mjh9.NsBsQ3xpOJNZUXO5Dc-Yk4p4m6p4EeDWgEfc5BaNKd4",
    "ret": "success"
}

直接輸出解析後的 Context

tokenStr 是 Context 一變數

handleToken 方法對於的是 ApiRequestTokenHandler

func handleToken(c *core.Context, w http.ResponseWriter, r *http.Request)  {
    util.RenderJson(w,context)
}

輸出加密的資料

tokenStr 是 Context 一變數

handleToken 方法對於的是 ApiRequestTokenHandler

type IEncrypt interface {
    AesEncrypt(origData, key []byte) ([]byte, error)   // 加密
    AesDecrypt(encrypted, key []byte) ([]byte, error)  // 解密
}
// 加密輸出
func encOutput(w http.ResponseWriter, r *http.Request)  {
    core.HandlerMapWithOutputJson(w, func() map[string]interface{} {
        d := "狗年平安"
        aes := encrypt.DefaultAES{}
        return util.GetCommonSuccess(aes.AesEncryptStr(d))
    })
}
輸出:
{
    "msg": "ufffdu0002ufffdu001euufffdu001cufffdu0013mufffdM\ufffdufffdu0011",
    "ret": "success"
}

與資料庫互動

func test3(w http.ResponseWriter,r *http.Request)  {
    core.HandlerMapWithOutputJson(w, func() map[string]interface{} {
        // 插入一條評論
        item := &model.Comment{
            Id  :util.NewId(),         // 評論 id
            UserId  :"123456",             // 評論人 id
            Name    :"LinGuanHong",        // 評論人名稱
            Content :"hello word",         // 評論內容
        }
        affect,_ := core.Engine.Insert(item)  // 執行插入,傳入 struct 引用
        m :=  map[string]interface{}{}
        if affect > 0 {
            m["ret"] = "insert success"
            comments := make([]model.Comment, 0)
            core.Engine.Find(&comments)   // select 出來,獲取所有評論輸出
            m["msg"] = comments
        }else{
            m["ret"] = "insert failed"
        }
        return m
    })
}

輸出的結果是:
{
  "msg": [
    {
      "id": "1kubpgh9pprrucy11e456fyytw",
      "UserId": "123456",
      "name": "LinGuanHong",
      "content": "hello word"
    }
  ],
  "ret": "insert success"
}

上述的功能結合使用,便可做到牢固的安全性。此外,可以再結合 https 級別的路由使用。

如果您認為這篇文章還不錯或者有所收穫,您可以通過掃描一下下面的支付寶二維碼 打賞我一杯咖啡【物質支援】,也可以點選右下角的【推薦】按鈕【精神支援】,因為這兩種支援都是我繼續寫作,分享的最大動力



img_12e3f54d4d0f70f0eb14f20548e3d781.png

相關文章