mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-03-29 23:28:36 +00:00
feat(auth): enhance IP restriction handling with CIDR support
This commit is contained in:
29
common/ip.go
29
common/ip.go
@@ -2,6 +2,15 @@ package common
|
||||
|
||||
import "net"
|
||||
|
||||
func IsIP(s string) bool {
|
||||
ip := net.ParseIP(s)
|
||||
return ip != nil
|
||||
}
|
||||
|
||||
func ParseIP(s string) net.IP {
|
||||
return net.ParseIP(s)
|
||||
}
|
||||
|
||||
func IsPrivateIP(ip net.IP) bool {
|
||||
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
||||
return true
|
||||
@@ -20,3 +29,23 @@ func IsPrivateIP(ip net.IP) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsIpInCIDRList(ip net.IP, cidrList []string) bool {
|
||||
for _, cidr := range cidrList {
|
||||
_, network, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
// 尝试作为单个IP处理
|
||||
if whitelistIP := net.ParseIP(cidr); whitelistIP != nil {
|
||||
if ip.Equal(whitelistIP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if network.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -186,23 +186,7 @@ func isIPListed(ip net.IP, list []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, whitelistCIDR := range list {
|
||||
_, network, err := net.ParseCIDR(whitelistCIDR)
|
||||
if err != nil {
|
||||
// 尝试作为单个IP处理
|
||||
if whitelistIP := net.ParseIP(whitelistCIDR); whitelistIP != nil {
|
||||
if ip.Equal(whitelistIP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if network.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return IsIpInCIDRList(ip, list)
|
||||
}
|
||||
|
||||
// IsIPAccessAllowed 检查IP是否允许访问
|
||||
|
||||
@@ -217,11 +217,6 @@ func IntMax(a int, b int) int {
|
||||
}
|
||||
}
|
||||
|
||||
func IsIP(s string) bool {
|
||||
ip := net.ParseIP(s)
|
||||
return ip != nil
|
||||
}
|
||||
|
||||
func GetUUID() string {
|
||||
code := uuid.New().String()
|
||||
code = strings.Replace(code, "-", "", -1)
|
||||
|
||||
@@ -2,12 +2,14 @@ package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
@@ -240,13 +242,20 @@ func TokenAuth() func(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
allowIpsMap := token.GetIpLimitsMap()
|
||||
if len(allowIpsMap) != 0 {
|
||||
allowIpsMap := token.GetIpLimits()
|
||||
if len(allowIpsMap) > 0 {
|
||||
clientIp := c.ClientIP()
|
||||
if _, ok := allowIpsMap[clientIp]; !ok {
|
||||
logger.LogDebug(c, "Token has IP restrictions, checking client IP %s", clientIp)
|
||||
ip := net.ParseIP(clientIp)
|
||||
if ip == nil {
|
||||
abortWithOpenAiMessage(c, http.StatusForbidden, "无法解析客户端 IP 地址")
|
||||
return
|
||||
}
|
||||
if common.IsIpInCIDRList(ip, allowIpsMap) == false {
|
||||
abortWithOpenAiMessage(c, http.StatusForbidden, "您的 IP 不在令牌允许访问的列表中")
|
||||
return
|
||||
}
|
||||
logger.LogDebug(c, "Client IP %s passed the token IP restrictions check", clientIp)
|
||||
}
|
||||
|
||||
userCache, err := model.GetUserCache(token.UserId)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
|
||||
"github.com/bytedance/gopkg/util/gopool"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -35,26 +34,26 @@ func (token *Token) Clean() {
|
||||
token.Key = ""
|
||||
}
|
||||
|
||||
func (token *Token) GetIpLimitsMap() map[string]any {
|
||||
func (token *Token) GetIpLimits() []string {
|
||||
// delete empty spaces
|
||||
//split with \n
|
||||
ipLimitsMap := make(map[string]any)
|
||||
ipLimits := make([]string, 0)
|
||||
if token.AllowIps == nil {
|
||||
return ipLimitsMap
|
||||
return ipLimits
|
||||
}
|
||||
cleanIps := strings.ReplaceAll(*token.AllowIps, " ", "")
|
||||
if cleanIps == "" {
|
||||
return ipLimitsMap
|
||||
return ipLimits
|
||||
}
|
||||
ips := strings.Split(cleanIps, "\n")
|
||||
for _, ip := range ips {
|
||||
ip = strings.TrimSpace(ip)
|
||||
ip = strings.ReplaceAll(ip, ",", "")
|
||||
if common.IsIP(ip) {
|
||||
ipLimitsMap[ip] = true
|
||||
if ip != "" {
|
||||
ipLimits = append(ipLimits, ip)
|
||||
}
|
||||
}
|
||||
return ipLimitsMap
|
||||
return ipLimits
|
||||
}
|
||||
|
||||
func GetAllUserTokens(userId int, startIdx int, num int) ([]*Token, error) {
|
||||
|
||||
@@ -557,11 +557,11 @@ const EditTokenModal = (props) => {
|
||||
<Col span={24}>
|
||||
<Form.TextArea
|
||||
field='allow_ips'
|
||||
label={t('IP白名单')}
|
||||
label={t('IP白名单(支持CIDR表达式)')}
|
||||
placeholder={t('允许的IP,一行一个,不填写则不限制')}
|
||||
autosize
|
||||
rows={1}
|
||||
extraText={t('请勿过度信任此功能,IP可能被伪造')}
|
||||
extraText={t('请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用')}
|
||||
showClear
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
"Homepage URL 填": "Fill in the Homepage URL",
|
||||
"ID": "ID",
|
||||
"IP": "IP",
|
||||
"IP白名单": "IP whitelist",
|
||||
"IP白名单(支持CIDR表达式)": "IP whitelist (supports CIDR expressions)",
|
||||
"IP限制": "IP restrictions",
|
||||
"IP黑名单": "IP blacklist",
|
||||
"JSON": "JSON",
|
||||
@@ -1752,7 +1752,7 @@
|
||||
"请先阅读并同意用户协议和隐私政策": "Please read and agree to the user agreement and privacy policy first",
|
||||
"请再次输入新密码": "Please enter the new password again",
|
||||
"请前往个人设置 → 安全设置进行配置。": "Please go to Personal Settings → Security Settings to configure.",
|
||||
"请勿过度信任此功能,IP可能被伪造": "Do not over-trust this feature, IP can be spoofed",
|
||||
"请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "Do not over-trust this feature, IP can be spoofed, please use it in conjunction with gateways such as nginx and CDN",
|
||||
"请在系统设置页面编辑分组倍率以添加新的分组:": "Please edit Group ratios in system settings to add new groups:",
|
||||
"请填写完整的产品信息": "Please fill in complete product information",
|
||||
"请填写完整的管理员账号信息": "Please fill in the complete administrator account information",
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
"Homepage URL 填": "Remplir l'URL de la page d'accueil",
|
||||
"ID": "ID",
|
||||
"IP": "IP",
|
||||
"IP白名单": "Liste blanche d'adresses IP",
|
||||
"IP白名单(支持CIDR表达式)": "Liste blanche d'adresses IP (prise en charge des expressions CIDR)",
|
||||
"IP限制": "Restrictions d'IP",
|
||||
"IP黑名单": "Liste noire d'adresses IP",
|
||||
"JSON": "JSON",
|
||||
@@ -1762,7 +1762,7 @@
|
||||
"请先阅读并同意用户协议和隐私政策": "Veuillez d'abord lire et accepter l'accord utilisateur et la politique de confidentialité",
|
||||
"请再次输入新密码": "Veuillez saisir à nouveau le nouveau mot de passe",
|
||||
"请前往个人设置 → 安全设置进行配置。": "Veuillez aller dans Paramètres personnels → Paramètres de sécurité pour configurer.",
|
||||
"请勿过度信任此功能,IP可能被伪造": "Ne faites pas trop confiance à cette fonctionnalité, l'IP peut être usurpée",
|
||||
"请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "Ne faites pas trop confiance à cette fonctionnalité, l'IP peut être usurpée, veuillez l'utiliser en conjonction avec des passerelles telles que nginx et cdn",
|
||||
"请在系统设置页面编辑分组倍率以添加新的分组:": "Veuillez modifier les ratios de groupe dans les paramètres système pour ajouter de nouveaux groupes :",
|
||||
"请填写完整的产品信息": "Veuillez renseigner l'ensemble des informations produit",
|
||||
"请填写完整的管理员账号信息": "Veuillez remplir les informations complètes du compte administrateur",
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"Homepage URL 填": "ホームページURLを入力してください",
|
||||
"ID": "ID",
|
||||
"IP": "IP",
|
||||
"IP白名单": "IPホワイトリスト",
|
||||
"IP白名单(支持CIDR表达式)": "IPホワイトリスト(CIDR表記に対応)",
|
||||
"IP限制": "IP制限",
|
||||
"IP黑名单": "IPブラックリスト",
|
||||
"JSON": "JSON",
|
||||
@@ -1669,7 +1669,7 @@
|
||||
"请先阅读并同意用户协议和隐私政策": "まずユーザー利用規約とプライバシーポリシーをご確認の上、同意してください",
|
||||
"请再次输入新密码": "新しいパスワードを再入力してください",
|
||||
"请前往个人设置 → 安全设置进行配置。": "アカウント設定 → セキュリティ設定 にて設定してください。",
|
||||
"请勿过度信任此功能,IP可能被伪造": "IPは偽装される可能性があるため、この機能を過信しないでください",
|
||||
"请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "IPは偽装される可能性があるため、この機能を過信しないでください。nginxやCDNなどのゲートウェイと組み合わせて使用してください。",
|
||||
"请在系统设置页面编辑分组倍率以添加新的分组:": "新規グループを追加するには、システム設定ページでグループ倍率を編集してください:",
|
||||
"请填写完整的管理员账号信息": "管理者アカウント情報をすべて入力してください",
|
||||
"请填写密钥": "APIキーを入力してください",
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
"Homepage URL 填": "URL домашней страницы:",
|
||||
"ID": "ID",
|
||||
"IP": "IP",
|
||||
"IP白名单": "Белый список IP",
|
||||
"IP白名单(支持CIDR表达式)": "Белый список IP (поддерживает выражения CIDR)",
|
||||
"IP限制": "Ограничения IP",
|
||||
"IP黑名单": "Черный список IP",
|
||||
"JSON": "JSON",
|
||||
@@ -1773,7 +1773,7 @@
|
||||
"请先阅读并同意用户协议和隐私政策": "Пожалуйста, сначала прочтите и согласитесь с пользовательским соглашением и политикой конфиденциальности",
|
||||
"请再次输入新密码": "Пожалуйста, введите новый пароль ещё раз",
|
||||
"请前往个人设置 → 安全设置进行配置。": "Пожалуйста, перейдите в Личные настройки → Настройки безопасности для конфигурации.",
|
||||
"请勿过度信任此功能,IP可能被伪造": "Не доверяйте этой функции чрезмерно, IP может быть подделан",
|
||||
"请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "Не доверяйте этой функции чрезмерно, IP может быть подделан, используйте её вместе с nginx и CDN и другими шлюзами",
|
||||
"请在系统设置页面编辑分组倍率以添加新的分组:": "Пожалуйста, отредактируйте коэффициенты групп на странице системных настроек для добавления новой группы:",
|
||||
"请填写完整的产品信息": "Пожалуйста, заполните всю информацию о продукте",
|
||||
"请填写完整的管理员账号信息": "Пожалуйста, заполните полную информацию об учётной записи администратора",
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"Homepage URL 填": "Điền URL trang chủ",
|
||||
"ID": "ID",
|
||||
"IP": "IP",
|
||||
"IP白名单": "Danh sách trắng IP",
|
||||
"IP白名单(支持CIDR表达式)": "Danh sách trắng IP (hỗ trợ biểu thức CIDR)",
|
||||
"IP限制": "Hạn chế IP",
|
||||
"IP黑名单": "Danh sách đen IP",
|
||||
"JSON": "JSON",
|
||||
@@ -1987,7 +1987,7 @@
|
||||
"请先阅读并同意用户协议和隐私政策": "Vui lòng đọc và đồng ý với thỏa thuận người dùng và chính sách bảo mật trước",
|
||||
"请再次输入新密码": "Vui lòng nhập lại mật khẩu mới",
|
||||
"请前往个人设置 → 安全设置进行配置。": "Vui lòng truy cập Cài đặt cá nhân → Cài đặt bảo mật để cấu hình.",
|
||||
"请勿过度信任此功能,IP可能被伪造": "Đừng quá tin tưởng tính năng này, IP có thể bị giả mạo",
|
||||
"请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "Đừng quá tin tưởng tính năng này, IP có thể bị giả mạo, vui lòng sử dụng cùng với nginx và các cổng khác như cdn",
|
||||
"请在系统设置页面编辑分组倍率以添加新的分组:": "Vui lòng chỉnh sửa tỷ lệ nhóm trên trang cài đặt hệ thống để thêm nhóm mới:",
|
||||
"请填写完整的管理员账号信息": "Vui lòng điền đầy đủ thông tin tài khoản quản trị viên",
|
||||
"请填写密钥": "Vui lòng điền khóa",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"Homepage URL 填": "Homepage URL 填",
|
||||
"ID": "ID",
|
||||
"IP": "IP",
|
||||
"IP白名单": "IP白名单",
|
||||
"IP白名单(支持CIDR表达式)": "IP白名单(支持CIDR表达式)",
|
||||
"IP限制": "IP限制",
|
||||
"IP黑名单": "IP黑名单",
|
||||
"JSON": "JSON",
|
||||
@@ -1740,7 +1740,7 @@
|
||||
"请先阅读并同意用户协议和隐私政策": "请先阅读并同意用户协议和隐私政策",
|
||||
"请再次输入新密码": "请再次输入新密码",
|
||||
"请前往个人设置 → 安全设置进行配置。": "请前往个人设置 → 安全设置进行配置。",
|
||||
"请勿过度信任此功能,IP可能被伪造": "请勿过度信任此功能,IP可能被伪造",
|
||||
"请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用",
|
||||
"请在系统设置页面编辑分组倍率以添加新的分组:": "请在系统设置页面编辑分组倍率以添加新的分组:",
|
||||
"请填写完整的产品信息": "请填写完整的产品信息",
|
||||
"请填写完整的管理员账号信息": "请填写完整的管理员账号信息",
|
||||
|
||||
Reference in New Issue
Block a user