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

相关文章
|
3月前
|
安全 Java 数据库
SpringSecurity实现多种登录方式,如邮件验证码、电话号码登录
SpringSecurity实现多种登录方式,如邮件验证码、电话号码登录
139 1
|
3月前
|
API 数据安全/隐私保护
SpringSecurity结合JwtToken验证(后端部分)
SpringSecurity结合JwtToken验证(后端部分)
28 0
SpringSecurity结合JwtToken验证(后端部分)
|
7月前
|
NoSQL Redis
使用黑名单完成,Jwt退出登录操作
使用黑名单完成,Jwt退出登录操作
465 0
|
8月前
|
JSON 安全 搜索推荐
​SpringSecurity-5-自定义登录验证
​SpringSecurity-5-自定义登录验证
96 0
|
8月前
|
安全 搜索推荐 Java
SpringSecurity-8-自动登录和注销功能实现
当我们在登录某一个网站的时候,我们会使用账号密码进行登录,但是我们不想每一次关闭浏览器,我们不想每一次重新输入账号和密码,贴合用户的登录体验,给用户带来方便,我们使用自动登录功能,将登录信息保存在浏览器的cookie中,这样用户在下次访问的时候,自动实现校验并新建登录状态。但是这样也会给用户带来信息泄露的危险。
92 0
|
8月前
|
缓存 API 数据库
一行代码教你实现登录鉴权
一行代码教你实现登录鉴权
89 0
|
安全 Java API
原来SpringSecurity整合OAuth2后开放权限拦截路径还能这么玩?
当我们整合了`Spring Security`以及`OAuth2`后发现,有一些业务请求是需要开放的,因为种种原因这时访问者还没有身份标识(`比如:用户刚来,还没有注册,需要进行新用户注册,这时注册业务相关的接口都应该是开放的`),下面我们来看看`ApiBoot`是怎么排除路径不进行权限拦截的。
|
安全 Java 数据安全/隐私保护
springsecurity默认用户生成
springsecurity默认用户生成
290 0
|
数据安全/隐私保护 数据库
SpringSecurity 密码验证流程
一、通过debug源码,解析,更加清楚的了解核心的原理,出现问题可以更好的解决 二、密码验证流程 1 默认使用 UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter 执行方法: pu.
6814 1
|
测试技术
Confluence 6 为登录失败配置使用验证码
如果你具有 Confluence 管理员的权限,你可以限制 Confluence 登录失败的最大尝试次数。在给予最大登录失败尝试(默认为 3 次)次数后,Confluence 将会在用户进行再次尝试的时候显示验证码输入框。
1234 0