Obtain the real IP address and the corresponding province and city through the Request request


title: Obtain the real IP address and the corresponding province city and system browser information through Request
date: 2022-12-16 16:20:26
tags:

  • GeoIP2
  • UserAgentUtils
    categories:
  • Development practice
    cover: https://cover.png
    feature: false

1. Obtain real IP address

1.1 Code

The code is as follows, here is the encapsulation method CommonUtil.isBlank()for judging empty space

public static String getIpAddress(HttpServletRequest request) {
    
    
        // 首先, 获取 X-Forwarded-For 中的 IP 地址,它在 HTTP 扩展协议中能表示真实的客户端 IP
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (CommonUtil.isNotBlank(ipAddress) && !"unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 多次反向代理后会有多个 ip 值,第一个 ip 才是真实 ip, 例: X-Forwarded-For: client, proxy1, proxy2,proxy…
            int index = ipAddress.indexOf(",");
            if (index != -1) {
    
    
                return ipAddress.substring(0, index);
            }

            return ipAddress;
        }

        // 如果 X-Forwarded-For 获取不到, 就去获取 X-Real-IP
        ipAddress = request.getHeader("X-Real-IP");
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 如果 X-Real-IP 获取不到, 就去获取 Proxy-Client-IP
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 如果 Proxy-Client-IP 获取不到, 就去获取 WL-Proxy-Client-IP
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 如果 WL-Proxy-Client-IP 获取不到, 就去获取 HTTP_CLIENT_IP
            ipAddress = request.getHeader("HTTP_CLIENT_IP");
        }
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 如果 HTTP_CLIENT_IP 获取不到, 就去获取 HTTP_X_FORWARDED_FOR
            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 都获取不到, 最后才通过 request.getRemoteAddr() 获取IP
            ipAddress = request.getRemoteAddr();
        }

        return "0:0:0:0:0:0:0:1".equals(ipAddress) ? "127.0.0.1" : ipAddress;;
}

1.2 Explanation

1. First, obtain the IP address at the 0th position X-Forwarded-Forin , which can represent the real client IP in the HTTP extension protocol, as in the following example:

X-Forwarded-For: client, proxy1, proxy2, proxy…

2. If you can’t X-Forwarded-Forget it , get it X-Real-IP, X-Real-IPif you can’t get it, get it in turn Proxy-Client-IP, WL-Proxy-Client-IP, HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR. Finally, if you can’t get it, request.getRemoteAddr()you can get

  1. X-Real-IP: Record the real IP of the client requesting, X-Forwarded-Forsimilar
  2. Proxy-Client-IP: The IP of the proxy client. If the real IP of the client cannot be obtained, only the IP of the proxy client can be obtained.
  3. WL-Proxy-Client-IP: The parameters used to obtain the real IP under Weblogic
  4. HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR: It can be understood that X-Forwarded-Forthey are usages in PHP

3. When request.getRemoteAddr()obtaining , what is obtained is 0:0:0:0:0:0:0:1 of IPV6, which needs to be converted to 127.0.0.1 of IPV4

1.3 Nginx configuration request header parameters

server {
        listen       8081;
        server_name  localhost;

        location / {
            root   html/resource-nav;
            index  index.html index.htm;
        }
  
        location ~ /resNav {
            #代理请求头相关
	    proxy_set_header Host $host:$server_port; 
	    proxy_set_header X-Real-Ip $remote_addr;
	    proxy_set_header X-Forwarded-For $remote_addr;
  
            proxy_pass http://ip:port;
        }
}

2. Get province and city information by IP address

Divided into two ways, online and offline:

1. Use the API provided by the online third party:

  • ip-api.com
  • ip.taotao.com
  • baidu map api
  • Sina iplookup

2. Use the offline query method:

  • Innocence library
  • GeoLite2
  • Evan Technology

The specific data richness, accuracy and query speed can be collected by yourself. Since GeoLite2 is free, and the offline query speed is faster and stable, and there is no limit to the number of API concurrency, etc., GeoLite2 is used here to obtain province and city information, and the data richness is also relatively high

2.1 Download GeoLite2 City library

The GeoLite database is owned by MaxMind. The GeoLite database has an open source version and a paid version. The open source version is used here. GeoLite has been updated to 2, so download the GeoLite2 City library. The download address is as follows: GeoLite2 Free Geolocation Data | MaxMind Developer Portal

Click Download Files on the page

insert image description here

If you are not logged in, you will be redirected to the login page

insert image description here

If you do not have an account, click Create

insert image description here

There will be several account forms here, choose to log in to the free GeoLite2 database and Web service

insert image description here

After filling in the corresponding information, an email to set the password will be sent, click the link to set the password

insert image description here

Click to log in when finished

insert image description here

Enter the user name and password to log in, the user name is the email address

insert image description here

select download database

insert image description here

Select GZIP download

insert image description here

After the download is complete, you will get a tar package file

insert image description here

After decompression, there is the database file we need (Windows can be decompressed with 7-Zip)

insert image description here

2.2 use

2.2.1 Put the file into the project root path

insert image description here

2.2.2 Introducing dependencies

It seems that version 3.0 or above supports JDK 11 at least, and if it is JDK 8, it can use 2.16.1 at most

<!-- GeoIP2 -->
<dependency>
        <groupId>com.maxmind.geoip2</groupId>
        <artifactId>geoip2</artifactId>
        <version>2.16.1</version>
</dependency>

2.2.3 Code

Here new DatabaseReader.Builder(database).build()supports two types, one is File and the other is InputStream. Both are available for local projects, but there may be problems obtaining File type files when they are packaged and run on the server. It is best to obtain the build through streaming

public class Test {
    
    
    public static void main(String[] args) throws IOException, GeoIp2Exception {
    
    
        // 读取数据库文件
        ClassPathResource classPathResource = new ClassPathResource("GeoLite2-City.mmdb");
        InputStream database = database = classPathResource.getInputStream();
        // 创建数据库
        DatabaseReader reader = new DatabaseReader.Builder(database).build();
        // 获取 IP 地址信息
        InetAddress ipAddress = InetAddress.getByName("139.227.47.35");

        // 获取查询信息
        CityResponse response = reader.city(ipAddress);

        // 国家信息
        Country country = response.getCountry();
        System.out.println(country.getIsoCode()); // 'CN'
        System.out.println(country.getName()); // 'China'
        // {de=China, ru=Китай, pt-BR=China, ja=中国, en=China, fr=Chine, zh-CN=中国, es=China}
        System.out.println(country.getNames());
        System.out.println(country.getNames().get("zh-CN")); // '中国'

        // 省级信息
        Subdivision subdivision = response.getMostSpecificSubdivision();
        System.out.println(subdivision.getIsoCode()); // 'SH'
        System.out.println(subdivision.getName()); // 'Shanghai'
        // {
    
    {en=Shanghai, fr=Municipalité de Shanghai, zh-CN=上海, pt-BR=Xangai}}
        System.out.println(subdivision.getNames());
        System.out.println(subdivision.getNames().get("zh-CN")); // '上海'

        // 城市信息
        City city = response.getCity();
        System.out.println(city.getName()); // 'Shanghai'
        System.out.println(city.getNames().get("zh-CN")); // '上海'

        // 邮政编码(国内的可能获取不到)
        Postal postal = response.getPostal();
        System.out.println(postal.getCode()); // '55423'

        // 经纬度
        Location location = response.getLocation();
        System.out.println(location.getLatitude()); // 纬度 31.2222
        System.out.println(location.getLongitude()); // 经度 121.4581
    }
}

insert image description here

2.3 Encapsulated as a tool class

1. Entity class

@Data
@TableName("login_geo")
public class LoginGeoDO {
    
    

    // 主键ID
    private String id;

    // 国家 ISO 代码
    private String countryIsoCode;

    // 国家名称
    private String countryName;

    // 国家中文名称
    private String countryZhCnName;

    // 省级 ISO 代码, 外国则是同级别地区代码
    private String subdivisionIsoCode;

    // 省级名称
    private String subdivisionName;

    // 省级中文名称
    private String subdivisionZhCnName;

    // 城市名称
    private String cityName;

    // 城市中文名称
    private String cityZhCnName;

    // 邮政编码
    private String postal;

    // 纬度
    private double latitude;

    // 经度
    private double longitude;

    // 创建时间
    private Timestamp createTime;

    // 更新时间
    private Timestamp updateTime;
}

2. Packaging tools

The previous method of obtaining the IP address is also encapsulated here, and LogUtil is the encapsulated log tool class

public class AuthUtil {
    
    

    private static InputStream database;

    private static DatabaseReader reader;

    static {
    
    
        // 读取数据库文件
        LogUtil.info("读取数据库文件");
        ClassPathResource classPathResource = new ClassPathResource("GeoLite2-City.mmdb");
        // 创建数据库
        try {
    
    
            database = classPathResource.getInputStream();
            reader = new DatabaseReader.Builder(database).build();
        } catch (IOException e) {
    
    
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 获取 IP 地址
     *
     * @param request 请求
     * @return {@link String}
     * @author Fan
     * @since 2022/11/28 9:08
     */
    public static String getIpAddress(HttpServletRequest request) {
    
    
        // 首先, 获取 X-Forwarded-For 中的 IP 地址,它在 HTTP 扩展协议中能表示真实的客户端 IP
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (CommonUtil.isNotBlank(ipAddress) && !"unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 多次反向代理后会有多个 ip 值,第一个 ip 才是真实 ip, 例: X-Forwarded-For: client, proxy1, proxy2,proxy…
            int index = ipAddress.indexOf(",");
            if (index != -1) {
    
    
                return ipAddress.substring(0, index);
            }

            return ipAddress;
        }

        // 如果 X-Forwarded-For 获取不到, 就去获取 X-Real-IP
        ipAddress = request.getHeader("X-Real-IP");
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 如果 X-Real-IP 获取不到, 就去获取 Proxy-Client-IP
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 如果 Proxy-Client-IP 获取不到, 就去获取 WL-Proxy-Client-IP
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 如果 WL-Proxy-Client-IP 获取不到, 就去获取 HTTP_CLIENT_IP
            ipAddress = request.getHeader("HTTP_CLIENT_IP");
        }
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 如果 HTTP_CLIENT_IP 获取不到, 就去获取 HTTP_X_FORWARDED_FOR
            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (CommonUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
            // 都获取不到, 最后才通过 request.getRemoteAddr() 获取IP
            ipAddress = request.getRemoteAddr();
        }

        return ipAddress;
    }

    /**
     * 通过 IP 地址获取地理信息
     *
     * @param ipAddress IP地址
     * @return {@link LoginGeoDO}
     * @author Fan
     * @since 2022/12/14 16:35
     */
    public static LoginGeoDO getGeoInformation(String ipAddress) {
    
    
        try {
    
    
            // 获取 IP 地址信息
            InetAddress inetAddress = InetAddress.getByName(ipAddress);
            // 获取查询信息
            CityResponse response = reader.city(inetAddress);
            LoginGeoDO loginGeoDO = new LoginGeoDO();

            // 国家信息
            Country country = response.getCountry();
            loginGeoDO.setCountryIsoCode(country.getIsoCode());
            loginGeoDO.setCountryName(country.getName());
            loginGeoDO.setCountryZhCnName(country.getNames().get("zh-CN"));

            // 省级信息
            Subdivision subdivision = response.getMostSpecificSubdivision();
            loginGeoDO.setSubdivisionIsoCode(subdivision.getIsoCode());
            loginGeoDO.setSubdivisionName(subdivision.getName());
            loginGeoDO.setSubdivisionZhCnName(subdivision.getNames().get("zh-CN"));

            // 城市信息
            City city = response.getCity();
            loginGeoDO.setCityName(city.getName());
            loginGeoDO.setCityZhCnName(city.getNames().get("zh-CN"));

            // 邮政编码(国内的可能获取不到)
            Postal postal = response.getPostal();
            loginGeoDO.setPostal(postal.getCode());

            // 经纬度
            Location location = response.getLocation();
            loginGeoDO.setLatitude(location.getLatitude());
            loginGeoDO.setLongitude(location.getLongitude());

            return loginGeoDO;
        } catch (IOException | GeoIp2Exception exception) {
    
    
            LogUtil.error(exception.getMessage());
            return null;
        }
    }
}

3. Obtain system and browser information

This type of information is generally obtained through the UA (User Agent) identification. The Chinese name of User Agent is User Agent, or UA for short. It is a special string header that enables the server to identify the operating system and version, CPU type, browser and version, browser rendering engine, browser language, and browser version used by the client. plug-ins, etc.

First get the User-Agent in the request header

String ua = request.getHeader("User-Agent");

Introduce UserAgentUtils dependency

<!-- UserAgentUtils -->
<dependency>
    <groupId>eu.bitwalker</groupId>
    <artifactId>UserAgentUtils</artifactId>
    <version>1.21</version>
</dependency>

Use the provided UserAgent class to parse the ua string

UserAgent userAgent = UserAgent.parseUserAgentString(ua);

// 操作系统
userAgent.getOperatingSystem().getName()
// 浏览器
userAgent.getBrowser().getName()

Guess you like

Origin blog.csdn.net/ACE_U_005A/article/details/128312267