IP地址获取
NG配置
所有经过的NG链路都需要配置以下内容。原因是因为
location / {
proxy_pass http://yun.baidu.com/baiduyunapp;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
JAVA代码获取IP
- 获取入口。
@Component
@Slf4j
public class HttpInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
RequestContextParam requestContextParam = new RequestContextParam();
requestContextParam.setIpAddr(IpUtils.getIpAddr(request));
RequestContextUtils.setContext(requestContextParam);
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
RequestContextUtils.removeContext();
}
}
- 实际获取类IpUtils。
public class IpUtils {
/**
* 获取访问用户的客户端IP(适用于公网与局域网).
*/
public static final String getIpAddr(final HttpServletRequest request)
throws Exception {
if (request == null) {
throw (new Exception("getIpAddr method HttpServletRequest Object is null"));
}
String ipString = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ipString) || "unknown".equalsIgnoreCase(ipString)) {
ipString = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ipString) || "unknown".equalsIgnoreCase(ipString)) {
ipString = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ipString) || "unknown".equalsIgnoreCase(ipString)) {
ipString = request.getRemoteAddr();
}
// 多个路由时,取第一个非unknown的ip
final String[] arr = ipString.split(",");
for (final String str : arr) {
if (!"unknown".equalsIgnoreCase(str)) {
ipString = str;
break;
}
}
return ipString;
}
public static boolean internalIp(String ip) {
byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip);
}
private static boolean internalIp(byte[] addr) {
if (StringUtils.isNull(addr) || addr.length < 2) {
return true;
}
final byte b0 = addr[0];
final byte b1 = addr[1];
// 10.x.x.x/8
final byte SECTION_1 = 0x0A;
// 172.16.x.x/12
final byte SECTION_2 = (byte) 0xAC;
final byte SECTION_3 = (byte) 0x10;
final byte SECTION_4 = (byte) 0x1F;
// 192.168.x.x/16
final byte SECTION_5 = (byte) 0xC0;
final byte SECTION_6 = (byte) 0xA8;
switch (b0) {
case SECTION_1:
return true;
case SECTION_2:
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
return true;
}
case SECTION_5:
switch (b1) {
case SECTION_6:
return true;
}
default:
return false;
}
}
/**
* 将IPv4地址转换成字节
*
* @param text IPv4地址
* @return byte 字节
*/
public static byte[] textToNumericFormatV4(String text) {
if (text.length() == 0) {
return null;
}
byte[] bytes = new byte[4];
String[] elements = text.split("\\.", -1);
try {
long l;
int i;
switch (elements.length) {
case 1:
l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L))
return null;
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 2:
l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L))
return null;
bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L))
return null;
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 3:
for (i = 0; i < 2; ++i) {
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L))
return null;
bytes[i] = (byte) (int) (l & 0xFF);
}
l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L))
return null;
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 4:
for (i = 0; i < 4; ++i) {
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L))
return null;
bytes[i] = (byte) (int) (l & 0xFF);
}
break;
default:
return null;
}
} catch (NumberFormatException e) {
return null;
}
return bytes;
}
public static String getHostIp() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
}
return "127.0.0.1";
}
public static String getHostName() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
}
return "未知";
}
}
- 其他辅助类。
public class RequestContextUtils {
private static final ThreadLocal<RequestContextParam> context = new ThreadLocal<>();
public static void setContext(RequestContextParam contextParam) {
context.set(contextParam);
}
public static RequestContextParam getContext() {
return context.get();
}
public static void removeContext() {
context.remove();
}
}
微信公众号GPS获取及坐标系转换
通过微信公众号获取GPS坐标,如果要计算该坐标与某个位置的距离。需要做以下操作。
- 将某个位置通过百度GPS解析成经纬度。
- 将公众号GPS坐标转换为百度坐标系。
- 只有保证以上两点在同一个坐标系才能计算距离。
相关类及参数如下。
百度地图公共类
@Component("baiduMapUtils")
public class BaiduMapUtils {
@Value("${baiduMaps.url}")
private String url;
@Value("${baiduMaps.ak}")
private String ak;
/**
* 逆地理编码
*/
public static final String REVERSE_GEOCODING = "reverse_geocoding/v3/";
/**
* 地理编码
*/
public static final String GEOCODING = "geocoding/v3/";
/**
* 坐标转换
*/
public static final String GEOCONV = "geoconv/v1/";
/**
* IP地理位置解析
*/
public static final String IP_LOCATION = "location/ip";
/**
* 根据详细地址获取百度坐标点
* 地址详细至 省 市 区/县 街道 门牌号
* @param address 地址
* @return { lng: 经度, lat: 纬度 }
*/
public JSONObject getCoordinate(String address) throws InterfaceServiceException {
StringBuilder sb = new StringBuilder();
sb.append("ak=");
sb.append(ak);
sb.append("&address=");
sb.append(address);
sb.append("&output=json");
JSONObject jsonObject = JSONObject.parseObject(HttpUtils.sendGet(url.concat(GEOCODING), sb.toString()));
if (jsonObject.getInteger("status") != 0) {
throw new InterfaceServiceException(RetCode.SYSTEM_ERROR.getCode(), "获取百度坐标点失败");
}
return jsonObject.getJSONObject("result").getJSONObject("location");
}
/**
* 根据百度坐标 获取详细地址
* @param lng 经度
* @param lat 纬度
* @return 地址详细至 省 市 区/县 街道 门牌号
*/
public String getAddress(double lng, double lat) throws InterfaceServiceException {
StringBuilder sb = new StringBuilder();
sb.append("ak=");
sb.append(ak);
sb.append("&output=json");
sb.append("&location=");
sb.append(lat);
sb.append(",");
sb.append(lng);
JSONObject jsonObject = JSONObject.parseObject(HttpUtils.sendGet(url.concat(REVERSE_GEOCODING), sb.toString()));
if (jsonObject.getInteger("status") != 0) {
throw new InterfaceServiceException(RetCode.SYSTEM_ERROR.getCode(), "百度坐标获取详细地址失败");
}
return jsonObject.getJSONObject("result").getString("formatted_address");
}
/**
* 其他坐标转换为百度坐标
* @param lng 经度
* @param lat 纬度
* @param type
* 默认 1:GPS设备获取的角度坐标,WGS84坐标;
* 2:GPS获取的米制坐标、sogou地图所用坐标;
* 3:google地图、soso地图、aliyun地图、mapabc地图和amap地图所用坐标,国测局(GCJ02)坐标;
* 4:3中列表地图坐标对应的米制坐标;
* 5:百度地图采用的经纬度坐标;
* 6:百度地图采用的米制坐标;
* 7:mapbar地图坐标;
* 8:51地图坐标
* @return { lng: 经度, lat: 纬度 }
*/
public JSONObject changeCoordinate(double lng, double lat, Integer type) throws InterfaceServiceException {
StringBuilder sb = new StringBuilder();
sb.append("ak=");
sb.append(ak);
sb.append("&coords=");
sb.append(lng);
sb.append(",");
sb.append(lat);
if (StringUtils.isNotEmpty(type)) {
sb.append("&from");
sb.append(type);
}
JSONObject jsonObject = JSONObject.parseObject(HttpUtils.sendGet(url.concat(GEOCONV), sb.toString()));
if (jsonObject.getInteger("status") != 0) {
throw new InterfaceServiceException(RetCode.SYSTEM_ERROR.getCode(), "其他坐标转换为百度坐标失败");
}
// 取第一个匹配到的坐标点
JSONObject result = jsonObject.getJSONArray("result").getJSONObject(0);
JSONObject data = new JSONObject();
data.put("lng", result.get("x"));
data.put("lat", result.get("y"));
return data;
}
/**
* 两坐标直线距离计算 Sphere坐标系计算
* @param lngFrom 起点经度
* @param latFrom 起点纬度
* @param lngTo 终点经度
* @param latTo 终点经度
* @return double 两坐标直线距离(单位:m) 误差 0.1m
*/
public double getDistance(double lngFrom, double latFrom, double lngTo, double latTo) {
GlobalCoordinates source = new GlobalCoordinates(latFrom, lngFrom);
GlobalCoordinates target = new GlobalCoordinates(latTo, lngTo);
return new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.Sphere, source, target).getEllipsoidalDistance();
}
/**
* 根据百度坐标 获取详细地址
* @param lng 经度
* @param lat 纬度
* @return 地址详细至 省 市 区/县 街道 门牌号
*/
public JSONObject getAddressDivision(double lng, double lat) throws InterfaceServiceException {
StringBuilder sb = new StringBuilder();
sb.append("ak=");
sb.append(ak);
sb.append("&output=json");
sb.append("&location=");
sb.append(lat);
sb.append(",");
sb.append(lng);
JSONObject jsonObject = JSONObject.parseObject(HttpUtils.sendGet(url.concat(REVERSE_GEOCODING), sb.toString()));
if (jsonObject.getInteger("status") != 0) {
throw new InterfaceServiceException(RetCode.SYSTEM_ERROR.getCode(), "百度坐标获取详细地址失败");
}
return jsonObject.getJSONObject("result").getJSONObject("addressComponent");
}
/**
* 通过ip地址获取地址信息
* @param ip ip地址
* @return
* {
* address: "北京市", #简要地址信息
* address_detail: #结构化地址信息
* {
* city: "北京市", #城市
* city_code: 131, #百度城市代码
* province: "北京市", #省份
* },
* point: #当前城市中心点
* {
* x: "116.39564504", #当前城市中心点经度
* y: "39.92998578" #当前城市中心点纬度
* }
* }
* @throws InterfaceServiceException
*/
public JSONObject getIPLocation(String ip) throws InterfaceServiceException {
StringBuilder sb = new StringBuilder();
sb.append("ak=");
sb.append(ak);
sb.append("&ip=");
sb.append(ip);
sb.append("&coor=bd09ll");
JSONObject jsonObject = JSONObject.parseObject(HttpUtils.sendGet(url.concat(IP_LOCATION), sb.toString()));
if (jsonObject.getInteger("status") != 0) {
throw new InterfaceServiceException(RetCode.SYSTEM_ERROR.getCode(), "获取百度IP解析失败");
}
return jsonObject.getJSONObject("content");
}
}
使用方式
private void getGpsDistance(String applyNo, double longitude, double latitude, Xd ext) throws Exception {
try {
JSONObject addr = baiduMapUtils.changeCoordinate(longitude, latitude, 3);
log.info("转换为百度坐标:{}", addr.toJSONString());
if (addr != null) {
ext.setLatitude(new BigDecimal(addr.getString("lat")));
ext.setLongitude(new BigDecimal(addr.getString("lng")));
}
if (applyNo != null && ext.getLatitude() != null && ext.getLongitude() != null) {
XdBusi e = 从数据库获取;
BigDecimal bg = new BigDecimal(baiduMapUtil.getDistance(ext.getLongitude().doubleValue(),
ext.getLatitude().doubleValue(),
DoubleUtils.getValueNullToDouble(e.getLongitude()),
DoubleUtils.getValueNullToDouble(e.getLatitude())) + "").divide(new BigDecimal(1000));
double distanceStore = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
ext.setGpsApplyDistance(distanceStore + "");
XdMerchantStore store = 从数据库获取;
JSONObject coordinate = baiduMapUtil.getCoordinate(store.getOfficeDetailAddr());
String lng = coordinate.getString("lng");
String lat = coordinate.getString("lat");
BigDecimal bg2 = new BigDecimal(baiduMapUtils.getDistance(DoubleUtils.getValueNullToDouble(lng),
DoubleUtils.getValueNullToDouble(lat),
ext.getLongitude().doubleValue(),
ext.getLatitude().doubleValue()) + "").divide(new BigDecimal(1000));
double distanceM = bg2.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
ext.setGpsStoreDistance(distanceM + "");
}
JSONObject addrJsonObject = baiduMapUtils.getAddressDivision(ext.getLongitude().doubleValue(),
ext.getLatitude().doubleValue());
String province = addrJsonObject.getString("province");
String city = addrJsonObject.getString("city");
ext.setGpsProvince(province);
ext.setGpsCity(city);
} catch (Exception e) {
log.error("获取替换gps信息异常", e);
}
}