diff --git a/ani-rss-application/src/main/java/ani/rss/controller/ConfigController.java b/ani-rss-application/src/main/java/ani/rss/controller/ConfigController.java index 61204b53..8d065e36 100644 --- a/ani-rss-application/src/main/java/ani/rss/controller/ConfigController.java +++ b/ani-rss-application/src/main/java/ani/rss/controller/ConfigController.java @@ -32,6 +32,7 @@ import lombok.Cleanup; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jsoup.Jsoup; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -304,7 +305,7 @@ public class ConfigController extends BaseController { @Auth @Operation(summary = "导入设置") - @PostMapping("/importConfig") + @PostMapping(value = "/importConfig", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Result importConfig(@RequestParam("file") MultipartFile file) throws IOException { String originalFilename = file.getOriginalFilename(); String extName = FileUtil.extName(originalFilename); diff --git a/ani-rss-application/src/main/java/ani/rss/controller/UploadController.java b/ani-rss-application/src/main/java/ani/rss/controller/UploadController.java index ee2dc3dc..be21e952 100644 --- a/ani-rss-application/src/main/java/ani/rss/controller/UploadController.java +++ b/ani-rss-application/src/main/java/ani/rss/controller/UploadController.java @@ -9,6 +9,7 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.crypto.SecureUtil; import io.swagger.v3.oas.annotations.Operation; import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -22,7 +23,7 @@ import java.io.IOException; public class UploadController extends BaseController { @Auth @Operation(summary = "上传文件") - @PostMapping("/upload") + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Result upload(@RequestParam("file") MultipartFile file) throws IOException { HttpServletRequest request = Global.REQUEST.get(); String type = request.getParameter("type"); diff --git a/ani-rss-application/src/main/java/ani/rss/entity/Config.java b/ani-rss-application/src/main/java/ani/rss/entity/Config.java index c4151c04..53af0561 100644 --- a/ani-rss-application/src/main/java/ani/rss/entity/Config.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/Config.java @@ -731,4 +731,10 @@ public class Config implements Serializable { */ @Schema(description = "构建信息") private String buildInfo; + + @Schema(description = "启用 受信任的反向代理IP") + private Boolean reverseProxyTrustIpListEnabled; + + @Schema(description = "受信任的反向代理IP") + private List reverseProxyTrustIpList; } diff --git a/ani-rss-application/src/main/java/ani/rss/util/other/AuthUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/AuthUtil.java index 7ee5ef9a..a4c65492 100644 --- a/ani-rss-application/src/main/java/ani/rss/util/other/AuthUtil.java +++ b/ani-rss-application/src/main/java/ani/rss/util/other/AuthUtil.java @@ -10,6 +10,7 @@ import ani.rss.entity.Global; import ani.rss.entity.Login; import ani.rss.entity.Result; import ani.rss.exception.ResultException; +import cn.hutool.core.net.NetUtil; import cn.hutool.core.text.StrFormatter; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; @@ -20,6 +21,7 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -97,8 +99,30 @@ public class AuthUtil { */ public static String getIp() { try { + Config config = ConfigUtil.CONFIG; + List reverseProxyTrustIpList = config.getReverseProxyTrustIpList(); + Boolean reverseProxyTrustIpListEnabled = config.getReverseProxyTrustIpListEnabled(); + HttpServletRequest request = Global.REQUEST.get(); - return request.getRemoteAddr(); + String ip = request.getRemoteAddr(); + if (!reverseProxyTrustIpListEnabled) { + // 未启用 受信任的反向代理IP + return ip; + } + + if (!reverseProxyTrustIpList.contains(ip)) { + // 不在名单中, 受信任的反向代理IP + return ip; + } + + // https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Reference/Headers/X-Forwarded-For + String forwardedFor = request.getHeader("X-Forwarded-For"); + if (StrUtil.isNotBlank(forwardedFor)) { + // 获取第一个IP + return NetUtil.getMultistageReverseProxyIp(forwardedFor); + } + + return ip; } catch (Exception e) { String message = ExceptionUtils.getMessage(e); log.error(message, e); diff --git a/ani-rss-application/src/main/java/ani/rss/util/other/ConfigUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/ConfigUtil.java index 699466bf..754e9c10 100644 --- a/ani-rss-application/src/main/java/ani/rss/util/other/ConfigUtil.java +++ b/ani-rss-application/src/main/java/ani/rss/util/other/ConfigUtil.java @@ -230,7 +230,9 @@ public class ConfigUtil { .setScrape(false) .setReplace(false) .setMaxFileNameLength(0) - .setLimitLoginAttempts(true); + .setLimitLoginAttempts(true) + .setReverseProxyTrustIpList(List.of("127.0.0.1")) + .setReverseProxyTrustIpListEnabled(false); } /** diff --git a/ani-rss-ui/src/config/LoginConfig.vue b/ani-rss-ui/src/config/LoginConfig.vue index 98d65c22..3b65fdb6 100644 --- a/ani-rss-ui/src/config/LoginConfig.vue +++ b/ani-rss-ui/src/config/LoginConfig.vue @@ -50,6 +50,13 @@ + +
+ +
+ +
+