美文网首页
golang + vue3+vite 构建后台管理系统笔记。

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

作者: 毛毛v5 | 来源:发表于2023-02-22 09:25 被阅读0次

    开始选用的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
    }
    
    

    相关文章

      网友评论

          本文标题:golang + vue3+vite 构建后台管理系统笔记。

          本文链接:https://www.haomeiwen.com/subject/ukqakdtx.html