登录访问时获取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,查询用户数据直接比较即可。

相关文章
|
应用服务中间件 Linux nginx
在Linux中,如何统计ip访问情况?分析 nginx 访问日志?如何找出访问页面数量在前十位的ip?
在Linux中,如何统计ip访问情况?分析 nginx 访问日志?如何找出访问页面数量在前十位的ip?
|
Prometheus Cloud Native 调度
Sentinel 新版本发布,提升配置灵活性以及可观测配套
Sentinel 新版本发布,提升配置灵活性以及可观测配套
1739 105
|
SQL 安全 关系型数据库
AWVS扫描在线站点实现安全评估
AWVS扫描在线站点实现安全评估
|
消息中间件 监控 Java
解锁Spring Cloud微服务架构的奥秘:深度剖析拆分原则,打造高内聚低耦合的业务创新引擎!
【8月更文挑战第3天】踏入微服务领域,Spring Cloud以丰富组件助力高效系统构建。微服务拆分需遵循原则确保系统高内聚低耦合且能适应变化。首要原则为单一职责,每个服务专注一个业务功能,降低复杂度并提高可维护性。其次,追求高内聚低耦合以减少服务间影响。围绕业务域拆分有助于保持逻辑清晰及团队协作。处理数据一致性问题时,考虑采用最终一致性模型。Spring Cloud提供Eureka、Zuul/Gateway、Sleuth和Config等工具支持服务发现、路由、跟踪及配置管理,共同构建灵活健壮的微服务架构。
423 2
|
存储 Kubernetes 数据安全/隐私保护
kubernetes 中pv的回收策略
在Kubernetes中,持久卷(Persistent Volume,PV)的回收策略可以通过`persistentVolumeReclaimPolicy`字段来定义。这个字段有以下几个可选值:1. `Retain`:保留持久卷,不进行自动回收。当持久卷使用完成后,需要手动进行清理和释放。2. `Delete`:删除持久卷,当持久卷不再被使用时,Kubernetes会自动删除并释放它。3. `Recycle`:回收持久卷,当持久卷不再被使用时,Kubernetes会自动进行回收操作。这种回收策略主要适用于一些旧的存储后端,它会尝试清空持久卷中的数据,但不会保证数据安全。需要注意的是,`Recy
890 0
|
机器学习/深度学习 人工智能 自然语言处理
使用 Java 进行自然语言处理(NLP)
【4月更文挑战第19天】自然语言处理(NLP)让计算机理解人类语言,Java作为广泛应用的编程语言,在NLP中扮演重要角色。NLP应用于智能客服、机器翻译、情感分析和文本分类等领域。Java因丰富库支持、高性能和跨平台性成为NLP开发的理想选择。关键技術包括词法分析、句法分析、语义理解和文本向量化。Stanford NLP和OpenNLP是Java中的知名NLP库。开发流程涉及数据准备、选择库、特征工程、模型训练、评估和部署。随着NLP技术进步,Java在该领域将持续发挥影响力。
1593 2
|
SQL 监控 关系型数据库
深入解析MySQL死锁:原因、检测与解决方案
深入解析MySQL死锁:原因、检测与解决方案
|
测试技术 API 数据处理
深入解析 Laravel 中的集合(Collections)
【8月更文挑战第31天】
575 0
|
网络架构
Ensp DHCP 接口地址池(配置命令)
Ensp DHCP 接口地址池(配置命令)
624 1
|
存储 Ubuntu 编译器
C与汇编混合编程
C与汇编混合编程
334 0

热门文章

最新文章