本模块创作基于gin 框架路由匹配和Django 的身份验证和权限验证创作的,目前主要实现了身份验证,基于路由配置自动匹配需要验证的路由,另外可以通过配置实现不同路由匹配不同身份验证方式 .
git地址: https://github.com/xxxxxxming/authtest
后续有时间持续更新该模块
模块主要由三个文件组成,分别是路由处理,身份认证,中间件拦截.
代码如下:
1. 路由处理,包含路由数创建和路由解析
package utils
import (
"bytes"
"strings"
)
const (
static nodeType = iota
root
param
query
)
type nodeType uint8
type node struct {
path string
children []*node
nType nodeType
tokenAuth string
permissionsAuth string
}
type methodTree struct {
method string
root *node
}
type Engine struct {
trees methodTrees
}
type methodTrees []methodTree
func (trees methodTrees) get(method string) *node {
for _, tree := range trees {
if tree.method == method {
return tree.root
}
}
return nil
}
func assert1(guard bool, text string) {
if !guard {
panic(text)
}
}
func (n *node) addRoute(path, tokenAuth, permissionsAuth string) {
pathList := strings.Split(path, "/")
s := []byte(path)
countSlash := uint16(bytes.Count(s, []byte("/")))
if countSlash == 0 {
return
}
if countSlash == 1 && len(pathList) == 0 {
n.nType = root
n.path = "/"
n.tokenAuth = tokenAuth
n.permissionsAuth = permissionsAuth
} else {
n.insertChild(path, tokenAuth, permissionsAuth)
}
}
func getUrlList(path string) []string {
pathList := strings.Split(path, "/")
list := []string{}
for _, p := range pathList {
if p == "" {
continue
}
index := bytes.IndexByte([]byte(p), '?')
if index == -1 {
list = append(list, p)
} else {
list = append(list, p[:index], p[index:])
}
}
return list
}
func (n *node) insertChild(path, tokenAuth, permissionsAuth string) {
list := getUrlList(path)
head := n
llen := len(list)
for index1, l := range list {
findflag := false
for index2, n1 := range head.children {
if n1.path == l {
if llen == index1+1 {
n1.tokenAuth = tokenAuth
n1.permissionsAuth = permissionsAuth
}
head = head.children[index2]
findflag = true
break
}
}
if findflag {
continue
}
var nType nodeType
if l[0] != ':' && l[0] != '?' {
nType = static
} else if l[0] == ':' {
nType = param
} else if l[0] == '?' {
nType = query
l = l[1:]
}
if llen == index1+1 {
head.children = append(head.children, &node{path: l, nType: nType, tokenAuth: tokenAuth, permissionsAuth: permissionsAuth})
} else {
head.children = append(head.children, &node{path: l, nType: nType})
}
hlen := len(head.children)
head = head.children[hlen-1]
}
}
func (engine *Engine) addRoute(method, path, tokenAuth, permissionsAuth string) {
assert1(path[0] == '/', "path must begin with '/'")
assert1(method != "", "HTTP method can not be empty")
root := engine.trees.get(method)
if root == nil {
root = new(node)
root.path = "/"
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
root.addRoute(path, tokenAuth, permissionsAuth)
}
func (engine *Engine) post(relativePath, tokenHandle, persissionHandle string) {
engine.addRoute("post", relativePath, tokenHandle, persissionHandle)
}
func (engine *Engine) get(relativePath, tokenHandle, persissionHandle string) {
engine.addRoute("get", relativePath, tokenHandle, persissionHandle)
}
func (engine *Engine) delete(relativePath, tokenHandle, persissionHandle string) {
engine.addRoute("delete", relativePath, tokenHandle, persissionHandle)
}
func (engine *Engine) patch(relativePath, tokenHandle, persissionHandle string) {
engine.addRoute("patch", relativePath, tokenHandle, persissionHandle)
}
func (engine *Engine) update(relativePath, tokenHandle, persissionHandle string) {
engine.addRoute("update", relativePath, tokenHandle, persissionHandle)
}
func (engine *Engine) ParseUrlTree(url, method, querys string) string {
root := engine.trees.get(method)
if root == nil {
return ""
}
if url == "/" {
if root.path == url {
return root.tokenAuth
}
return ""
}
list := getUrlList(url)
if querys != "" {
list = append(list, querys)
}
llen := len(list)
for index1, p := range list {
compareFlag := false
index3 := 0
for index2, p1 := range root.children {
index3++
if p1.nType == query {
qflag := true
for _, q := range joinUrlQuery(p1.path) {
if !strings.Contains(p, q) {
qflag = false
break
}
}
if qflag {
compareFlag = true
}
} else {
if p1.path == p {
compareFlag = true
}
}
if compareFlag {
if llen == index1+1 {
return p1.tokenAuth
}
root = root.children[index2]
break
}
}
if index3 == len(root.children) && !compareFlag {
break
}
}
return ""
}
func joinUrlQuery(urlQuery string) []string {
byteUrlQuery := []byte(urlQuery)
flag := false
byteQuey := []byte{}
for _, b := range byteUrlQuery {
if b == '=' {
flag = true
} else if b == '&' {
flag = false
}
if flag {
continue
}
byteQuey = append(byteQuey, b)
}
return strings.Split(string(byteQuey), "&")
}
var Root Engine
func InitTree() {
Root.get("/test/:id", "tokenAuth1", "")
Root.get("/test/t1/:id", "tokenAuth1", "")
Root.get("/test/t2", "", "")
Root.get("/test/t/:id1?name&pwd", "tokenAuth1", "")
Root.get("/test/t2/:id1/:id2", "", "")
Root.get("/test", "", "")
Root.post("/test/:id", "", "")
Root.post("/test/t1/:id", "", "")
Root.post("/test/t2", "", "")
Root.post("/test/t/:id1", "", "")
Root.post("/test/t2/:id1/:id2", "", "")
Root.post("/test", "", "")
}
2. 身份认证.基于jwt库创建的模块包含token生成,解析,以及根据字符串匹配身份验证函数的函数
package utils
import (
"errors"
"time"
"github.com/dgrijalva/jwt-go"
)
const (
SECRETKEY = "243223ffslsfsldf"
)
type MyClaims struct {
Uid int `json:"uid"`
Rid int `json:"rid"`
jwt.StandardClaims
}
func CreateToken(uid int, rid int) (string, error) {
maxAge := 60 * 100
var claims = MyClaims{
uid,
rid,
jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Duration(maxAge) * time.Second).Unix(),
Issuer: "yangjia",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(SECRETKEY))
if err != nil {
return "", err
}
return tokenString, nil
}
func ParseToken(tokenString string) (*MyClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
return []byte(SECRETKEY), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
func ParseToken2(tokenString string) (*MyClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
return []byte(SECRETKEY), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
type tokenAuth interface {
TokenAuth(string) (*MyClaims, error)
}
type PermissionAuth interface {
permissionAuth(string) bool
}
type tokenAuthHadle func(string) (*MyClaims, error)
type permissionAuthHadle func(string) bool
func (t tokenAuthHadle) TokenAuth(token string) (*MyClaims, error) {
return t(token)
}
func (t permissionAuthHadle) permissionAuth(permission string) bool {
return t(permission)
}
func tokenAuth1(token string) (*MyClaims, error) {
return ParseToken(token)
}
func tokenAuth2(token string) (*MyClaims, error) {
return ParseToken2(token)
}
func permissionAuth(string) bool {
return true
}
var AuthMap = make(map[string]tokenAuth)
func InitAuth() {
AuthMap["tokenAuth1"] = tokenAuthHadle(tokenAuth1)
AuthMap["tokenAuth2"] = tokenAuthHadle(tokenAuth2)
}
3. 中间件拦截
package middlewares
import (
"main/utils"
"strings"
"github.com/gin-gonic/gin"
)
func Authorization() gin.HandlerFunc {
return func(c *gin.Context) {
method := strings.ToLower(c.Request.Method)
parmas := c.Request.Header
var code uint
var msg string
var errflag = false
str := utils.Root.ParseUrlTree(c.FullPath(), method, c.Request.URL.RawQuery)
if str != "" {
tokenHandle, b := utils.AuthMap[str]
if b {
token := parmas["Authorization"]
if token == nil {
code = utils.ApiCode.Unauthorized
msg = "缺失Authorization请求头"
errflag = true
} else {
claims, err := tokenHandle.TokenAuth(token[0])
if err != nil {
code = utils.ApiCode.Unauthorized
msg = err.Error()
errflag = true
} else if claims == nil {
code = utils.ApiCode.Unauthorized
msg = "token校验失败"
errflag = true
}
}
}
}
if errflag == true {
c.JSON(200, gin.H{
"code": code,
"msg": msg,
"result": nil,
})
c.Abort()
}
c.Next()
}
}
测试结果:
1. 错误的token:
2. 没有token 3. 正确token
|