Golang项目实战--基于gin框架实现JWT身份认证
需求:
1.RESTFul API与API版本控制
2.中间件与jwt实现统一鉴权
3.Controller模型绑定与参数认证
4.protobuf消息传输
项目结构:
对鉴权逻辑这部分,要考虑到,哪些资源需要涉及到鉴权,哪些不需要涉及。比如说,对于用户登录,用户注册,这两块就不需要去鉴权,因为鉴权是在用户当前是否具有访问某一系统资源的权限。
所以,针对这部分,course表示我们要访问的资源,login表示登录逻辑(只有当用户登录成功后,服务端才会给user一个token),然后用户携带着这个token去访问具体资源。user表示用户部分逻辑,这里简单的设计成获取用户信息,创建用户信息;middleware则表示中间件,比如鉴权,我们就不需要为它单独设计一个handlerfunc了,通过中间件的形式,可以更好的减少代码的冗余的程度。
首先对于路由部分,设计如下:
对于course,user,router,login的设计代码如下:
//course.go package routers import ( "LearningGin/JWT/course" "github.com/gin-gonic/gin" ) func InitCourse(group *gin.RouterGroup) { v1 := group.Group("/v1") { //路径传参 v1.GET("/course/:id", course.Get) v1.POST("/course", course.Add) v1.PUT("/course", course.Update) v1.DELETE("/course", course.Delete) } } //login.go package routers import ( "LearningGin/JWT/login" "github.com/gin-gonic/gin" ) func InitLogin(group *gin.RouterGroup) { v1 := group.Group("v1") { v1.GET("/login", login.Login) } } //router.go package routers import ( "LearningGin/JWT/middleware" "github.com/gin-gonic/gin" ) func Init(r *gin.Engine) { api := r.Group("/api") api.Use(middleware.Cors(), middleware.Auth()) //课程相关接口 InitCourse(api) //用户相互接口 InitUser(api) notAuthApi := r.Group("/api") notAuthApi.Use() //登录接口 InitLogin(notAuthApi) } //user.go package routers import ( "LearningGin/JWT/user" "github.com/gin-gonic/gin" ) func InitUser(group *gin.RouterGroup) { v1 := group.Group("/v1") { v1.GET("/user", user.Get) v1.POST("/user", user.Add) } v2 := group.Group("/v2") { v2.GET("/user", user.Get) v2.POST("/user", user.AddV2) } }
api部分:系统资源course的CRUD,user逻辑,以及登录逻辑
//course.go package course import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) type course struct { Name string `json:"name"form:"name"binding:"required,alphaunicode"` Teacher string `json:"teacher"form:"teacher"binding:"required,alphaunicode"` Duration string `json:"duration"form:"duration"binding:"required,number"` } func Add(c *gin.Context) { req := &course{} err := c.ShouldBind(req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": err.Error(), }) return } c.JSON(http.StatusOK, req) fmt.Println(*req) } func Get(c *gin.Context) { id := c.Param("id") c.JSON(http.StatusOK, gin.H{ "id": id, }) } func Update(c *gin.Context) { req := &course{} err := c.BindJSON(req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": err.Error(), }) return } c.JSON(http.StatusOK, req) } func Delete(c *gin.Context) { id := c.Query("id") c.JSON(http.StatusOK, gin.H{ "id": id, }) } //login.go package login import ( "LearningGin/JWT/jwt" jwt2 "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/gin" "net/http" "time" ) func Login(c *gin.Context) { data := jwt.Data{ Name: "Kol", Age: 24, Gender: "male", StandardClaims: jwt2.StandardClaims{ ExpiresAt: time.Now().Unix() + 60*60*24*30, IssuedAt: time.Now().Unix(), Issuer: "Kol", }, } sign, err := jwt.Sign(data) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": err.Error(), }) } c.JSON(http.StatusOK, gin.H{ "access_token": sign, }) } //user.go package user import ( "github.com/gin-gonic/gin" "net/http" ) func Get(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "method": c.Request.Method, "Path": c.Request.URL.Path, }) } func Add(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "method": c.Request.Method, "Path": c.Request.URL.Path, }) }
jwt,以及中间件部分:
//jwt.go package jwt import "github.com/dgrijalva/jwt-go" type Data struct { Name string Age int Gender string jwt.StandardClaims } var key = "abcdefg1234567" func Sign(data jwt.Claims) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, data) sign, err := token.SignedString([]byte(key)) if err != nil { return "", err } return sign, nil } func Verify(sign string, data jwt.Claims) error { _, err := jwt.ParseWithClaims(sign, data, func(token *jwt.Token) (interface{}, error) { return []byte(key), nil }) return err } //auth.go package middleware import ( "LearningGin/JWT/jwt" "github.com/gin-gonic/gin" "net/http" ) func Auth() gin.HandlerFunc { return func(context *gin.Context) { accessToken := context.Request.Header.Get("access_token") data := &jwt.Data{} err := jwt.Verify(accessToken, data) if err != nil { context.JSON(http.StatusForbidden, gin.H{ "message": "身份认证失败", }) context.Abort() } context.Set("authInfo", data) context.Next() } }