golang gin 框架实现 oauth2

作者: adm 分类: go 发布时间: 2022-09-23

1. 获取依赖包

go get github.com/gin-gonic/gin
go get gopkg.in/oauth2.v3
go get github.com/google/uuid

2. 项目结构和源码
项目地址

3. 主要文件说明
main.go

package main

import (
	"github.com/gin-gonic/gin"
	"logistics/demo"
	"logistics/oauth2"
)


func main() {
	g := gin.Default()
	auth := g.Group("/auth")
	{
		auth.GET("/token", oauth2.TokenRequest)
		auth.GET("/credentials",oauth2.Credentials)
	}
	de := g.Group("/demo")
	{
		de.GET("/message",demo.Message)
	}
	de1 := g.Group("/demo1")
	{
		var c *gin.Context
		//权限认证中间件
		de1.Use(oauth2.AuthValidate(c))
		de1.GET("/message",demo.Message1)
	}
	g.Run(":9096")
}

ginOauth.go

package oauth2

import (
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"gopkg.in/oauth2.v3"
	"gopkg.in/oauth2.v3/errors"
	"gopkg.in/oauth2.v3/manage"
	"gopkg.in/oauth2.v3/models"
	"gopkg.in/oauth2.v3/server"
	"gopkg.in/oauth2.v3/store"
	"log"
	"logistics/model"
	"time"
)

var (
	gServer      *server.Server
	gClient      *store.ClientStore
	gManage      *manage.Manager
	baseResponse  *model.Base
	credentialsResponse *model.Credentials
)

/**
统一token返回格式
 */
func SetExtensionFields(ti oauth2.TokenInfo) map[string]interface{}{
	data := map[string]interface{}{
		"code": 1,
		"message":  "success",
	}
	return data
}

func init()  {
	gManage = manage.NewDefaultManager()
	gManage.MustTokenStorage(store.NewMemoryTokenStore())
	gClient = store.NewClientStore()
	gManage.MapClientStorage(gClient)
	gServer = server.NewDefaultServer(gManage)
	gServer.SetAllowGetAccessRequest(true)
	gServer.SetClientInfoHandler(server.ClientFormHandler)
	var cfg = &manage.Config{AccessTokenExp: time.Hour * 200, RefreshTokenExp: time.Hour * 24 * 300, IsGenerateRefresh: true}
	gManage.SetAuthorizeCodeTokenCfg(cfg)
	gManage.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
    gManage.SetClientTokenCfg(cfg)
	gServer.SetExtensionFieldsHandler(SetExtensionFields)
	gServer.SetInternalErrorHandler(func(err error) (re *errors.Response) {
		log.Println("Internal Error:", err.Error())
		return
	})

	gServer.SetResponseErrorHandler(func(re *errors.Response) {
		log.Println("Response Error:", re.Error.Error())
	})
}


func TokenRequest(c *gin.Context){
	gServer.HandleTokenRequest(c.Writer, c.Request)
}

func Credentials(c *gin.Context){
	clientId := uuid.New().String()[:16]
	clientSecret := uuid.New().String()[:16]
	err := gClient.Set(clientId, &models.Client{
		ID:     clientId,
		Secret: clientSecret,
		Domain: "http://localhost:9094",
	})
	if err != nil {
		baseResponse = &model.Base{}
		baseResponse.Code = 1000
		baseResponse.Message = err.Error()
		c.JSON(500, baseResponse)
		c.Abort()
	}
	credentialsResponse = &model.Credentials{}
	credentialsResponse.Code = 1
	credentialsResponse.Message = "success"
	credentialsResponse.ClientId = clientId
	credentialsResponse.ClientSecret = clientSecret
	c.JSON(200, credentialsResponse)
}

/**
权限验证中间件
 */
func AuthValidate(c *gin.Context) gin.HandlerFunc{
	return func(c *gin.Context) {
		_, err := gServer.ValidationBearerToken(c.Request)
		if err != nil {
			baseResponse = &model.Base{}
			baseResponse.Code = 1001
			baseResponse.Message = err.Error()
			c.JSON(401, baseResponse)
			c.Abort()
			return
		}else{
			c.Next()
		}

	}
}

base.go

package model

type Base struct {
	Code int `json:"code"`
	Message string `json:"message"`
}

credentials.go

package model

type Credentials struct {
	Base
	ClientId string `json:"clientId"`
	ClientSecret string `json:"clientSecret"`
}

test.go

package demo

import "github.com/gin-gonic/gin"

func Message(c *gin.Context){
	c.JSON(200, "I not need auth")
}

func Message1(c *gin.Context){
	c.JSON(200, "I need auth")
}

4. 说明
代码中的

	clientId := uuid.New().String()[:16]
	clientSecret := uuid.New().String()[:16]

可以修改成固定值方便测试

5. 请求
首先获取 Credentials http://localhost:9096/auth/credentials 返回结果

{
    "code": 1,
    "message": "success",
    "clientId": "a02156b4-1a46-43",
    "clientSecret": "98871767-3760-46"
}

然后再获取 accessToken http://localhost:9096/auth/token?grant_type=client_credentials&client_id=a02156b4-1a46-431&client_secret=98871767-3760-46&scope=all 返回结果

{
    "access_token": "DK0LDFEHPEA19DCBXN8-2W",
    "code": 1,
    "expires_in": 720000,
    "message": "success",
    "refresh_token": "YCLVAWSKWGIV___ZNJIGDQ",
    "scope": "all",
    "token_type": "Bearer"
}

原来是没有 code 和 message,为了统一增加的。

请求接口 http://localhost:9096/demo/message 这个不需要授权可以访问 返回结果

"I not need auth"

请求接口 http://localhost:9096/demo1/message 不加授权返回

{
    "code": 1001,
    "message": "invalid access token"
}

增加授权返回

"I need auth"

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!