SoFunction
Updated on 2025-05-08

Go Web Backend Management System Project Implementation

1. Background introduction

This is a web background management system developed based on the Go language. It is a relatively rough work for me to practice during my study.

2. Technical architecture

rear end

  • language: Written in Go language (Golang), because of its concise and efficient, strong concurrency capabilities, and excellent performance, it is particularly suitable for building high-concurrency web services.
  • HTTP routing: Use the Gorilla Mux library, which supports flexible routing definitions and middleware integration, making it easier to build complex APIs and web page routing.
  • database: Select MySQL database, through/go-sql-driver/mysqlDriver to realize interaction with Go applications and is responsible for storing user data, session information and other key data.

front end

The author is not familiar with front-end knowledge and uses AI to generate relevant code

3. Code structure and functional modules

The code structure of the project is clear and reasonable, and it is divided into multiple packages according to the functional module. The following is an introduction to the main packages and their functions:

config package

package config

import (
	"database/sql"
	"os"

	"/gorilla/sessions"
)

var (
	// SessionStore session storage	SessionStore = ([]byte("This is a fixed key, please replace it with a safer value in production"))

	// DB database connection	DB *
)

// GetEnvOrDefault Gets the environment variable, and if it does not exist, use the default valuefunc GetEnvOrDefault(key, defaultValue string) string {
	if value := (key); value != "" {
		return value
	}
	return defaultValue
}

The config package is mainly responsible for storing some global configurations and resources, such as session storage and database connections. It defines a global session storeSessionStore, used to manage user sessions.GetEnvOrDefaultThe function is used to obtain the value of the environment variable. If the variable does not exist, it returns the default value, which is convenient for configuring and applying in different environments.

db package

package db

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	"GoWeb1/config"
	"GoWeb1/models"

	_ "/go-sql-driver/mysql"
)

// InitDB initializes the databasefunc InitDB() error {
	// Connect to the database	dsn := ("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		("DB_USER", "root"),
		("DB_PASSWORD", "123456"),
		("DB_HOST", "localhost"),
		("DB_PORT", "3306"),
		("DB_NAME", "goweb"),
	)

	var err error
	, err = ("mysql", dsn)
	if err != nil {
		return ("Connecting to database failed: %v", err)
	}

	// Test connection	if err = (); err != nil {
		return ("Database connection test failed: %v", err)
	}

	// Create user table	_, err = (`
		CREATE TABLE IF NOT EXISTS users (
			id INT AUTO_INCREMENT PRIMARY KEY,
			username VARCHAR(50) NOT NULL UNIQUE,
			password_hash VARCHAR(255) NOT NULL,
			role VARCHAR(20) NOT NULL DEFAULT 'user',
			login_attempts INT NOT NULL DEFAULT 0,
			last_attempt DATETIME,
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
			avatar VARCHAR(255) DEFAULT '',
			status INT NOT NULL DEFAULT 1
		) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
	`)
	if err != nil {
		return ("Creating user table failed: %v", err)
	}

	// Check if the default administrator user exists	var count int
	err = ("SELECT COUNT(*) FROM users WHERE username = 'admin'").Scan(&count)
	if err != nil {
		return ("Query administrator user number failed: %v", err)
	}

	// If there is no user named admin, create a default administrator	if count == 0 {
		adminHash, _ := ("123456")

		// Create an administrator user		_, err = (
			"INSERT INTO users (username, password_hash, role, status) VALUES (?, ?, ?, ?)",
			"admin", adminHash, "admin", 1,
		)
		if err != nil {
			return ("Creating Admin User Failed: %v", err)
		}

		("Default administrator user admin created with password 123456")
	} else {
		("The default administrator user admin already exists, no need to create")
	}

	// Add avatar field to the user table (if not exists)	_, err = (`
		ALTER TABLE users ADD COLUMN IF NOT EXISTS avatar VARCHAR(255) DEFAULT ''
	`)
	if err != nil {
		("Add avatar field warning: %v", err)
	}

	// Create a session table	_, err = (`
		CREATE TABLE IF NOT EXISTS sessions (
			id VARCHAR(100) PRIMARY KEY,
			username VARCHAR(50),
			role VARCHAR(20),
			created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
		)
	`)
	if err != nil {
		("Creating session table failed: %v", err)
	}

	// Create a password reset table	_, err = (`
		CREATE TABLE IF NOT EXISTS password_resets (
			username VARCHAR(50) PRIMARY KEY,
			token VARCHAR(100),
			expiry DATETIME
		)
	`)
	if err != nil {
		("Create password reset table failed: %v", err)
	}

	("Database initialization successfully")
	return nil
}

// CreateAccessLogTable Create access record tablefunc CreateAccessLogTable() error {
	_, err := ("CREATE TABLE IF NOT EXISTS access_log (id INT AUTO_INCREMENT PRIMARY KEY, access_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP)")
	return err
}

// GetUserByUsername Get user information based on user namefunc GetUserByUsername(username string) (, error) {
	var user 
	err := (
		"SELECT id, username, password_hash, role, login_attempts, IFNULL(last_attempt, NOW()), created_at, updated_at, IFNULL(avatar, ''), status FROM users WHERE username = ?",
		username,
	).Scan(&, &, &, &, &, &, &, &, &, &)
	return user, err
}

// UpdateUserLoginAttempts Update user login attempt informationfunc UpdateUserLoginAttempts(userID int, attempts int, lastAttempt ) error {
	_, err := (
		"UPDATE users SET login_attempts = ?, last_attempt = ? WHERE id = ?",
		attempts, lastAttempt, userID,
	)
	return err
}

// IsUsernameExists Check whether the username existsfunc IsUsernameExists(username string) bool {
	var count int
	("SELECT COUNT(*) FROM users WHERE username = ?", username).Scan(&count)
	return count > 0
}

// CreateUser Create new userfunc CreateUser(username, passwordHash string, role string, status int) error {
	_, err := (
		"INSERT INTO users (username, password_hash, role, status) VALUES (?, ?, ?, ?)",
		username, passwordHash, role, status,
	)
	return err
}

// CountRegisteredUsers counts the number of registered usersfunc CountRegisteredUsers() (int, error) {
	var count int
	err := ("SELECT COUNT(*) FROM users").Scan(&count)
	return count, err
}

// CountAccessTrends Statistical Access Trendsfunc CountAccessTrends(timeRange string) (int, error) {
	var query string
	var startDateStr string

	now := ()

	switch timeRange {
	case "7 days":
		startDateStr = (0, 0, -7).Format("2006-01-02")
		query = "SELECT COUNT(*) FROM access_log WHERE access_time >= ?"
	case "30sky":
		startDateStr = (0, 0, -30).Format("2006-01-02")
		query = "SELECT COUNT(*) FROM access_log WHERE access_time >= ?"
	default:
		query = "SELECT COUNT(*) FROM access_log"
		// No parameters are required, all visits are calculated		var count int
		err := (query).Scan(&count)
		return count, err
	}

	var count int
	err := (query, startDateStr).Scan(&count)
	return count, err
}

// GetAccessTrendData Get access trend data grouped by datefunc GetAccessTrendData(days int) ([], error) {
	// Calculate the start date, does not rely on CURDATE()	endDate := ()
	startDate := (0, 0, -days)

	// Format date is MySQL date format	startDateStr := ("2006-01-02")

	query := `
		SELECT 
			DATE(access_time) as date, 
			COUNT(*) as count 
		FROM 
			access_log 
		WHERE 
			access_time >= ? 
		GROUP BY 
			DATE(access_time) 
		ORDER BY 
			date ASC
	`

	rows, err := (query, startDateStr)
	if err != nil {
		return nil, err
	}
	defer ()

	var result []
	for () {
		var data 
		if err := (&, &); err != nil {
			return nil, err
		}
		result = append(result, data)
	}

	// If there is no data, fill in empty data	if len(result) == 0 {
		result = make([], days)
		for i := 0; i < days; i++ {
			date := ().AddDate(0, 0, -days+i+1)
			result[i] = {
				Date:  ("2006-01-02"),
				Count: 0,
			}
		}
		return result, nil
	}

	// Fill in missing dates	filled := make([], 0)
	currentDate := (0, 0, 1)

	dataMap := make(map[string]int)
	for _, data := range result {
		dataMap[] = 
	}

	for !(endDate) {
		dateStr := ("2006-01-02")
		count, exists := dataMap[dateStr]
		if !exists {
			count = 0
		}
		filled = append(filled, {
			Date:  dateStr,
			Count: count,
		})
		currentDate = (0, 0, 1)
	}

	return filled, nil
}

The db package is responsible for interacting with the database and completing various data additions, deletions, modifications and search operations. It provides a series of functions from initializing database connections, creating necessary table structures, to user authentication, data statistics, etc.

InitDBFunctions are the core entrance to the database module. They connect to the MySQL database according to the environment variable configuration and create the necessary table structures, including user tables, session tables and password reset tables. It also checks if the default administrator user exists (admin), create if it does not exist, and set a default password for it.

GetUserByUsernameThe function querys user information based on the user name and returns a user-specific information.Structure.

UpdateUserLoginAttemptsUsed to update the user's login attempts and last login time.

IsUsernameExistsCheck whether the specified username has been registered.

CreateUserInsert a new user record into the database.

CountRegisteredUsersandCountAccessTrendsUsed to count the number of registered users and access trend data respectively.

GetAccessTrendDataGet access trend data grouped by date, used to draw access statistics charts on the front end.

handlers package

package handlers

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"GoWeb1/config"
	"GoWeb1/db"
	"GoWeb1/utils"

	"/gorilla/sessions"
)

// LoginHandler login processing functionfunc LoginHandler(w , r *) {
	if  == "GET" {
		data := map[string]interface{}{}
		if ().Get("registered") == "1" {
			data["success"] = "Registered successfully, please log in"
		}
		if ().Get("reset") == "1" {
			data["success"] = "Password reset successfully, please log in with the new password"
		}

		tmpl := (("templates/"))
		(w, data)
		return
	}

	// POST processing	username := ("username")
	password := ("password")

	("Try to log in: username=%s", username)

	// Query the user	user, err := (username)
	if err != nil {
		("Query user failed: %v", err)
		(w, "Error in username or password", )
		return
	}

	("Find the user: ID=%d, username=%s, Role=%s", , , )

	// Check password	if !(password, ) {
		("Password verification failed")
		(w, "Error in username or password", )
		return
	}

	("Password verification succeeded")

	// Check whether the user status is disabled	if  == 0 {
		("User %s has been disabled by the administrator", username)
		(w, "Your account has been disabled, please contact the administrator", )
		return
	}

	// Update the user's login attempts and last login time	err = (, 0, ())
	if err != nil {
		("Update user login time failed: %v", err)
		// Continue to process without interrupting the login process	}

	// Clear all existing cookies	for _, cookie := range () {
		newCookie := &{
			Name:     ,
			Value:    "",
			Path:     "/",
			MaxAge:   -1,
		}
		(w, newCookie)
	}

	// Delete all old session records of the user	_, err = ("DELETE FROM sessions WHERE username = ?", username)
	if err != nil {
		("Delete old session record failed: %v", err)
		// Continue to process without interrupting the login process	}

	// Create a new session ID	sessionID := (32)
	(w, &{
		Name:     "user_session",
		Value:    sessionID,
		Path:     "/",
		HttpOnly: true,
		MaxAge:   86400, // 1 day	})

	// Insert a new session record	_, err = ("INSERT INTO sessions (id, username, role) VALUES (?, ?, ?)", sessionID, , )
	if err != nil {
		("Save session failed: %v", err)
		(w, "Internal Server Error", )
		return
	}

	("Session created, redirected to homepage")
	(w, r, "/", )
}

// RegisterHandler registration processingfunc RegisterHandler(w , r *) {
	if  == "GET" {
		tmpl := (("templates/"))
		(w, nil)
		return
	}

	// POST request processing	username := ("username")
	password := ("password")
.confirmPassword := ("confirm_password")

	// Form Verification	var errorMsg string
	if !(username) {
		errorMsg = "The username must be 4-20 characters and can only contain letters, numbers and underscores"
	} else if (username) {
		errorMsg = "Username has been used"
	} else if !(password) {
		errorMsg = "The password requires at least 6 characters"
	} else if password != .confirmPassword {
		errorMsg = "The password entered twice is inconsistent"
	}

	if errorMsg != "" {
		()
		([]byte(errorMsg))
		return
	}

	// Create a user	hashedPassword, err := (password)
	if err != nil {
		("Password encryption failed: %v", err)
		(w, "Register failed, please try again later", )
		return
	}

	err = (username, hashedPassword, "user", 1)
	if err != nil {
		("Creating user failed: %v", err)
		(w, "Register failed, please try again later", )
		return
	}

	("User %s registered successfully", username)
	(w, r, "/login?registered=1", )
}

// LogoutHandler logout processingfunc LogoutHandler(w , r *) {
	// Get session ID from cookies	cookie, err := ("user_session")
	if err == nil {
		// Delete the session record in the database		_, err := ("DELETE FROM sessions WHERE id = ?", )
		if err != nil {
			("Delete session record failed: %v", err)
		}

		// Clear cookies		(w, &{
			Name:     "user_session",
			Value:    "",
			Path:     "/",
			MaxAge:   -1,
			HttpOnly: true,
		})
	}

	// Clear all other possible cookies	for _, c := range () {
		(w, &{
			Name:     ,
			Value:    "",
			Path:     "/",
			MaxAge:   -1,
			HttpOnly: true,
		})
	}

	// Set response header to prevent cache	().Set("Cache-Control", "no-cache, no-store, must-revalidate")
	().Set("Pragma", "no-cache")
	().Set("Expires", "0")

	// Redirect to the login page	(w, r, "/login", )
}

// ClearCookieHandler Clears session cookie handling functionfunc ClearCookieHandler(w , r *) {
	// Clear session cookies	cookie := &{
		Name:     "session",
		Value:    "",
		Path:     "/",
		MaxAge:   -1,
		HttpOnly: true,
	}
	(w, cookie)

	().Set("Content-Type", "text/html; charset=utf-8")
	([]byte(`
		<html>
		<head>
			<title>Session cleared</title>
			<meta http-equiv="refresh" content="2;url=/login">
			<style>
				body { font-family: Arial, sans-serif; text-align: center; margin-top: 100px; }
			</style>
		</head>
		<body>
			<h1>Session cleared</h1>
			<p>Redirecting to the login page...</p>
		</body>
		</html>
	`))
}

The handlers package is the request processing center of the project. It defines various HTTP request processing functions and implements the interactive logic between the user and the system.

LoginHandlerProcess user login requests. For GET requests, it renders the login page; for POST requests, it verifies the user name and password entered by the user, querys the user information in the database, checks whether the password matches and whether the user status is normal. If the verification is passed, a new session ID is created for the user, stored in the database, and sent it to the client as a cookie, and finally redirected to the user to the homepage.

RegisterHandlerProcess user registration request. It verifies the registration information entered by the user, including the user name format, password strength, and whether the password is consistent when the two inputs are entered. After verification is passed, the password is encrypted and the new user information is inserted into the database. After the registration is successful, redirect the user to the login page.

LogoutHandlerImplement user logout function. It ensures that the user exits the system safely by obtaining the session ID in the user cookie, deleting the corresponding session record from the database, and clearing the client's session cookie.

ClearCookieHandlerUsed to clear session cookies, which resets session-related cookies and redirects the user to the login page.

These processing functions work together to realize the core functions of user authentication and session management, ensuring that users can log in, register and log out of the system safely.

middleware package

package middleware

import (
	"context"
	"log"
	"net/http"

	"GoWeb1/config"
)

// AuthMiddleware Authentication Middlewarefunc AuthMiddleware(next )  {
	return func(w , r *) {
		// Get session ID from cookies		cookie, err := ("user_session")
		if err != nil {
			("Failed to get session cookie: %v", err)
			(w, r, "/login", )
			return
		}

		// Verify the session from the database		var username, role string
		err = ("SELECT username, role FROM sessions WHERE id = ?", ).Scan(&username, &role)
		if err != nil {
			("Query session record failed: %v", err)
			(w, r, "/login", )
			return
		}

		// Check whether the user status is disabled		var status int
		err = ("SELECT status FROM users WHERE username = ?", username).Scan(&status)
		if err != nil || status == 0 {
			// If the user is disabled, delete the session and redirect to the login page			("DELETE FROM sessions WHERE id = ?", )
			(w, &{
				Name:     "user_session",
				Value:    "",
				Path:     "/",
				MaxAge:   -1,
				HttpOnly: true,
			})
			(w, r, "/login", )
			return
		}

		// The session is valid, set the context and call the next processing function		r = (((), "username", username))
		r = (((), "role", role))
		next(w, r)
	}
}

// AdminMiddleware Admin permissions middlewarefunc AdminMiddleware(next )  {
	return func(w , r *) {
		// Get session ID from cookies		cookie, err := ("user_session")
		if err != nil {
			("Failed to get session cookie: %v", err)
			(w, "Unauthorized", )
			return
		}

		// Get username and role from the database		var username, role string
		err = ("SELECT username, role FROM sessions WHERE id = ?", ).Scan(&username, &role)
		if err != nil {
			("Query session record failed: %v", err)
			(w, "Unauthorized", )
			return
		}

		// Check if it is an administrator role		if role != "admin" {
			(w, "Insufficient permissions", )
			return
		}

		next(w, r)
	}
}

The middleware package defines two core middleware:AuthMiddlewareandAdminMiddleware

AuthMiddlewareUsed to verify the session of ordinary users. It checks the session cookies in the request and queries the session history in the database to verify that the user is logged in. If the session is invalid or the user has been disabled, it redirects the user to the login page. For a valid session, it stores user information into the request context for use by subsequent processing functions.

AdminMiddlewareThen inAuthMiddlewareBased on the   , further verify whether the user has administrator rights. Only if the user role isadminOnly when access to protected administrator routes is allowed.

These middlewares ensure the security of the system and the correct access to the functions by intercepting the request, verifying the user identity and permissions before the request reaches the processing function.

utils package

package utils

import (
	"crypto/rand"
	"encoding/base64"
	"regexp"

	"/x/crypto/bcrypt"
)

// HashPassword password encryption functionfunc HashPassword(password string) (string, error) {
	bytes, err := ([]byte(password), 14)
	return string(bytes), err
}

// CheckPassword Verify passwordfunc CheckPassword(password, hash string) bool {
	err := ([]byte(hash), []byte(password))
	return err == nil
}

// GenerateRandomString generates random stringsfunc GenerateRandomString(length int) string {
	b := make([]byte, length)
	(b)
	return (b)[:length]
}

// IsValidUsername Verify username formatfunc IsValidUsername(username string) bool {
	if len(username) < 4 || len(username) > 20 {
		return false
	}
	// Only letters, numbers and underscores are allowed	re := ("^[a-zA-Z0-9_]+$")
	return (username)
}

// IsValidPassword Verify password strengthfunc IsValidPassword(password string) bool {
	return len(password) >= 6
}

The utils package provides reusable tool functions in the project.

HashPasswordUse the bcrypt algorithm to encrypt the password to generate a secure password hash.

CheckPasswordUsed to verify whether the plaintext password entered by the user matches the password hash value stored in the database.

GenerateRandomStringGenerate random strings of a specified length for use in scenarios such as session ID, password reset tokens, etc.

IsValidUsernameandIsValidPasswordUsed to verify whether the user name and password meet the specified format and strength requirements.

4. Summary

This Go-based web backend management system project still has many bugs and shortcomings, and I would be grateful if I have any advice.

This is the end of this article about the implementation of the Go Web Backend Management System project. For more information about the Go Web Backend Management System project, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!