add application scope
This commit is contained in:
parent
f2efea4ed1
commit
40074417c7
@ -22,6 +22,7 @@ import (
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/grpc/documents"
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/grpc/interceptor"
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/http"
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/http/controller/application_v1"
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/http/controller/v1"
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/http/middleware"
|
||||
"leafdev.top/Ecosystem/recommender/internal/router"
|
||||
@ -37,18 +38,20 @@ func CreateApp() (*base.Application, error) {
|
||||
loggerLogger := logger.NewZapLogger()
|
||||
config := conf.ProviderConfig(loggerLogger)
|
||||
jwksJWKS := jwks.NewJWKS(config, loggerLogger)
|
||||
authService := auth.NewAuthService(config, jwksJWKS, loggerLogger)
|
||||
db := orm.NewGORM(config, loggerLogger)
|
||||
query := dao.NewQuery(db)
|
||||
authService := auth.NewAuthService(config, jwksJWKS, loggerLogger, query)
|
||||
applicationService := application.NewService(query)
|
||||
applicationController := v1.NewApplicationController(authService, applicationService)
|
||||
handlers := http.NewHandler(applicationController)
|
||||
application_v1ApplicationController := application_v1.NewApplicationController(authService, applicationService)
|
||||
handlers := http.NewHandler(applicationController, application_v1ApplicationController)
|
||||
api := router.NewApiRoute(handlers)
|
||||
swaggerRouter := router.NewSwaggerRoute()
|
||||
ginLoggerMiddleware := middleware.NewGinLoggerMiddleware(loggerLogger)
|
||||
authMiddleware := middleware.NewAuthMiddleware(authService)
|
||||
jsonResponseMiddleware := middleware.NewJSONResponseMiddleware()
|
||||
httpMiddleware := http.NewMiddleware(ginLoggerMiddleware, authMiddleware, jsonResponseMiddleware)
|
||||
applicationAuthMiddleware := middleware.NewApplicationAuthMiddleware(authService)
|
||||
httpMiddleware := http.NewMiddleware(ginLoggerMiddleware, authMiddleware, jsonResponseMiddleware, applicationAuthMiddleware)
|
||||
httpServer := server.NewHTTPServer(config, api, swaggerRouter, httpMiddleware)
|
||||
documentService := documents.NewDocumentService(query)
|
||||
interceptorAuth := interceptor.NewAuth(authService)
|
||||
|
46
docs/docs.go
46
docs/docs.go
@ -237,6 +237,52 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/applications/v1/info": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "当前的应用程序信息",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"application_api"
|
||||
],
|
||||
"summary": "Current Application Info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/schema.ResponseBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/entity.Application"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.ResponseBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
|
@ -228,6 +228,52 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/applications/v1/info": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "当前的应用程序信息",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"application_api"
|
||||
],
|
||||
"summary": "Current Application Info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/schema.ResponseBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/entity.Application"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.ResponseBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
|
@ -176,6 +176,32 @@ paths:
|
||||
summary: 创建并保存一个应用程序
|
||||
tags:
|
||||
- applications
|
||||
/applications/v1/info:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 当前的应用程序信息
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/schema.ResponseBody'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/entity.Application'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/schema.ResponseBody'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Current Application Info
|
||||
tags:
|
||||
- application_api
|
||||
securityDefinitions:
|
||||
ApiKeyAuth:
|
||||
in: header
|
||||
|
@ -73,7 +73,6 @@ func (hs *HttpServer) BizRouter() *gin.Engine {
|
||||
|
||||
apiV1 := rootGroup.Group("/api/v1")
|
||||
{
|
||||
//apiV1.Use(corsMiddleWare)
|
||||
apiV1.Use(hs.middleware.JSONResponse.ContentTypeJSON)
|
||||
apiV1.Use(hs.middleware.Auth.RequireJWTIDToken)
|
||||
hs.apiRouter.InitApiRouter(apiV1)
|
||||
@ -85,6 +84,13 @@ func (hs *HttpServer) BizRouter() *gin.Engine {
|
||||
hs.apiRouter.InitNoAuthApiRouter(apiV1NoAuth)
|
||||
}
|
||||
|
||||
applicationApiV1 := rootGroup.Group("/applications/v1")
|
||||
{
|
||||
applicationApiV1.Use(hs.middleware.JSONResponse.ContentTypeJSON)
|
||||
applicationApiV1.Use(hs.middleware.ApplicationAuth.RequireApplicationAuth)
|
||||
hs.apiRouter.InitApplicationApi(applicationApiV1)
|
||||
}
|
||||
|
||||
hs.Gin.NoRoute(func(ctx *gin.Context) {
|
||||
response.Ctx(ctx).Status(http.StatusNotFound).Error(consts.ErrPageNotFound).Send()
|
||||
})
|
||||
|
42
internal/handler/http/controller/application_v1/content.go
Normal file
42
internal/handler/http/controller/application_v1/content.go
Normal file
@ -0,0 +1,42 @@
|
||||
package application_v1
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/http/response"
|
||||
"leafdev.top/Ecosystem/recommender/internal/service/application"
|
||||
"leafdev.top/Ecosystem/recommender/internal/service/auth"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ApplicationController struct {
|
||||
authService *auth.Service
|
||||
applicationService *application.Service
|
||||
}
|
||||
|
||||
func NewApplicationController(authService *auth.Service, applicationService *application.Service) *ApplicationController {
|
||||
return &ApplicationController{
|
||||
authService,
|
||||
applicationService,
|
||||
}
|
||||
}
|
||||
|
||||
// Info godoc
|
||||
// @Summary Current Application Info
|
||||
// @Description 当前的应用程序信息
|
||||
// @Tags application_api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Success 200 {object} schema.ResponseBody{data=entity.Application}
|
||||
// @Failure 400 {object} schema.ResponseBody
|
||||
// @Router /applications/v1/info [get]
|
||||
func (ac *ApplicationController) Info(c *gin.Context) {
|
||||
app, err := ac.authService.GetApplication(c)
|
||||
if err != nil {
|
||||
response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send()
|
||||
return
|
||||
}
|
||||
|
||||
response.Ctx(c).Data(app).Send()
|
||||
return
|
||||
}
|
37
internal/handler/http/middleware/application_auth.go
Normal file
37
internal/handler/http/middleware/application_auth.go
Normal file
@ -0,0 +1,37 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/http/response"
|
||||
"leafdev.top/Ecosystem/recommender/internal/service/auth"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ApplicationAuthMiddleware struct {
|
||||
authService *auth.Service
|
||||
}
|
||||
|
||||
func NewApplicationAuthMiddleware(authService *auth.Service) *ApplicationAuthMiddleware {
|
||||
return &ApplicationAuthMiddleware{
|
||||
authService,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ApplicationAuthMiddleware) RequireApplicationAuth(c *gin.Context) {
|
||||
token, err := a.authService.GetBearerToken(c)
|
||||
if err != nil {
|
||||
response.Ctx(c).Status(http.StatusForbidden).Error(err).Send()
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
applicationEntity, err := a.authService.GetAppByToken(c, token)
|
||||
if err != nil {
|
||||
response.Ctx(c).Status(http.StatusForbidden).Error(err).Send()
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
a.authService.SetApplicationScope(c, applicationEntity)
|
||||
c.Next()
|
||||
}
|
@ -29,7 +29,8 @@ func (a AuthMiddleware) RequireJWTIDToken(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(consts.AuthMiddlewareKey, user)
|
||||
c.Set(consts.AuthMiddlewareKeyUser.String(), user)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
@ -41,6 +42,7 @@ func (a AuthMiddleware) RequireJWTAccessToken(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(consts.AuthMiddlewareKey, user)
|
||||
c.Set(consts.AuthMiddlewareKeyUser.String(), user)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package http
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/http/controller/application_v1"
|
||||
v1 "leafdev.top/Ecosystem/recommender/internal/handler/http/controller/v1"
|
||||
"leafdev.top/Ecosystem/recommender/internal/handler/http/middleware"
|
||||
)
|
||||
@ -10,37 +11,45 @@ var ProviderSet = wire.NewSet(
|
||||
middleware.NewAuthMiddleware,
|
||||
middleware.NewGinLoggerMiddleware,
|
||||
middleware.NewJSONResponseMiddleware,
|
||||
middleware.NewApplicationAuthMiddleware,
|
||||
NewMiddleware,
|
||||
v1.NewApplicationController,
|
||||
application_v1.NewApplicationController,
|
||||
NewHandler,
|
||||
)
|
||||
|
||||
type Middleware struct {
|
||||
GinLogger *middleware.GinLoggerMiddleware
|
||||
Auth *middleware.AuthMiddleware
|
||||
JSONResponse *middleware.JSONResponseMiddleware
|
||||
GinLogger *middleware.GinLoggerMiddleware
|
||||
Auth *middleware.AuthMiddleware
|
||||
ApplicationAuth *middleware.ApplicationAuthMiddleware
|
||||
JSONResponse *middleware.JSONResponseMiddleware
|
||||
}
|
||||
|
||||
func NewMiddleware(
|
||||
GinLogger *middleware.GinLoggerMiddleware,
|
||||
Auth *middleware.AuthMiddleware,
|
||||
JSONResponse *middleware.JSONResponseMiddleware,
|
||||
ApplicationAuth *middleware.ApplicationAuthMiddleware,
|
||||
) *Middleware {
|
||||
return &Middleware{
|
||||
Auth: Auth,
|
||||
GinLogger: GinLogger,
|
||||
JSONResponse: JSONResponse,
|
||||
Auth: Auth,
|
||||
GinLogger: GinLogger,
|
||||
JSONResponse: JSONResponse,
|
||||
ApplicationAuth: ApplicationAuth,
|
||||
}
|
||||
}
|
||||
|
||||
type Handlers struct {
|
||||
Application *v1.ApplicationController
|
||||
ApplicationController *v1.ApplicationController
|
||||
ApplicationApi *application_v1.ApplicationController
|
||||
}
|
||||
|
||||
func NewHandler(
|
||||
application *v1.ApplicationController,
|
||||
applicationController *v1.ApplicationController,
|
||||
applicationApiController *application_v1.ApplicationController,
|
||||
) *Handlers {
|
||||
return &Handlers{
|
||||
Application: application,
|
||||
ApplicationController: applicationController,
|
||||
ApplicationApi: applicationApiController,
|
||||
}
|
||||
}
|
||||
|
@ -23,13 +23,17 @@ func NewApiRoute(
|
||||
}
|
||||
|
||||
func (a *Api) InitApiRouter(r *gin.RouterGroup) {
|
||||
r.GET("/applications", a.h.Application.List)
|
||||
r.POST("/applications", a.h.Application.Save)
|
||||
r.GET("/application/:application_id/tokens", a.h.Application.ListToken)
|
||||
r.POST("/application/:application_id/tokens", a.h.Application.SaveToken)
|
||||
r.GET("/applications", a.h.ApplicationController.List)
|
||||
r.POST("/applications", a.h.ApplicationController.Save)
|
||||
r.GET("/application/:application_id/tokens", a.h.ApplicationController.ListToken)
|
||||
r.POST("/application/:application_id/tokens", a.h.ApplicationController.SaveToken)
|
||||
|
||||
}
|
||||
|
||||
func (a *Api) InitNoAuthApiRouter(r *gin.RouterGroup) {
|
||||
|
||||
}
|
||||
|
||||
func (a *Api) InitApplicationApi(r *gin.RouterGroup) {
|
||||
r.GET("/info", a.h.ApplicationApi.Info)
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"leafdev.top/Ecosystem/recommender/internal/base/conf"
|
||||
"leafdev.top/Ecosystem/recommender/internal/base/logger"
|
||||
"leafdev.top/Ecosystem/recommender/internal/dao"
|
||||
"leafdev.top/Ecosystem/recommender/internal/entity"
|
||||
"leafdev.top/Ecosystem/recommender/internal/schema"
|
||||
"leafdev.top/Ecosystem/recommender/internal/service/jwks"
|
||||
"leafdev.top/Ecosystem/recommender/pkg/consts"
|
||||
@ -17,37 +19,49 @@ type Service struct {
|
||||
config *conf.Config
|
||||
jwks *jwks.JWKS
|
||||
logger *logger.Logger
|
||||
q *dao.Query
|
||||
}
|
||||
|
||||
func NewAuthService(config *conf.Config, jwks *jwks.JWKS, logger *logger.Logger) *Service {
|
||||
func NewAuthService(config *conf.Config, jwks *jwks.JWKS, logger *logger.Logger, q *dao.Query) *Service {
|
||||
return &Service{
|
||||
config: config,
|
||||
jwks: jwks,
|
||||
logger: logger,
|
||||
q: q,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Service) GetBearerToken(c *gin.Context) (string, error) {
|
||||
authorization := c.Request.Header.Get(consts.AuthHeader)
|
||||
|
||||
if authorization == "" {
|
||||
return "", consts.ErrBearerTokenNotFound
|
||||
}
|
||||
|
||||
authSplit := strings.Split(authorization, " ")
|
||||
if len(authSplit) != 2 {
|
||||
return "", consts.ErrNotBearerType
|
||||
}
|
||||
|
||||
if authSplit[0] != consts.AuthPrefix {
|
||||
return "", consts.ErrNotBearerType
|
||||
}
|
||||
|
||||
return authSplit[1], nil
|
||||
}
|
||||
|
||||
func (a *Service) GinMiddlewareAuth(tokenType schema.JWTTokenTypes, c *gin.Context) (*schema.User, error) {
|
||||
if a.config.Debug.Enabled {
|
||||
return a.parseUserJWT(tokenType, "")
|
||||
}
|
||||
|
||||
authorization := c.Request.Header.Get(consts.AuthHeader)
|
||||
token, err := a.GetBearerToken(c)
|
||||
|
||||
if authorization == "" {
|
||||
return nil, consts.ErrJWTFormatError
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authSplit := strings.Split(authorization, " ")
|
||||
if len(authSplit) != 2 {
|
||||
return nil, consts.ErrJWTFormatError
|
||||
}
|
||||
|
||||
if authSplit[0] != consts.AuthPrefix {
|
||||
return nil, consts.ErrNotBearerType
|
||||
}
|
||||
|
||||
return a.parseUserJWT(tokenType, authSplit[1])
|
||||
return a.parseUserJWT(tokenType, token)
|
||||
}
|
||||
|
||||
func (a *Service) AuthFromToken(tokenType schema.JWTTokenTypes, token string) (*schema.User, error) {
|
||||
@ -63,7 +77,7 @@ func (a *Service) GetUserFromIdToken(idToken string) (*schema.User, error) {
|
||||
}
|
||||
|
||||
func (a *Service) GinUser(c *gin.Context) *schema.User {
|
||||
user, _ := c.Get(consts.AuthMiddlewareKey)
|
||||
user, _ := c.Get(consts.AuthMiddlewareKeyUser.String())
|
||||
return user.(*schema.User)
|
||||
}
|
||||
|
||||
@ -74,7 +88,7 @@ func (a *Service) GetUserId(ctx context.Context) schema.UserId {
|
||||
}
|
||||
|
||||
func (a *Service) GetUser(ctx context.Context) *schema.User {
|
||||
user := ctx.Value(consts.AuthMiddlewareKey)
|
||||
user := ctx.Value(consts.AuthMiddlewareKeyUser.String())
|
||||
|
||||
user2 := user.(*schema.User)
|
||||
user2.ID = user2.Token.Sub
|
||||
@ -83,8 +97,7 @@ func (a *Service) GetUser(ctx context.Context) *schema.User {
|
||||
}
|
||||
|
||||
func (a *Service) SetUser(ctx context.Context, user *schema.User) context.Context {
|
||||
context.WithValue(ctx, consts.AuthMiddlewareKey, user)
|
||||
return context.WithValue(ctx, consts.AuthMiddlewareKey, user)
|
||||
return context.WithValue(ctx, consts.AuthMiddlewareKeyUser.String(), user)
|
||||
}
|
||||
|
||||
func (a *Service) parseUserJWT(tokenType schema.JWTTokenTypes, jwtToken string) (*schema.User, error) {
|
||||
@ -137,3 +150,35 @@ func (a *Service) parseUserJWT(tokenType schema.JWTTokenTypes, jwtToken string)
|
||||
|
||||
return jwtIdToken, nil
|
||||
}
|
||||
|
||||
func (a *Service) GetAppByToken(ctx context.Context, token string) (*entity.Application, error) {
|
||||
r, err := a.q.ApplicationToken.WithContext(ctx).Where(a.q.ApplicationToken.Token.Eq(token)).
|
||||
Preload(a.q.ApplicationToken.Application).First()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.Application == nil {
|
||||
return nil, consts.ErrApplicationNotFound
|
||||
}
|
||||
|
||||
return r.Application, nil
|
||||
}
|
||||
|
||||
func (a *Service) GetApplication(ctx context.Context) (*entity.Application, error) {
|
||||
app := ctx.Value(consts.AuthMiddlewareKeyApplication.String())
|
||||
|
||||
app2, ok := app.(*entity.Application)
|
||||
|
||||
if !ok {
|
||||
return nil, consts.ErrApplicationNotFound
|
||||
}
|
||||
|
||||
return app2, nil
|
||||
}
|
||||
|
||||
func (a *Service) SetApplicationScope(c *gin.Context, application *entity.Application) context.Context {
|
||||
c.Set(consts.AuthMiddlewareKeyApplication.String(), application)
|
||||
return context.WithValue(c, consts.AuthMiddlewareKeyApplication, application)
|
||||
}
|
||||
|
7
pkg/consts/application.go
Normal file
7
pkg/consts/application.go
Normal file
@ -0,0 +1,7 @@
|
||||
package consts
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrApplicationNotFound = errors.New("application not found")
|
||||
)
|
@ -5,6 +5,8 @@ import (
|
||||
"leafdev.top/Ecosystem/recommender/internal/schema"
|
||||
)
|
||||
|
||||
type AuthMiddlewareKey string
|
||||
|
||||
const (
|
||||
AuthHeader = "Authorization"
|
||||
AuthPrefix = "Bearer"
|
||||
@ -12,17 +14,22 @@ const (
|
||||
//AnonymousUser schema.UserId = 1
|
||||
AnonymousUser schema.UserId = "anonymous"
|
||||
|
||||
AuthMiddlewareKey = "auth.user"
|
||||
AuthAssistantShareMiddlewareKey = "auth.assistant.share"
|
||||
AuthMiddlewareKeyUser AuthMiddlewareKey = "auth.user"
|
||||
AuthMiddlewareKeyApplication AuthMiddlewareKey = "auth.application"
|
||||
)
|
||||
|
||||
func (a AuthMiddlewareKey) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNotValidToken = errors.New("无效的 JWT 令牌")
|
||||
ErrJWTFormatError = errors.New("JWT 格式错误")
|
||||
ErrNotBearerType = errors.New("不是 Bearer 类型")
|
||||
ErrEmptyResponse = errors.New("我们的服务器返回了空请求,可能某些环节出了问题")
|
||||
ErrTokenError = errors.New("token 类型错误")
|
||||
ErrBearerToken = errors.New("无效的 Bearer 令牌")
|
||||
ErrBearerTokenNotFound = errors.New("bearer 令牌未找到")
|
||||
ErrNotValidToken = errors.New("无效的 JWT 令牌")
|
||||
ErrJWTFormatError = errors.New("JWT 格式错误")
|
||||
ErrNotBearerType = errors.New("不是 Bearer 类型")
|
||||
ErrEmptyResponse = errors.New("我们的服务器返回了空请求,可能某些环节出了问题")
|
||||
ErrTokenError = errors.New("token 类型错误")
|
||||
ErrBearerToken = errors.New("无效的 Bearer 令牌")
|
||||
|
||||
ErrNotYourResource = errors.New("你不能修改这个资源,因为它不是你创建的。")
|
||||
ErrPermissionDenied = errors.New("没有权限访问此资源")
|
||||
|
Loading…
Reference in New Issue
Block a user