开始选用的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
}
网友评论