| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- package com.dms.service;
- import com.dms.common.Errors;
- import com.dms.common.IpAddresses;
- import com.dms.mapper.IpEntryMapper;
- import com.dms.model.UserEntity;
- import jakarta.servlet.http.HttpServletRequest;
- import java.net.InetAddress;
- import java.net.UnknownHostException;
- import java.util.List;
- import org.springframework.stereotype.Service;
- /**
- * 登录 IP 限制校验,对齐 backend/dms/services/ip_entry_service.py verify_login_ip。
- */
- @Service
- public class LoginIpService {
- private final IpEntryMapper ipEntryMapper;
- public LoginIpService(IpEntryMapper ipEntryMapper) {
- this.ipEntryMapper = ipEntryMapper;
- }
- /**
- * 仅内置 admin 账号豁免(管理员类别不再整体豁免,避免普通机构管理员
- * 绕过本部门IP限制登录);机构未分配任何IP段仍放行。
- */
- public void verifyLoginIp(UserEntity user, HttpServletRequest request) {
- if (AdminPermission.isSuperAdmin(user) || user.getOrganizationId() == null) {
- return;
- }
- List<String> ipValues =
- ipEntryMapper.selectActiveIpValuesByOrganization(user.getOrganizationId());
- if (ipValues.isEmpty()) {
- return;
- }
- String rawIp = AuditService.clientIp(request);
- if (rawIp == null) {
- throw Errors.loginIpForbidden();
- }
- InetAddress address;
- try {
- address = InetAddress.getByName(rawIp.strip());
- } catch (UnknownHostException ex) {
- throw Errors.loginIpForbidden();
- }
- for (String value : ipValues) {
- if (matchesValue(address, value)) {
- return;
- }
- }
- throw Errors.loginIpForbidden();
- }
- /**
- * 对齐 _ip_matches:单条 ip_value(精确IP/CIDR网段/IP范围)是否命中来源地址;
- * 存量数据格式异常按不匹配处理(安全优先),不中断登录流程判断。
- * 包级私有便于单测。
- */
- static boolean matchesValue(InetAddress address, String value) {
- try {
- if (value.contains("/")) {
- String[] parts = value.split("/", 2);
- InetAddress network = InetAddress.getByName(parts[0]);
- int prefix = Integer.parseInt(parts[1]);
- return networkInetVersion(address) == networkInetVersion(network)
- && sameNetwork(address, network, prefix);
- }
- if (value.contains("-")) {
- String[] parts = value.split("-", -1);
- if (parts.length != 2 || parts[0].isEmpty() || parts[1].isEmpty()) {
- return false;
- }
- InetAddress start = InetAddress.getByName(parts[0]);
- InetAddress end = InetAddress.getByName(parts[1]);
- return networkInetVersion(start) == networkInetVersion(end)
- && networkInetVersion(address) == networkInetVersion(start)
- && IpAddresses.unsignedCompare(
- address.getAddress(), start.getAddress()) >= 0
- && IpAddresses.unsignedCompare(
- address.getAddress(), end.getAddress()) <= 0;
- }
- return address.equals(InetAddress.getByName(value));
- } catch (UnknownHostException | NumberFormatException ex) {
- return false;
- }
- }
- private static int networkInetVersion(InetAddress address) {
- return address.getAddress().length == 4 ? 4 : 6;
- }
- /** 前缀位匹配(地址族已由调用方保证一致)。 */
- private static boolean sameNetwork(InetAddress address, InetAddress network, int prefixLength) {
- byte[] addressBytes = address.getAddress();
- byte[] networkBytes = network.getAddress();
- int totalBits = addressBytes.length * 8;
- if (prefixLength < 0 || prefixLength > totalBits) {
- return false;
- }
- int fullBytes = prefixLength / 8;
- for (int i = 0; i < fullBytes; i++) {
- if (addressBytes[i] != networkBytes[i]) {
- return false;
- }
- }
- int remainderBits = prefixLength % 8;
- if (remainderBits == 0) {
- return true;
- }
- int mask = 0xFF << (8 - remainderBits);
- return (addressBytes[fullBytes] & mask) == (networkBytes[fullBytes] & mask);
- }
- }
|