Skip to main content
Version: 2.8.x(Latest)

User information is a sensitive interface that must ensure it can only be accessed after the user has logged in. Similarly, interfaces related to users editing the word library also need authentication. It is not feasible to write the same code for authentication before every interface that requires it. Hence, a middleware/interceptor should be developed to uniformly verify if the Token is valid.

Middleware/interceptors are functions or components that handle HTTP requests and responses. They are typically used to perform certain actions before the request reaches the final handler or before the response is sent to the client.

Auth Middleware


GoFrame provides an elegant way to control requests using middleware, which is also the mainstream method provided by WebServer for controlling the request flow, making it a flexible and powerful plugin mechanism based on middleware design.

internal/logic/middleware/auth.go

package middleware  

import (
"net/http"

"github.com/gogf/gf/v2/net/ghttp"
"github.com/golang-jwt/jwt/v5"
"star/utility"
)

func Auth(r *ghttp.Request) {
var (
jwtKey = utility.JwtKey
tokenString = r.Header.Get("Authorization")
)
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil || !token.Valid {
r.Response.WriteStatus(http.StatusForbidden)
r.Exit()
}

r.Middleware.Next()
}

The code r.Middleware.Next() is the core control of the middleware. Placing it at the end of the function makes it a pre-middleware, meaning it's called before the request. Placing it at the beginning makes it a post-middleware, effective after the request.

r.Header.Get("Authorization") retrieves the Authorization field from the HTTP Header, i.e., the Token passed from the frontend. After parsing the Token with jwt.Parse, it uses token.Valid to verify its validity. If it invalidates, it returns an HTTP StatusForbidden 403 status code, indicating insufficient permissions. Otherwise, it calls r.Middleware.Next() to proceed.

The written middleware is reserved and will be registered uniformly after interface development is complete. Next, it follows the pleasant triple-axe rule.

Add Api


api/account/v1/account.go

package v1  

import (
"github.com/gogf/gf/v2/frame/g"
)

type InfoReq struct {
g.Meta `path:"account/info" method:"get" sm:"Get Information" tags:"User"`
}

type InfoRes struct {
Username string `json:"username" dc:"Username"`
Email string `json:"email" dc:"Email"`
CreatedAt *gtime.Time `json:"created" dc:"Creation Time"`
UpdatedAt *gtime.Time `json:"update" dc:"Update Time"`
}

The InfoRes structure defines four response data fields, where the *gtime.Time data type is a framework time type provided by the gtime component.

Write Logic


internal/logic/users/account.go

package users  

import (
"context"
"errors"
"time"

"github.com/gogf/gf/v2/frame/g"
"github.com/golang-jwt/jwt/v5"
"star/internal/dao"
"star/internal/model/entity"
"star/utility"
)

...

func Info(ctx context.Context) (user *entity.Users, err error) {
user = new(entity.Users)
tokenString := g.RequestFromCtx(ctx).Request.Header.Get("Authorization")

tokenClaims, _ := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
return utility.JwtKey, nil
})

if claims, ok := tokenClaims.Claims.(*UserClaims); ok && tokenClaims.Valid {
err = dao.Users.Ctx(ctx).Where("id", claims.Id).Scan(&user)
}
return
}

In Logic, you cannot directly access the HTTP object; instead, use g.RequestFromCtx(ctx).Request to obtain it from the context. After obtaining the Token, parse out the user ID, and call the Scan method to assign the query result to the entity.Users structure.

The Scan method is a powerful one that automatically recognizes and converts based on the given parameter type, commonly used in data query operations.

Controller Calls Logic


internal/controller/account/account_v1_info.go

package account  

import (
"context"

"star/api/account/v1"
"star/internal/logic/users"
)

func (c *ControllerV1) Info(ctx context.Context, req *v1.InfoReq) (res *v1.InfoRes, err error) {
user, err := users.Info(ctx)
if err != nil {
return nil, err
}
return &v1.InfoRes{
Username: user.Username,
Email: user.Email,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}, nil
return
}

Register New Controller


Use group.Group to add a new route group and group.Middleware to register the Auth middleware. All controllers under this group need authentication before accessing.

internal/cmd/cmd.go

package cmd  

import (
"context"

"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd"
"star/internal/controller/account"
"star/internal/controller/users"
"star/internal/logic/middleware"
)

var (
Main = gcmd.Command{
Name: "main",
Usage: "main",
Brief: "start http server",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ghttp.MiddlewareHandlerResponse)
group.Group("/v1", func(group *ghttp.RouterGroup) {
group.Bind(
users.NewV1(),
)
group.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(middleware.Auth)
group.Bind(
account.NewV1(),
)
})
})
})
s.Run()
return nil
},
}
)

API Testing


Remember to replace Authorization with your own:

$ curl -H "Authorization: eyJhbGci...W6Ed_d3P77Mc" http://127.0.0.1:8000/v1/account/info

{
"code":0,
"message":"",
"data":{
"username":"oldme",
"email":"tyyn1022@gmail.com",
"created":"2024-11-08 17:02:16",
"update":"2024-11-08 17:02:16"
}
}