mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 05:00:25 +08:00 
			
		
		
		
	管理系统安全设置增加“自定义客户端IP报头”
This commit is contained in:
		@@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/utils"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/langs"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/langs"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
 | 
				
			||||||
@@ -106,7 +107,7 @@ func (this *ParentAction) CreateLog(level string, messageCode langs.MessageCode,
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err := dao.SharedLogDAO.CreateAdminLog(this.AdminContext(), level, this.Request.URL.Path, desc, this.RequestRemoteIP(), messageCode, args)
 | 
						err := dao.SharedLogDAO.CreateAdminLog(this.AdminContext(), level, this.Request.URL.Path, desc, loginutils.RemoteIP(&this.ActionObject), messageCode, args)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		utils.PrintError(err)
 | 
							utils.PrintError(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,7 +175,7 @@ func (this *IndexAction) RunPost(params struct {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelError, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSystemError, err.Error()), this.RequestRemoteIP(), codes.AdminLogin_LogSystemError, []any{err.Error()})
 | 
							err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelError, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSystemError, err.Error()), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogSystemError, []any{err.Error()})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			utils.PrintError(err)
 | 
								utils.PrintError(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -185,7 +185,7 @@ func (this *IndexAction) RunPost(params struct {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !resp.IsOk {
 | 
						if !resp.IsOk {
 | 
				
			||||||
		err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelWarn, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogFailed, params.Username), this.RequestRemoteIP(), codes.AdminLogin_LogFailed, []any{params.Username})
 | 
							err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelWarn, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogFailed, params.Username), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogFailed, []any{params.Username})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			utils.PrintError(err)
 | 
								utils.PrintError(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -225,7 +225,7 @@ func (this *IndexAction) RunPost(params struct {
 | 
				
			|||||||
	params.Auth.StoreAdmin(adminId, params.Remember)
 | 
						params.Auth.StoreAdmin(adminId, params.Remember)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 记录日志
 | 
						// 记录日志
 | 
				
			||||||
	err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSuccess, params.Username), this.RequestRemoteIP(), codes.AdminLogin_LogSuccess, []any{params.Username})
 | 
						err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSuccess, params.Username), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogSuccess, []any{params.Username})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		utils.PrintError(err)
 | 
							utils.PrintError(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -235,7 +235,7 @@ func (this *IndexAction) RunPost(params struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 检查登录区域
 | 
					// 检查登录区域
 | 
				
			||||||
func (this *IndexAction) checkRegion() bool {
 | 
					func (this *IndexAction) checkRegion() bool {
 | 
				
			||||||
	var ip = this.RequestRemoteIP()
 | 
						var ip = loginutils.RemoteIP(&this.ActionObject)
 | 
				
			||||||
	var result = iplibrary.LookupIP(ip)
 | 
						var result = iplibrary.LookupIP(ip)
 | 
				
			||||||
	if result != nil && result.IsOk() && result.CountryId() > 0 && lists.ContainsInt64([]int64{9, 10}, result.CountryId()) {
 | 
						if result != nil && result.IsOk() && result.CountryId() > 0 && lists.ContainsInt64([]int64{9, 10}, result.CountryId()) {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,12 +3,15 @@
 | 
				
			|||||||
package loginutils
 | 
					package loginutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
 | 
				
			||||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
						teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
 | 
				
			||||||
	"github.com/iwind/TeaGo/actions"
 | 
						"github.com/iwind/TeaGo/actions"
 | 
				
			||||||
	stringutil "github.com/iwind/TeaGo/utils/string"
 | 
						stringutil "github.com/iwind/TeaGo/utils/string"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CalculateClientFingerprint 计算客户端指纹
 | 
					// CalculateClientFingerprint 计算客户端指纹
 | 
				
			||||||
@@ -17,8 +20,26 @@ func CalculateClientFingerprint(action *actions.ActionObject) string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RemoteIP 获取客户端IP
 | 
					// RemoteIP 获取客户端IP
 | 
				
			||||||
// TODO 将来增加是否使用代理设置(即从X-Real-IP中获取IP)
 | 
					 | 
				
			||||||
func RemoteIP(action *actions.ActionObject) string {
 | 
					func RemoteIP(action *actions.ActionObject) string {
 | 
				
			||||||
 | 
						securityConfig, _ := configloaders.LoadSecurityConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if securityConfig != nil {
 | 
				
			||||||
 | 
							if len(securityConfig.ClientIPHeaderNames) > 0 {
 | 
				
			||||||
 | 
								var headerNames = regexp.MustCompile(`[,;\s,、;]`).Split(securityConfig.ClientIPHeaderNames, -1)
 | 
				
			||||||
 | 
								for _, headerName := range headerNames {
 | 
				
			||||||
 | 
									headerName = http.CanonicalHeaderKey(strings.TrimSpace(headerName))
 | 
				
			||||||
 | 
									if len(headerName) == 0 {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									var ipValue = action.Request.Header.Get(headerName)
 | 
				
			||||||
 | 
									if net.ParseIP(ipValue) != nil {
 | 
				
			||||||
 | 
										return ipValue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ip, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
 | 
						ip, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
 | 
				
			||||||
	return ip
 | 
						return ip
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ import (
 | 
				
			|||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/setup"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/setup"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/utils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
 | 
				
			||||||
@@ -146,7 +147,7 @@ func (this *OtpAction) RunPost(params struct {
 | 
				
			|||||||
		this.ErrorPage(err)
 | 
							this.ErrorPage(err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, this.Lang(codes.AdminLogin_LogOtpVerifiedSuccess), this.RequestRemoteIP(), codes.AdminLogin_LogOtpVerifiedSuccess, nil)
 | 
						err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, this.Lang(codes.AdminLogin_LogOtpVerifiedSuccess), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogOtpVerifiedSuccess, nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		utils.PrintError(err)
 | 
							utils.PrintError(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,6 +77,8 @@ func (this *IndexAction) RunPost(params struct {
 | 
				
			|||||||
	AllowIPs           []string
 | 
						AllowIPs           []string
 | 
				
			||||||
	AllowRememberLogin bool
 | 
						AllowRememberLogin bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ClientIPHeaderNames string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DenySearchEngines bool
 | 
						DenySearchEngines bool
 | 
				
			||||||
	DenySpiders       bool
 | 
						DenySpiders       bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -137,6 +139,9 @@ func (this *IndexAction) RunPost(params struct {
 | 
				
			|||||||
	// 允许本地
 | 
						// 允许本地
 | 
				
			||||||
	config.AllowLocal = params.AllowLocal
 | 
						config.AllowLocal = params.AllowLocal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 客户端IP获取方式
 | 
				
			||||||
 | 
						config.ClientIPHeaderNames = params.ClientIPHeaderNames
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 禁止搜索引擎和爬虫
 | 
						// 禁止搜索引擎和爬虫
 | 
				
			||||||
	config.DenySearchEngines = params.DenySearchEngines
 | 
						config.DenySearchEngines = params.DenySearchEngines
 | 
				
			||||||
	config.DenySpiders = params.DenySpiders
 | 
						config.DenySpiders = params.DenySpiders
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,6 @@ import (
 | 
				
			|||||||
	"github.com/iwind/TeaGo/lists"
 | 
						"github.com/iwind/TeaGo/lists"
 | 
				
			||||||
	"github.com/iwind/TeaGo/logs"
 | 
						"github.com/iwind/TeaGo/logs"
 | 
				
			||||||
	"github.com/iwind/TeaGo/maps"
 | 
						"github.com/iwind/TeaGo/maps"
 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
@@ -145,12 +144,7 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
 | 
				
			|||||||
	action.AddHeader("Content-Security-Policy", "default-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'")
 | 
						action.AddHeader("Content-Security-Policy", "default-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 检查IP
 | 
						// 检查IP
 | 
				
			||||||
	if !checkIP(securityConfig, action.RequestRemoteIP()) {
 | 
						if !checkIP(securityConfig, loginutils.RemoteIP(action)) {
 | 
				
			||||||
		action.ResponseWriter.WriteHeader(http.StatusForbidden)
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	remoteAddr, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
 | 
					 | 
				
			||||||
	if len(remoteAddr) > 0 && remoteAddr != action.RequestRemoteIP() && !checkIP(securityConfig, remoteAddr) {
 | 
					 | 
				
			||||||
		action.ResponseWriter.WriteHeader(http.StatusForbidden)
 | 
							action.ResponseWriter.WriteHeader(http.StatusForbidden)
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ import (
 | 
				
			|||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
 | 
				
			||||||
	"github.com/iwind/TeaGo/actions"
 | 
						"github.com/iwind/TeaGo/actions"
 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -33,12 +32,7 @@ func (this *UserShouldAuth) BeforeAction(actionPtr actions.ActionWrapper, paramN
 | 
				
			|||||||
	action.AddHeader("Content-Security-Policy", "default-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'")
 | 
						action.AddHeader("Content-Security-Policy", "default-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 检查IP
 | 
						// 检查IP
 | 
				
			||||||
	if !checkIP(securityConfig, action.RequestRemoteIP()) {
 | 
						if !checkIP(securityConfig, loginutils.RemoteIP(action)) {
 | 
				
			||||||
		action.ResponseWriter.WriteHeader(http.StatusForbidden)
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	remoteAddr, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
 | 
					 | 
				
			||||||
	if len(remoteAddr) > 0 && remoteAddr != action.RequestRemoteIP() && !checkIP(securityConfig, remoteAddr) {
 | 
					 | 
				
			||||||
		action.ResponseWriter.WriteHeader(http.StatusForbidden)
 | 
							action.ResponseWriter.WriteHeader(http.StatusForbidden)
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
{$layout}
 | 
					{$layout}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="margin"></div>
 | 
					<div class="margin"></div>
 | 
				
			||||||
 | 
					<p class="comment">当前安全设置只适用于管理系统,对用户网站没有任何影响。</p>
 | 
				
			||||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
 | 
					<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
 | 
				
			||||||
	<csrf-token></csrf-token>
 | 
						<csrf-token></csrf-token>
 | 
				
			||||||
	<table class="ui table definition selectable">
 | 
						<table class="ui table definition selectable">
 | 
				
			||||||
@@ -56,6 +57,13 @@
 | 
				
			|||||||
            </td>
 | 
					            </td>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
        <tbody v-show="moreOptionsVisible">
 | 
					        <tbody v-show="moreOptionsVisible">
 | 
				
			||||||
 | 
					            <tr>
 | 
				
			||||||
 | 
					                <td>自定义客户端IP报头</td>
 | 
				
			||||||
 | 
					                <td>
 | 
				
			||||||
 | 
					                    <input type="text" name="clientIPHeaderNames" v-model="config.clientIPHeaderNames"/>
 | 
				
			||||||
 | 
					                    <p class="comment">可以通过此报头获取客户端IP,类似于<code-label>Client-IP</code-label>,用于使用反向代理访问管理系统的情形;如果有多个报头可以使用空格隔开。</p>
 | 
				
			||||||
 | 
					                </td>
 | 
				
			||||||
 | 
					            </tr>
 | 
				
			||||||
            <tr>
 | 
					            <tr>
 | 
				
			||||||
                <td>禁止搜索引擎</td>
 | 
					                <td>禁止搜索引擎</td>
 | 
				
			||||||
                <td>
 | 
					                <td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,13 +19,13 @@
 | 
				
			|||||||
			</td>
 | 
								</td>
 | 
				
			||||||
		</tr>
 | 
							</tr>
 | 
				
			||||||
		<tr>
 | 
							<tr>
 | 
				
			||||||
			<td>是否显示底部开源信息</td>
 | 
								<td>显示底部开源信息</td>
 | 
				
			||||||
			<td>
 | 
								<td>
 | 
				
			||||||
				<checkbox name="showOpenSourceInfo" v-model="config.showOpenSourceInfo"></checkbox>
 | 
									<checkbox name="showOpenSourceInfo" v-model="config.showOpenSourceInfo"></checkbox>
 | 
				
			||||||
			</td>
 | 
								</td>
 | 
				
			||||||
		</tr>
 | 
							</tr>
 | 
				
			||||||
		<tr>
 | 
							<tr>
 | 
				
			||||||
			<td>是否显示版本号</td>
 | 
								<td>显示版本号</td>
 | 
				
			||||||
			<td>
 | 
								<td>
 | 
				
			||||||
				<checkbox name="showVersion" v-model="config.showVersion"></checkbox>
 | 
									<checkbox name="showVersion" v-model="config.showVersion"></checkbox>
 | 
				
			||||||
                <p class="comment">选中后,在界面中显示系统版本号。</p>
 | 
					                <p class="comment">选中后,在界面中显示系统版本号。</p>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user