登录访问时获取IP并校验(Springsecurity )

简介: 因公司要求,针对项目进行ip限制,以往只是记录登录ip。所以此功能相对简单。

一、获取IP
获取IP有两种方式:1、自定义ip工具类。2、security获取。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

import javax.servlet.http.HttpServletRequest;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;

public class IPUtils {

    private static final String UNKNOWN = "unknown";

    protected IPUtils() {

    }

    /**
     * 获取IP地址 使用 Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非
     * unknown的有效IP字符串,则为真实IP地址
     */
    public static String getClientIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");

        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // log.info("获取客户端ip: " + ip);
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }
    /**
     * 判断是否为内网
     * @param ipAddress
     * @return
     */
    public static boolean isInnerIP(String ipAddress) {
        boolean isInnerIp = false;
        if (ipAddress.indexOf(",") != -1) {
            ipAddress = ipAddress.split(",")[0];
        }
        ipAddress = ipAddress.replaceAll(",", ".").replaceAll(" ", "");
        long ipNum = getIpNum(ipAddress);
        /**
         * 私有IP:
         * A类 10.0.0.0-10.255.255.255 
         * B类  172.16.0.0-172.31.255.255 
         * C类 192.168.0.0-192.168.255.255
            当然,还有127这个网段是环回地址
         **/
        long aBegin = getIpNum("10.0.0.0");
        long aEnd = getIpNum("10.255.255.255");
        long bBegin = getIpNum("172.16.0.0");
        long bEnd = getIpNum("172.31.255.255");
        long cBegin = getIpNum("192.168.0.0");
        long cEnd = getIpNum("192.168.255.255");
        isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd)
                || ipAddress.equals("127.0.0.1");
        return isInnerIp;
    }

    private static long getIpNum(String ipAddress) {
        String[] ip = ipAddress.split("\\.");
        long a = Integer.parseInt(ip[0]);
        long b = Integer.parseInt(ip[1]);
        long c = Integer.parseInt(ip[2]);
        long d = Integer.parseInt(ip[3]);

        long ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
        return ipNum;
    }

    private static boolean isInner(long userIp, long begin, long end) {
        return (userIp >= begin) && (userIp <= end);
    }
    /**
     * 此方法调用百度AIP来查询IP所在地域(YYR)
     * @param strIP(传入的IP地址)
     * @return
     */
    public static String getAddressByIP(String strIP) {
        try {
            URL url = new URL("http://api.map.baidu.com/location/ip?ak=F454f8a5efe5e577997931cc01de3974&ip="+strIP);
            URLConnection conn = url.openConnection();
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line = null;
            StringBuffer result = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
            reader.close();
            String ipAddr = result.toString();
            try {
                JSONObject obj1= JSON.parseObject(ipAddr);
                if("0".equals(obj1.get("status").toString())){
                    JSONObject obj2= JSON.parseObject(obj1.get("content").toString());
                    JSONObject obj3= JSON.parseObject(obj2.get("address_detail").toString());
                    return obj3.get("province").toString()+obj3.get("city").toString();
                }else{
                    return "读取失败";
                }
            } catch (JSONException e) {
                e.printStackTrace();
                return "读取失败";
            }

        } catch (IOException e) {
            return "读取失败";
        }
    }
    
    public static String getAddress(String ip) {
        String loginAddress = null;
        try {
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
            
            if("127.0.0.1".equals(ip) || isInnerIP(ip)) {
                    loginAddress = "局域网";
            }else{
                loginAddress = getAddressByIP(ip);
                if("读取失败".equals(loginAddress)) {
                    loginAddress = null;
                }
            }
            return loginAddress;
        } catch (Exception e) {
            return null;
        }
        
    }
}

在自定义的登录部分MyAuthenticationProvider 直接使用以下代码即可。

 WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails();
 String userIp = details.getRemoteAddress();

二、过滤IP
可以直接在Security的配置类中直接限制,指定ip时使用X.X.X.X ,若是ip段可是采用X.X.X.X/Y

//多个
http.authorizeRequests() 
     .antMatchers("/api/**", "/info") 
     .access("hasIpAddress('X.X.X.X') or hasIpAddress('Y.Y.Y.Y')") 
//单个
http.authorizeRequests()
   .antMatchers("/api/**", "/info").hasIpAddress("127.0.0.0/16")

自定义校验时,对访问ip的限制可以配置ip段,

public static boolean isInRange(String ip, String cidr) {
        String[] ips = ip.split("\\.");

        int ipAddr = (Integer.parseInt(ips[0]) << 24) | (Integer.parseInt(ips[1]) << 16) | (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]);
        int type = Integer.parseInt(cidr.replaceAll(".*/", ""));
        int mask = 0xFFFFFFFF << (32 - type);

        String cidrIp = cidr.replaceAll("/.*", "");
        String[] cidrIps = cidrIp.split("\\.");

        int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24) | (Integer.parseInt(cidrIps[1]) << 16) | (Integer.parseInt(cidrIps[2]) << 8) | Integer.parseInt(cidrIps[3]);

        return (ipAddr & mask) == (cidrIpAddr & mask);
    }

在数据库或其他地方保存指定的多个ip,使用lamda或者for处理。

List<String> list = new ArrayList<String>();
list.add("127.0.0.1");
list.add("127.0.0.2");
list.add("127.0.0.4");

String ip = "127.0.0.6";
boolean bString =  list.stream().anyMatch(p -> p.equals(ip));

也可以针对用指定固定ip,查询用户数据直接比较即可。

相关文章
|
存储 关系型数据库 MySQL
MySQL数据库的跨库查询和联合查询技巧
MySQL数据库的跨库查询和联合查询技巧
|
11月前
|
缓存 安全 Java
Java并发性能优化|读写锁与互斥锁解析
本文深入解析Java中两种核心锁机制——互斥锁与读写锁,通过概念对比、代码示例及性能测试,揭示其适用场景。互斥锁适用于写多或强一致性场景,读写锁则在读多写少时显著提升并发性能。结合锁降级、公平模式等高级特性,助你编写高效稳定的并发程序。
479 0
|
10月前
|
安全 Java 数据安全/隐私保护
Spring Security 核心技术解析与实践指南
本文档深入探讨 Spring Security 框架的核心架构、关键组件和实际应用。作为 Spring 生态系统中负责安全认证与授权的关键组件,Spring Security 为 Java 应用程序提供了全面的安全服务。本文将系统介绍其认证机制、授权模型、过滤器链原理、OAuth2 集成以及最佳实践,帮助开发者构建安全可靠的企业级应用。
561 0
|
算法 Go
【LeetCode 热题100】23:合并 K 个升序链表(详细解析)(Go语言版)
本文详细解析了 LeetCode 热题 23——合并 K 个升序链表的两种解法:优先队列(最小堆)和分治合并。题目要求将多个已排序链表合并为一个升序链表。最小堆方法通过维护节点优先级快速选择最小值,;分治合并则采用归并思想两两合并链表。文章提供了 Go 语言实现代码,并对比分析两种方法的适用场景,帮助读者深入理解链表操作与算法设计。
525 10
|
应用服务中间件 Linux nginx
在Linux中,如何统计ip访问情况?分析 nginx 访问日志?如何找出访问页面数量在前十位的ip?
在Linux中,如何统计ip访问情况?分析 nginx 访问日志?如何找出访问页面数量在前十位的ip?
|
JavaScript
【接地气】真正一口气讲清楚了Vue事件修饰符.once、.prevent、.stop、.capture、.self、.passive、.exac、.native的作用
【接地气】真正一口气讲清楚了Vue事件修饰符.once、.prevent、.stop、.capture、.self、.passive、.exac、.native的作用
【接地气】真正一口气讲清楚了Vue事件修饰符.once、.prevent、.stop、.capture、.self、.passive、.exac、.native的作用
|
消息中间件 监控 Java
解锁Spring Cloud微服务架构的奥秘:深度剖析拆分原则,打造高内聚低耦合的业务创新引擎!
【8月更文挑战第3天】踏入微服务领域,Spring Cloud以丰富组件助力高效系统构建。微服务拆分需遵循原则确保系统高内聚低耦合且能适应变化。首要原则为单一职责,每个服务专注一个业务功能,降低复杂度并提高可维护性。其次,追求高内聚低耦合以减少服务间影响。围绕业务域拆分有助于保持逻辑清晰及团队协作。处理数据一致性问题时,考虑采用最终一致性模型。Spring Cloud提供Eureka、Zuul/Gateway、Sleuth和Config等工具支持服务发现、路由、跟踪及配置管理,共同构建灵活健壮的微服务架构。
515 2
|
SQL 监控 关系型数据库
深入解析MySQL死锁:原因、检测与解决方案
深入解析MySQL死锁:原因、检测与解决方案
|
网络架构
Ensp DHCP 接口地址池(配置命令)
Ensp DHCP 接口地址池(配置命令)
677 1
|
测试技术 API 数据处理
深入解析 Laravel 中的集合(Collections)
【8月更文挑战第31天】
628 0