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

1. Introduction

In Web application development, permission authentication is a crucial aspect of system security. A well-designed permission authentication system can effectively control user access to resources, prevent unauthorized operations, and maintain code simplicity and maintainability. As application scale expands and business complexity increases, permission management becomes increasingly important and challenging.

This article will detail how to use the GoFrame framework to design and implement an efficient permission authentication system, without relying on complex third-party components, but leveraging GoFrame's powerful built-in features such as middleware mechanisms and metadata functionality. Through this article, you will grasp the core concepts of permission authentication and learn how to apply these techniques in real-world projects.

2. Implementing API Authorization with Middleware

Before designing the specific permission authentication module, let's first introduce how to use unified middleware for authorization. Middleware is a powerful feature in the GoFrame framework that allows us to insert custom logic at different stages of request processing, making it ideal for implementing cross-cutting concerns like permission verification. Using middleware for authorization is a more flexible and maintainable approach, effectively separating authentication logic from business logic.

2.1 Traditional Permission Authorization Approach

In traditional Web application development, permission authorization is typically implemented by dividing interfaces into different route groups, with each group applying different middleware for permission verification. For example:

func main() {
s := g.Server()

// Public interfaces, no authorization required
s.Group("/public", func(group *ghttp.RouterGroup) {
group.Bind(
controller.Login,
controller.Register,
// Other public interfaces...
)
})

// Interfaces requiring user login
s.Group("/api", func(group *ghttp.RouterGroup) {
// Add JWT authentication middleware
group.Middleware(middleware.JWTAuth)
group.Bind(
controller.User,
controller.Order,
// Other interfaces requiring login...
)
})

// Interfaces requiring admin permissions
s.Group("/admin", func(group *ghttp.RouterGroup) {
// Add JWT authentication middleware
group.Middleware(middleware.JWTAuth)
// Add admin permission verification middleware
group.Middleware(middleware.AdminAuth)
group.Bind(
controller.Admin,
controller.System,
// Other admin interfaces...
)
})

s.Run()
}

This approach has the following issues:

  1. Complex route maintenance: As the number of interfaces increases, careful consideration is needed for which route group each interface should belong to.
  2. Inflexible permission levels: When special permissions are needed for certain interfaces, new route groups may need to be created.
  3. High maintenance cost: When interface permissions need adjustment, interfaces may need to be moved between different route groups.
  4. Code duplication: Different route groups may need to define similar middleware logic repeatedly.

2.2 Metadata-based Permission Authorization

GoFrame provides powerful API metadata management capabilities that can significantly improve interface maintenance efficiency and reduce maintenance costs, especially in projects with many interfaces.

The main advantages of metadata management include:

  1. Centralized management: All interface metadata is defined in one place, making it easy to maintain and manage.
  2. Automatic API documentation generation: API documentation can be automatically generated based on metadata.
  3. Flexible permission management: Permission-related information can be defined in metadata, enabling flexible permission control.

2.2.1 API Metadata Management in GoFrame

In GoFrame, the g.Meta tag can be used to define interface metadata. For example:

type LoginReq struct {
g.Meta `path:"/login" method:"post" summary:"User login" tags:"Auth" noAuth:"true"`
Username string `v:"required#Please enter username" dc:"Username"`
Password string `v:"required#Please enter password" dc:"Password"`
}

type GetUserProfileReq struct {
g.Meta `path:"/user/profile" method:"get" summary:"Get user information" tags:"User"`
// No parameters needed, user id is obtained from context
}

type AdminOperationReq struct {
g.Meta `path:"/admin/operation" method:"post" summary:"Admin operation" tags:"Admin" role:"admin"`
OperationType string `v:"required#Please select operation type" dc:"Operation type"`
// Other parameters...
}

In the above examples, we defined two permission-related fields in the metadata:

  • noAuth:"true": Indicates that this interface does not require permission verification.
  • role:"admin": Indicates that this interface requires admin role to access.

2.2.2 Unified Middleware for Permission Authorization

With metadata support, we can implement a unified middleware to handle permission authorization for all interfaces:

func AuthMiddleware(r *ghttp.Request) {
// Get current route handler
handler := r.GetServeHandler()

// Check if noAuth metadata exists and is true, skip verification
noAuth := handler.GetMetaTag("noAuth")
if noAuth == "true" {
r.Middleware.Next()
return
}

// Get current user info (from context, JWT, Session, etc.)
userId := r.GetCtxVar("userId").Int()
if userId == 0 {
r.Response.WriteJson(ghttp.DefaultHandlerResponse{
Code: errcode.Unauthorized.Code(),
Message: errcode.Unauthorized.Message(),
})
return
}

// Check if specific role is required
requiredRole := handler.GetMetaTag("role")
if requiredRole != "" {
// Check if user has the role
hasRole, err := checkUserRole(userId, requiredRole)
if err != nil || !hasRole {
r.Response.WriteJson(ghttp.DefaultHandlerResponse{
Code: errcode.Forbidden.Code(),
Message: errcode.Forbidden.Message(),
})
return
}
}

// Other permission verification logic...

// Permission verification passed, continue request processing
r.Middleware.Next()
}

// Register global middleware in main function
func main() {
s := g.Server()

// Register global middleware to handle all requests
s.Use(AuthMiddleware)

// Register all interfaces without grouping by permissions
s.Group("/api", func(group *ghttp.RouterGroup) {
group.Bind(
controller.Login, // Include login interface without verification
controller.User, // Include regular user interfaces
controller.Admin, // Include admin interfaces
// Other interfaces...
)
})

s.Run()
}

This metadata-based unified middleware approach has the following advantages:

  1. Simplified route management: All interfaces can be registered in the same route group without grouping by permissions.
  2. Flexible permission configuration: Permission control is configured directly in the interface definition, making it easier to maintain and understand.
  3. Reduced maintenance cost: When interface permissions need adjustment, only metadata needs to be modified without moving interfaces.
  4. Code reuse: All permission verification logic is centralized in one middleware, avoiding code duplication.
  5. Strong extensibility: New permission control strategies can be easily extended by adding corresponding processing logic to the middleware.

This metadata-based permission authorization approach is particularly suitable for projects with many interfaces and complex permission control requirements, significantly improving development efficiency and code maintainability.

3. Practical Implementation of Permission Authentication System

In the previous chapters, we discussed the metadata-based unified middleware approach, which is very effective in simple scenarios. However, in real-world enterprise applications, permission management requirements are often more complex and diverse. Configuring role and permission information through hardcoded interface metadata has the following limitations:

  1. Lack of flexibility: When permission rules need dynamic adjustment, code must be modified and the application redeployed.
  2. Limited scalability: As the number of roles and permissions increases, metadata configuration becomes verbose and difficult to maintain.
  3. Inability to meet complex business requirements: Permission control in large systems often requires multi-dimensional judgment based on user attributes, time periods, operation objects, etc.
  4. Lack of permission management interface: Administrators cannot dynamically adjust permission configurations through an interface and must rely on developers to modify code.
  5. Difficulty in auditing and tracking: It is challenging to record and track permission change history, which is particularly important in systems with strict compliance requirements.

Therefore, in complex enterprise applications, we need to design an independent, database-based permission authentication module. This approach enables dynamic permission configuration, fine-grained control, and complete audit tracking, meeting the security requirements of enterprise applications.

Next, we will detail how to design and implement such a comprehensive permission authentication module, including database table design, core functionality implementation, and integration with the GoFrame framework.

Ask me