mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-14 20:30:25 +08:00
安全设置增加“检查客户端指纹"和"检查客户端区域"选项
This commit is contained in:
@@ -29,7 +29,7 @@ func LoadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig)
|
var v = reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig)
|
||||||
return &v, nil
|
return &v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,12 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
|
|||||||
return sharedSecurityConfig, nil
|
return sharedSecurityConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &systemconfigs.SecurityConfig{}
|
var config = &systemconfigs.SecurityConfig{
|
||||||
|
Frame: FrameSameOrigin,
|
||||||
|
AllowLocal: true,
|
||||||
|
CheckClientFingerprint: false,
|
||||||
|
CheckClientRegion: true,
|
||||||
|
}
|
||||||
err = json.Unmarshal(resp.ValueJSON, config)
|
err = json.Unmarshal(resp.ValueJSON, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("[SECURITY_MANAGER]" + err.Error())
|
logs.Println("[SECURITY_MANAGER]" + err.Error())
|
||||||
@@ -100,7 +105,9 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
|
|||||||
|
|
||||||
func defaultSecurityConfig() *systemconfigs.SecurityConfig {
|
func defaultSecurityConfig() *systemconfigs.SecurityConfig {
|
||||||
return &systemconfigs.SecurityConfig{
|
return &systemconfigs.SecurityConfig{
|
||||||
Frame: FrameSameOrigin,
|
Frame: FrameSameOrigin,
|
||||||
AllowLocal: true,
|
AllowLocal: true,
|
||||||
|
CheckClientFingerprint: false,
|
||||||
|
CheckClientRegion: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ func (this *AdminNode) Run() {
|
|||||||
// 启动API节点
|
// 启动API节点
|
||||||
this.startAPINode()
|
this.startAPINode()
|
||||||
|
|
||||||
|
// 启动IP库
|
||||||
|
this.startIPLibrary()
|
||||||
|
|
||||||
// 启动Web服务
|
// 启动Web服务
|
||||||
sessionManager, err := NewSessionManager()
|
sessionManager, err := NewSessionManager()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
18
internal/nodes/admin_node_ext.go
Normal file
18
internal/nodes/admin_node_ext.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
//go:build !plus
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 启动IP库
|
||||||
|
func (this *AdminNode) startIPLibrary() {
|
||||||
|
logs.Println("NODE", "initializing ip library ...")
|
||||||
|
err := iplibrary.InitDefault()
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("NODE", "initialize ip library failed: "+err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,9 @@ package rpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,3 +31,23 @@ func SharedRPC() (*RPCClient, error) {
|
|||||||
sharedRPC = client
|
sharedRPC = client
|
||||||
return sharedRPC, nil
|
return sharedRPC, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsConnError 是否为连接错误
|
||||||
|
func IsConnError(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为连接错误
|
||||||
|
statusErr, ok := status.FromError(err)
|
||||||
|
if ok {
|
||||||
|
var errorCode = statusErr.Code()
|
||||||
|
return errorCode == codes.Unavailable || errorCode == codes.Canceled
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(err.Error(), "code = Canceled") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,16 +4,41 @@ package loginutils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||||
|
"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/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CalculateClientFingerprint 计算客户端指纹
|
// CalculateClientFingerprint 计算客户端指纹
|
||||||
func CalculateClientFingerprint(action *actions.ActionObject) string {
|
func CalculateClientFingerprint(action *actions.ActionObject) string {
|
||||||
return stringutil.Md5(action.RequestRemoteIP() + "@" + action.Request.UserAgent())
|
return stringutil.Md5(RemoteIP(action) + "@" + action.Request.UserAgent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoteIP 获取客户端IP
|
||||||
|
// TODO 将来增加是否使用代理设置(即从X-Real-IP中获取IP)
|
||||||
|
func RemoteIP(action *actions.ActionObject) string {
|
||||||
|
ip, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupIPRegion 查找登录区域
|
||||||
|
func LookupIPRegion(ip string) string {
|
||||||
|
if len(ip) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = iplibrary.LookupIP(ip)
|
||||||
|
if result != nil && result.IsOk() {
|
||||||
|
// 这里不需要网络运营商信息
|
||||||
|
return result.CountryName() + "@" + result.ProvinceName() + "@" + result.CityName() + "@" + result.TownName()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCookie 设置Cookie
|
||||||
func SetCookie(action *actions.ActionObject, remember bool) {
|
func SetCookie(action *actions.ActionObject, remember bool) {
|
||||||
if remember {
|
if remember {
|
||||||
var cookie = &http.Cookie{
|
var cookie = &http.Cookie{
|
||||||
@@ -44,6 +69,7 @@ func SetCookie(action *actions.ActionObject, remember bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnsetCookie 重置Cookie
|
||||||
func UnsetCookie(action *actions.ActionObject) {
|
func UnsetCookie(action *actions.ActionObject) {
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: teaconst.CookieSID,
|
Name: teaconst.CookieSID,
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
DenySearchEngines bool
|
DenySearchEngines bool
|
||||||
DenySpiders bool
|
DenySpiders bool
|
||||||
|
|
||||||
|
CheckClientFingerprint bool
|
||||||
|
CheckClientRegion bool
|
||||||
|
|
||||||
DomainsJSON []byte
|
DomainsJSON []byte
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
@@ -150,6 +153,10 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
// 允许记住登录
|
// 允许记住登录
|
||||||
config.AllowRememberLogin = params.AllowRememberLogin
|
config.AllowRememberLogin = params.AllowRememberLogin
|
||||||
|
|
||||||
|
// Cookie检查
|
||||||
|
config.CheckClientFingerprint = params.CheckClientFingerprint
|
||||||
|
config.CheckClientRegion = params.CheckClientRegion
|
||||||
|
|
||||||
err = configloaders.UpdateSecurityConfig(config)
|
err = configloaders.UpdateSecurityConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
@@ -175,13 +175,32 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查指纹
|
// 检查指纹
|
||||||
var clientFingerprint = session.GetString("@fingerprint")
|
if securityConfig != nil && securityConfig.CheckClientFingerprint {
|
||||||
if len(clientFingerprint) > 0 && clientFingerprint != loginutils.CalculateClientFingerprint(action) {
|
var clientFingerprint = session.GetString("@fingerprint")
|
||||||
loginutils.UnsetCookie(action)
|
if len(clientFingerprint) > 0 && clientFingerprint != loginutils.CalculateClientFingerprint(action) {
|
||||||
session.Delete()
|
loginutils.UnsetCookie(action)
|
||||||
|
session.Delete()
|
||||||
|
|
||||||
this.login(action)
|
this.login(action)
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查区域
|
||||||
|
if securityConfig != nil && securityConfig.CheckClientRegion {
|
||||||
|
var oldClientIP = session.GetString("@ip")
|
||||||
|
var currentClientIP = loginutils.RemoteIP(action)
|
||||||
|
if len(oldClientIP) > 0 && len(currentClientIP) > 0 && oldClientIP != currentClientIP {
|
||||||
|
var oldRegion = loginutils.LookupIPRegion(oldClientIP)
|
||||||
|
var newRegion = loginutils.LookupIPRegion(currentClientIP)
|
||||||
|
if newRegion != oldRegion {
|
||||||
|
loginutils.UnsetCookie(action)
|
||||||
|
session.Delete()
|
||||||
|
|
||||||
|
this.login(action)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查用户是否存在
|
// 检查用户是否存在
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ func (this *UserShouldAuth) StoreAdmin(adminId int64, remember bool) {
|
|||||||
var session = this.action.Session()
|
var session = this.action.Session()
|
||||||
session.Write("adminId", numberutils.FormatInt64(adminId))
|
session.Write("adminId", numberutils.FormatInt64(adminId))
|
||||||
session.Write("@fingerprint", loginutils.CalculateClientFingerprint(this.action))
|
session.Write("@fingerprint", loginutils.CalculateClientFingerprint(this.action))
|
||||||
|
session.Write("@ip", loginutils.RemoteIP(this.action))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *UserShouldAuth) IsUser() bool {
|
func (this *UserShouldAuth) IsUser() bool {
|
||||||
|
|||||||
@@ -77,6 +77,20 @@
|
|||||||
<p class="comment">只允许通过这些域名(或者IP作为主机地址)访问当前管理系统,不填表示没有限制。</p>
|
<p class="comment">只允许通过这些域名(或者IP作为主机地址)访问当前管理系统,不填表示没有限制。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>检查客户端指纹</td>
|
||||||
|
<td>
|
||||||
|
<checkbox name="checkClientFingerprint" v-model="config.checkClientFingerprint"></checkbox>
|
||||||
|
<p class="comment">选中后,表示每次访问时都检查客户端相关信息是否跟登录时一致。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>检查客户端区域</td>
|
||||||
|
<td>
|
||||||
|
<checkbox name="checkClientRegion" v-model="config.checkClientRegion"></checkbox>
|
||||||
|
<p class="comment">选中后,表示每次访问时都检查客户端所在地理区域是否和登录时一致。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<submit-btn></submit-btn>
|
<submit-btn></submit-btn>
|
||||||
|
|||||||
Reference in New Issue
Block a user