golang + vue3+vite 构建后台管理系统笔记。

开始选用的gin框架,但是后来突然发现gin框架的路由不是最长匹配规则。不能实现路由降级。比如不能同时注册 / 和 /upload 路由。如下代码会报错。

// catch-all wildcard '*filepath' in new path '/*filepath' conflicts
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "pong",
            "date":    time.Now(),
        })
    })

    r.StaticFS("/", http.Dir("wwwroot"))

然后看到裸go的路由是支持降级的。所以考虑使用裸go
..............................

最后还是决定不用系统路由了。。。自己用URL QUERSTRING来路由。

package main

import (
    "GoServer/tools/caoxx"
    "GoServer/zmpermn"
    "GoServer/zmpermn/api100"
    "net/http"
)

func main() {
    // init database
    zmpermn.Dbx()
    mux := http.NewServeMux()
    api100.Setup()
    mux.HandleFunc("/api/v100/pub", api100.PubApiRoutes) // public api for anonymous operator
    mux.HandleFunc("/api/v100/pri", api100.PriApiRoutes) // private api for logined operator, header token control used.
    mux.Handle("/", http.FileServer(http.Dir("wwwroot")))
    caoxx.Logx("Bring Server online!")
    err := http.ListenAndServe(":8080", mux)
    if err != nil {
        caoxx.Logx("Server Fail:", err)
    } else {
        caoxx.Logx("Server Online!")
    }

}
package api100

import (
    "GoServer/tools/caoxx"
    "GoServer/zmpermn"
    "GoServer/zmpermn/api100/article"
    "GoServer/zmpermn/api100/upload"
    "GoServer/zmpermn/api100/user"
    "archive/zip"
    "encoding/json"
    "io"
    "math/rand"
    "net/http"
    "os"
    "path"
    "path/filepath"
    "strconv"
    "sync"
    "time"
)

type handlerType func(par map[string]interface{}, w http.ResponseWriter, r *http.Request)

var pubRoutes sync.Map
var priRoutes sync.Map

func PubApiRoutes(w http.ResponseWriter, r *http.Request) {
    setupCORS(&w)
    w.Header().Set("Content-Type", "application/json")
    if !validPostMethod(w, r) {
        return
    }
    api := r.FormValue("api")
    obj, ok := pubRoutes.Load(api)
    if ok {
        if f, ok := obj.(func(par map[string]interface{}, w http.ResponseWriter, r *http.Request)); ok {
            var req map[string]interface{}
            j := json.NewDecoder(r.Body)
            j.Decode(&req)
            handlerType(f)(req, w, r)
            return
        }
    }
    params := map[string]interface{}{}
    params["status"] = zmpermn.ErrGwFail
    tm := time.Now().Format(time.RFC1123)
    params["message"] = api + " Api Not implemented, The time is: " + tm
    json.NewEncoder(w).Encode(params)
}

func PriApiRoutes(w http.ResponseWriter, r *http.Request) {
    setupCORS(&w)
    w.Header().Set("Content-Type", "application/json")
    //fmt.Println(r)
    if !validPostMethod(w, r) {
        return
    }
    if !checkPermission(w, r) {
        return
    }
    api := r.FormValue("api")
    obj, ok := priRoutes.Load(api)
    if ok {
        if f, ok := obj.(func(par map[string]interface{}, w http.ResponseWriter, r *http.Request)); ok {
            var req map[string]interface{}
            j := json.NewDecoder(r.Body)
            j.Decode(&req)
            handlerType(f)(req, w, r)
            return
        }
    }
    params := map[string]interface{}{}
    params["status"] = zmpermn.ErrGwFail
    tm := time.Now().Format(time.RFC1123)
    params["message"] = api + " Api Not implemented, The time is: " + tm
    json.NewEncoder(w).Encode(params)
}

func validPostMethod(w http.ResponseWriter, r *http.Request) bool {
    //fmt.Println("Menthod:", r.Method)
    if r.Method == "POST" {
        return true
    }
    params := map[string]interface{}{}
    params["status"] = zmpermn.ErrGwFail
    tm := time.Now().Format(time.RFC1123)
    params["message"] = "Only 'POST' Method allowed, The time is: " + tm
    json.NewEncoder(w).Encode(params)
    return false
}

// CheckPermission checks the user/method/path combination from the request.
// Returns true (permission granted) or false (permission forbidden)
func checkPermission(w http.ResponseWriter, r *http.Request) bool {
    params := map[string]interface{}{}
    params["status"] = zmpermn.ErrGwForbidden
    tm := time.Now().Format(time.RFC1123)
    params["message"] = "Permission forbidde,Token Timeout, The time is: " + tm

    username := r.Header.Get("username")
    token := r.Header.Get("token")
    app := r.Header.Get("app")
    if app == "" || username == "" || token == "" {
        params["message"] = "poor params, The time is: " + tm

    } else {
        platform, err := strconv.Atoi(r.Header.Get("platform")) //r.FormValue("platform"))
        if err != nil {
            params["message"] = "poor platform, The time is: " + tm
        } else {
            if platform < 0 || platform > 3 {
                params["message"] = "poor platform, The time is: " + tm
            } else {
                kkey := app + username
                uit, ok := user.GetToken(kkey)
                if ok { // 已经登陆过了。
                    if uit.Ti[platform].Token == token { // 检查在线持有的token是否正确
                        // 这里可以加入用户是否允许访问某个API的URL的验证。这里只是粗略的验证登录过了就    可以访问所有url
                        //caoxx.Logx("checkPermission ok...")
                        return true
                    } else {
                        caoxx.Logx("token request:", token, " token online:(", platform, ") ", uit.Ti[platform])
                        params["message"] = "token not valid , The time is: " + tm
                    }
                } else {
                    params["message"] = "user not login, The time is: " + tm
                }
            }
        }
    }
    caoxx.Logx("checkPermission fail...", params["message"])
    w.WriteHeader(http.StatusForbidden)
    json.NewEncoder(w).Encode(params)
    return false
}

// 跨域
func setupCORS(w *http.ResponseWriter) {
    (*w).Header().Set("Access-Control-Allow-Origin", "*")
    (*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH, OPTIONS")
    (*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, access-control-allow-origin, access-control-allow-headers,Origin,x-requested-with,username,token,app,platform")
}

// 安装路由
func Setup() {
    user.PubSetup(&pubRoutes)
    user.PriSetup(&priRoutes)
    article.PubSetup(&pubRoutes)
    article.PriSetup(&priRoutes)
    upload.PriSetup(&priRoutes)
}

func GetCurrentPath() string {
    if ex, err := os.Executable(); err == nil {
        return filepath.Dir(ex)
    }
    return "./"
}

func GetRand() int {
    return rand.Intn(899) + 100
}

// Unzip decompresses a zip file to specified directory.
// Note that the destination directory don't need to specify the trailing path separator.
func Unzip(zipPath, dstDir, dir0 string) ([]string, error) {
    // open zip file
    reader, err := zip.OpenReader(zipPath)
    if err != nil {
        return nil, err
    }
    var ret []string
    defer reader.Close()
    for _, file := range reader.File {
        if err := unzipFile(file, dstDir); err != nil {
            return nil, err
        }
        //fmt.Println(file.FileInfo().Name(), dstDir)
        ret = append(ret, dir0+"/"+file.FileInfo().Name())
    }
    return ret, nil
}

func unzipFile(file *zip.File, dstDir string) error {
    // create the directory of file
    filePath := path.Join(dstDir, file.Name)
    if file.FileInfo().IsDir() {
        if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
            return err
        }
        return nil
    }
    if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
        return err
    }

    // open the file
    rc, err := file.Open()
    if err != nil {
        return err
    }
    defer rc.Close()

    // create the file
    w, err := os.Create(filePath)
    if err != nil {
        return err
    }
    defer w.Close()

    // save the decompressed file content
    _, err = io.Copy(w, rc)
    return err
}

package user

import (
    "GoServer/tools/caoxx"
    "GoServer/zmpermn"
    "encoding/json"
    "net/http"
    "strconv"
    "sync"
    "time"
)

func PubSetup(k *sync.Map) {
    k.Store("user/login", Login)
}

func PriSetup(k *sync.Map) {
    k.Store("user/logout", Logout)
    k.Store("user/refreshtoken", refreshToken)
}

var tokenMap sync.Map

// RequirePermission returns the 403 Forbidden to the client
func Login(par map[string]interface{}, w http.ResponseWriter, r *http.Request) {
    params := map[string]interface{}{}
    params["status"] = zmpermn.ErrGwFail
    username := caoxx.ToString(par["username"])
    password := caoxx.ToString(par["password"])
    mobile := caoxx.ToString(par["mo bile"])
    code := caoxx.ToString(par["code"])
    platform := caoxx.ToInt(par["platform"])
    if platform < 0 || platform > 3 {
        params["message"] = "invalid platform"
    } else {
        app := caoxx.ToString(par["app"])
        if app == "" {
            params["message"] = "no app"
        } else {
            uit, ok := zmpermn.Dbx().Login(username, password, mobile, code, platform, app) // 这个函数里面设置了token
            if ok {
                kkey := app + username
                tokenMap.Store(kkey, uit)
                ui := struct {
                    Id       int    `json:"_id"`
                    Username string `json:"username"`
                    Nickname string `json:"nickname"`
                    Token    string `json:"token"`
                    Avatar   string `json:"avatar"`
                    Mobile   string `json:"mobile"`
                    Platform int    `json:"platform"`
                    App      string `json:"app"`
                }{
                    Id:       int(uit.User.ID),
                    Username: username,
                    Nickname: uit.User.Nickname,
                    Token:    uit.Ti[platform].Token,
                    Avatar:   uit.User.Avatar,
                    Mobile:   mobile,
                    Platform: platform,
                    App:      app,
                }
                if ui.Nickname == "" {
                    ui.Nickname = ui.Username
                }
                params["status"] = zmpermn.ErrGwSucess
                params["message"] = "login ok"
                params["token"] = uit.Ti[platform].Token
                //jsonByte, _ := json.Marshal(ui)
                //params["ui"] = string(jsonByte)
                params["ui"] = ui
            } else {
                params["message"] = "login fail"
            }
        }
    }
    json.NewEncoder(w).Encode(params)
}

// RequirePermission returns the 403 Forbidden to the client
func Logout(par map[string]interface{}, w http.ResponseWriter, r *http.Request) {
    params := map[string]interface{}{}
    params["status"] = zmpermn.ErrGwFail
    params["message"] = "No more actions"
    json.NewEncoder(w).Encode(params)
}

// RequirePermission returns the 403 Forbidden to the client
func refreshToken(par map[string]interface{}, w http.ResponseWriter, r *http.Request) {
    params := map[string]interface{}{}
    params["status"] = zmpermn.ErrGwFail
    tm := time.Now().Format(time.RFC1123)
    params["message"] = "Permission forbidde,Token Timeout, The time is: " + tm

    username := r.Header.Get("username")
    token := r.Header.Get("token")
    app := r.Header.Get("app")
    if app == "" || username == "" || token == "" {
        params["message"] = "poor params, The time is: " + tm
    } else {
        platform, err := strconv.Atoi(r.Header.Get("platform")) //r.FormValue("platform"))
        if err != nil {
            params["message"] = "poor platform, The time is: " + tm
        } else {
            if platform < 0 || platform > 3 {
                params["message"] = "poor platform, The time is: " + tm
            } else {
                kkey := app + username
                uit, ok := GetToken(kkey)
                if ok { // 已经登陆过了。
                    if uit.Ti[platform].Token == token { // 检查在线持有的token是否正确
                        ts := time.Since(uit.Ti[platform].Lasttime)
                        if ts.Minutes() > 60*24*3 {
                            tokenMap.Delete(kkey)
                            params["status"] = zmpermn.ErrGwForbidden
                            w.WriteHeader(http.StatusForbidden)
                        } else {
                            // 超过时限,重新刷新一个token,防止劫持。
                            uit.Ti[platform].Lasttime = time.Now()
                            uit.Ti[platform].Token = zmpermn.NewUUID()
                            tokenMap.Store(kkey, uit)
                            params["status"] = zmpermn.ErrGwSucess
                            params["token"] = uit.Ti[platform].Token
                            params["message"] = "token refresh ok...., The time is: " + tm
                            caoxx.Logx("checkPermission ok...")
                        }
                    } else {
                        caoxx.Logx("token request:", token, " token online:(", platform, ") ", uit.Ti[platform])
                        params["message"] = "token not valid , The time is: " + tm
                    }
                } else {
                    params["message"] = "user not login, The time is: " + tm
                }
            }
        }
    }
    caoxx.Logx("checkPermission fail...", params["message"])
    json.NewEncoder(w).Encode(params)
}

func GetToken(key string) (zmpermn.UserinfoT, bool) {
    ui, ok := tokenMap.Load(key)
    if ok {
        return ui.(zmpermn.UserinfoT), ok
    }
    return zmpermn.UserinfoT{}, false
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容