mirror of
https://github.com/skyle1995/NetworkAuth.git
synced 2026-05-25 02:24:05 +08:00
更新底层架构
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"networkDev/web"
|
||||
"NetworkAuth/web"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
54
middleware/install.go
Normal file
54
middleware/install.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"NetworkAuth/services"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// InstallCheckMiddleware 检查系统是否已安装
|
||||
func InstallCheckMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
path := c.Request.URL.Path
|
||||
|
||||
// 放行静态资源和favicon
|
||||
if strings.HasPrefix(path, "/static/") || strings.HasPrefix(path, "/assets/") || path == "/favicon.ico" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否为安装相关的路由
|
||||
isInstallRoute := path == "/install" || path == "/api/install"
|
||||
|
||||
// 获取系统的安装状态
|
||||
// 在没有数据库的时候,GetSettingsService().GetString 会返回默认值 "0"
|
||||
isInstalled := services.GetSettingsService().GetString("is_installed", "0") == "1"
|
||||
|
||||
// 如果未安装且当前不是访问安装页面,则重定向到安装页面
|
||||
if !isInstalled && !isInstallRoute {
|
||||
// 对于 API 请求,返回 JSON 提示
|
||||
if strings.HasPrefix(path, "/api/") || strings.Contains(path, "/api/") {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"code": 403,
|
||||
"msg": "系统未初始化,请先完成安装",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/install")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 如果已安装但尝试访问安装页面,则重定向到首页或后台
|
||||
if isInstalled && isInstallRoute {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/admin")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"NetworkAuth/utils/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"networkDev/utils/logger"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
@@ -34,76 +36,49 @@ func NewLoggingMiddleware(logger *logger.Logger) *LoggingMiddleware {
|
||||
// ============================================================================
|
||||
|
||||
// Handler 返回Gin中间件函数,用于记录HTTP请求日志
|
||||
// 记录格式遵循Apache Common Log Format
|
||||
// 记录格式参考了更灵活的 NetworkAuth 实现,支持配置开关和日志级别检查
|
||||
func (lm *LoggingMiddleware) Handler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 记录开始时间
|
||||
// 检查是否启用了访问日志
|
||||
if !viper.GetBool("server.access_log") {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 如果日志级别不是Debug或更高(Trace),则不记录访问日志
|
||||
// 避免在Info级别输出过多的访问日志干扰正常业务日志
|
||||
if lm.logger.Level < logrus.DebugLevel {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
path := c.Request.URL.Path
|
||||
raw := c.Request.URL.RawQuery
|
||||
|
||||
// 处理请求
|
||||
c.Next()
|
||||
|
||||
// 计算处理时间
|
||||
// 计算响应时间
|
||||
duration := time.Since(start)
|
||||
|
||||
// 获取客户端IP
|
||||
clientIP := getClientIP(c)
|
||||
if raw != "" {
|
||||
path = path + "?" + raw
|
||||
}
|
||||
|
||||
// 记录日志 - Apache Common Log Format
|
||||
// 使用专门的HTTP日志方法避免User-Agent中的反斜杠被转义
|
||||
// 记录请求日志
|
||||
lm.logger.LogRequestWithHeaders(
|
||||
c.Request.Method,
|
||||
c.Request.RequestURI,
|
||||
clientIP,
|
||||
path,
|
||||
c.ClientIP(), // 使用 Gin 内置的方法获取 IP
|
||||
c.Writer.Status(),
|
||||
duration,
|
||||
"-", // referer (已废弃)
|
||||
c.Errors.ByType(gin.ErrorTypePrivate).String(),
|
||||
c.Request.UserAgent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 私有函数
|
||||
// ============================================================================
|
||||
|
||||
// getClientIP 获取客户端真实IP地址
|
||||
// 优先从X-Forwarded-For、X-Real-IP等头部获取,最后使用RemoteAddr
|
||||
func getClientIP(c *gin.Context) string {
|
||||
// 检查X-Forwarded-For头部
|
||||
xForwardedFor := c.GetHeader("X-Forwarded-For")
|
||||
if xForwardedFor != "" {
|
||||
// X-Forwarded-For可能包含多个IP,取第一个
|
||||
ips := strings.Split(xForwardedFor, ",")
|
||||
if len(ips) > 0 {
|
||||
return strings.TrimSpace(ips[0])
|
||||
}
|
||||
}
|
||||
|
||||
// 检查X-Real-IP头部
|
||||
xRealIP := c.GetHeader("X-Real-IP")
|
||||
if xRealIP != "" {
|
||||
return xRealIP
|
||||
}
|
||||
|
||||
// 检查X-Forwarded头部
|
||||
xForwarded := c.GetHeader("X-Forwarded")
|
||||
if xForwarded != "" {
|
||||
return xForwarded
|
||||
}
|
||||
|
||||
// 使用RemoteAddr
|
||||
remoteAddr := c.Request.RemoteAddr
|
||||
if strings.Contains(remoteAddr, ":") {
|
||||
// 移除端口号
|
||||
if idx := strings.LastIndex(remoteAddr, ":"); idx != -1 {
|
||||
return remoteAddr[:idx]
|
||||
}
|
||||
}
|
||||
|
||||
return remoteAddr
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 公共函数
|
||||
// ============================================================================
|
||||
@@ -111,7 +86,7 @@ func getClientIP(c *gin.Context) string {
|
||||
// WrapHandler 创建Gin日志中间件
|
||||
// 使用全局日志记录器创建日志中间件
|
||||
func WrapHandler() gin.HandlerFunc {
|
||||
logger := logger.GetLogger()
|
||||
middleware := NewLoggingMiddleware(logger)
|
||||
log := logger.GetLogger()
|
||||
middleware := NewLoggingMiddleware(log)
|
||||
return middleware.Handler()
|
||||
}
|
||||
|
||||
99
middleware/maintenance.go
Normal file
99
middleware/maintenance.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"NetworkAuth/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// MaintenanceMiddleware 维护模式中间件
|
||||
// 当开启维护模式时,拦截非白名单请求
|
||||
func MaintenanceMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 检查是否开启维护模式
|
||||
if !services.GetSettingsService().IsMaintenanceMode() {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 白名单检查(路径前缀匹配)
|
||||
path := c.Request.URL.Path
|
||||
|
||||
// 1. 允许静态资源
|
||||
if strings.HasPrefix(path, "/static/") || strings.HasPrefix(path, "/assets/") || path == "/favicon.ico" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 允许管理员后台相关接口(以便管理员登录关闭维护模式)
|
||||
// 包括登录页、登录接口、API接口、CSRF Token等
|
||||
if strings.HasPrefix(path, "/admin") {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 检查请求类型
|
||||
// AJAX/JSON 请求返回 503 JSON
|
||||
accept := c.GetHeader("Accept")
|
||||
xrw := strings.ToLower(strings.TrimSpace(c.GetHeader("X-Requested-With")))
|
||||
if strings.Contains(accept, "application/json") || xrw == "xmlhttprequest" || strings.HasPrefix(path, "/api/") {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{
|
||||
"code": 503,
|
||||
"success": false,
|
||||
"msg": "系统正在维护中,请稍后再试",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 4. 普通页面请求渲染维护页面
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
c.Status(http.StatusServiceUnavailable)
|
||||
c.Writer.WriteString(maintenanceHTML)
|
||||
c.Abort()
|
||||
}
|
||||
}
|
||||
|
||||
// 简单的维护页面 HTML
|
||||
const maintenanceHTML = `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>系统维护中</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #f0f2f5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
color: #333;
|
||||
}
|
||||
.container {
|
||||
text-align: center;
|
||||
background: white;
|
||||
padding: 40px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
}
|
||||
h1 { font-size: 24px; margin-bottom: 16px; color: #1890ff; }
|
||||
p { font-size: 16px; color: #666; line-height: 1.6; }
|
||||
.icon { font-size: 64px; margin-bottom: 24px; color: #faad14; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">⚠️</div>
|
||||
<h1>系统维护中</h1>
|
||||
<p>为了提供更好的服务,系统正在进行升级维护。<br>请稍后访问,给您带来的不便敬请谅解。</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>`
|
||||
Reference in New Issue
Block a user