13

我需要找到那些登录我们的应用程序的用户。
我们正在使用 Spring Security,并且必须有一种方法可以找出用户的 IP。

我认为这些信息都存储在他们的会话中。在 Spring Security 中,当前会话存储在SessionRegistry中。从这个类中,我可以获得经过身份验证的用户列表和一些会话信息。(使用getAllPrincipals,getAllSessionsgetSessionInformation

问题是,我怎样才能访问当前用户的 IP?考虑我们必须只为已知区域提供服务。SessionInformation
没有太大帮助 ,因为它不包含太多信息。

4

3 回答 3

19

我认为可以通过使用 hasIpAddress http 表达式来实现检查

请参阅第15.2 节 Web 安全表达式

<http use-expressions="true">
    <intercept-url pattern="/admin*"
        access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/>
    ...
  </http>

如果您想要更大的灵活性,您可以实现自己的 IP 地址检查服务,基于 IpAddressMatcher:

<bean id="ipCheckService" class="my.IpCheckService">
</bean>

<security:http auto-config="false" access-denied-page="/accessDenied.jsp" 
use-expressions="true">
    <security:intercept-url pattern="/login.jsp"
        access="@ipCheckService.isValid(request)" />

豆实现:

public class IpCheckService {
    public boolean isValid(HttpServletRequest request) {
        //This  service is a bean so you can inject other dependencies,
            //for example load the white list of IPs from the database 
        IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.0/24");
        
    try {
        return matcher.matches(request);
    } catch (UnsupportedOperationException e) { 
        return false;
    }
    }
}

更新:您可以尝试通过这种方式获取当前用户 IP:

    public static String getRequestRemoteAddr(){
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes())
                   .getRequest(); 
        return request.getRemoteAddr();
}

更新有关 IP 地址和会话之间关系的信息只能从不同的来源收集(例如侦听 AuthenticationSuccessEvent 和 SessionDestroyedEvent 事件,实现过滤器或使用 AOP 拦截器)。Spring Security 不存储此类信息,因为它没有用,因为 IP 地址仅在服务器处理ServletRequest时才有意义。

IP 地址可能会更改(用户可能正在使用代理),因此我们只能审核不同类型的事件,例如使用某些凭据登录、从不同 IP 访问服务或进行一些可疑活动。

于 2012-08-12T08:25:54.423 回答
9

您可以从WebAuthenticationDetails对象中获取 IP 地址,该对象可以从Authentication实例中获取。

Object details =
    SecurityContextHolder.getContext().getAuthentication().getDetails();
if (details instanceof WebAuthenticationDetails)
    ipAddress = ((WebAuthenticationDetails) details).getRemoteAddress();
于 2016-10-19T08:44:32.520 回答
6

您可以使用 HttpServletRequest 获取用户的 IP 地址。(SpringSecurity 的开发人员在他们的表达式hasIpAddress(...)中以相同的方式执行此操作,该表达式放置在WebSecurityExpressionRoot类中)。

例如,您可以通过 2 种方式获取 HttpServletRequest:

1)使用RequestContextHolder:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getRequest();

2)使用自动装配:

@Autowired
private HttpServletRequest request;

我从这里拿了这个。

然后使用HttpServletRequest您可以通过以下方式获取 IP 地址:

String address = request.getRemoteAddr();

这里是如何在 Spring Security 中比较地址的:

/**
 * Takes a specific IP address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or 202.24.0.0/14).
 *
 * @param ipAddress the address or range of addresses from which the request must come.
 * @return true if the IP address of the current request is in the required range.
 */
public boolean hasIpAddress(String ipAddress) {
    return (new IpAddressMatcher(ipAddress).matches(request));
}

和 IpAddressMatcher 类:

public final class IpAddressMatcher implements RequestMatcher {
    private final int nMaskBits;
    private final InetAddress requiredAddress;

    /**
     * Takes a specific IP address or a range specified using the
     * IP/Netmask (e.g. 192.168.1.0/24 or 202.24.0.0/14).
     *
     * @param ipAddress the address or range of addresses from which the request must come.
     */
    public IpAddressMatcher(String ipAddress) {

        if (ipAddress.indexOf('/') > 0) {
            String[] addressAndMask = StringUtils.split(ipAddress, "/");
            ipAddress = addressAndMask[0];
            nMaskBits = Integer.parseInt(addressAndMask[1]);
        } else {
            nMaskBits = -1;
        }
        requiredAddress = parseAddress(ipAddress);
    }

    public boolean matches(HttpServletRequest request) {
        return matches(request.getRemoteAddr());
    }

    public boolean matches(String address) {
        InetAddress remoteAddress = parseAddress(address);

        if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
            return false;
        }

        if (nMaskBits < 0) {
            return remoteAddress.equals(requiredAddress);
        }

        byte[] remAddr = remoteAddress.getAddress();
        byte[] reqAddr = requiredAddress.getAddress();

        int oddBits = nMaskBits % 8;
        int nMaskBytes = nMaskBits/8 + (oddBits == 0 ? 0 : 1);
        byte[] mask = new byte[nMaskBytes];

        Arrays.fill(mask, 0, oddBits == 0 ? mask.length : mask.length - 1, (byte)0xFF);

        if (oddBits != 0) {
            int finalByte = (1 << oddBits) - 1;
            finalByte <<= 8-oddBits;
            mask[mask.length - 1] = (byte) finalByte;
        }

 //       System.out.println("Mask is " + new sun.misc.HexDumpEncoder().encode(mask));

        for (int i=0; i < mask.length; i++) {
            if ((remAddr[i] & mask[i]) != (reqAddr[i] & mask[i])) {
                return false;
            }
        }

        return true;
    }

    private InetAddress parseAddress(String address) {
        try {
            return InetAddress.getByName(address);
        } catch (UnknownHostException e) {
            throw new IllegalArgumentException("Failed to parse address" + address, e);
        }
    }
}

编辑:

根据此处此处的相关问题,您可以使用自定义过滤器将用户的 IP 添加到会话中。然后从与用户相关的会话中获取此信息,这将是必要的。例如,您可以像这样放置用户的 IP 信息:

public class MonitoringFilter extends GenericFilterBean{
@Override
public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;

    String userIp = httpRequest.getRemoteAddr();

    httpRequest.getSession().setAttribute("userIp", userIp);

    // Add other attributes to session if necessary
}
于 2012-08-12T08:29:20.507 回答