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
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容