比如我们现在有一个服务地址,但是我们有不想把这个地址让别人知道,所有我们可以生成一个假的地址给他们,然后这个地址通过nginx进入到我们处理代理服务的接口里面,先找到真实的地址,然后再转发
比如下面的信息,我们把代理地址给用户(后面可以给接收token的参数gctk(参数名)),可以根据这个token信息判断,是否有访问服务的权限
真实地址
http://www.rog.org/server/rest/services/aa/MapServer
代理后的地址
http://127.0.0.1/skmap/forward/services/3da4413391a4403b877e38241e752cff/server/rest/services/aa/MapServer?f=json&gctk=tokenValue
nginx配置
location /skmap/ {
if ($request_method = 'OPTIONS') {return 204;}
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers *;
proxy_pass http://127.0.0.1:55096/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
controller
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 资源访问权限验证接口
*
*/
@RestController
public class VisitAuthController {
@Autowired
private VisitAuthService visitAuthService;
@ResponseBody
@RequestMapping(value = "/forward/services/**", method = {RequestMethod.POST, RequestMethod.GET})
public void service(HttpServletRequest request, HttpServletResponse response) {
visitAuthService.proxyServices(request, response);
}
}
service
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
/**
* @author alex
* @since 2020/02/28
*/
@Service
public class VisitAuthService {
private static Logger LOG = LoggerFactory.getLogger(VisitAuthService.class);
private static final String TOKEN_KEY = "gctk";
public void proxyServices(HttpServletRequest request, HttpServletResponse response) {
VisitRequestModel visitRequestModel = new VisitRequestModel(request, "/forward/services");
String code = visitRequestModel.getCode();
String key = visitRequestModel.getKey();
String queryString = visitRequestModel.getQueryString();
Resource resourceDO = resourceRepository.getByCode(code);//根据code查询资源,获取真实url地址
if (resourceDO == null) {
String error = "No Resource";
returnResponse(response, error);
return;
}
/**
* 至此 这里没有贴判断权限的代码权限 访问原始地址获取数据 输出数据
*/
String queryStringDecode = queryString;
// 首先将代理请求中的token信息剔除
if (StringUtils.isNotBlank(queryStringDecode)) {
queryStringDecode = queryStringDecode.replaceFirst(TOKEN_KEY + "=" + visitRequestModel.getToken(), "");
}
// 获取真实地址
String originUrl = resourceDO.getOriginUrl();
if (StringUtils.isNotBlank(queryStringDecode)) {
originUrl += "?" + queryStringDecode;
}
ProxyHttpRequestUtil.ResponseInfo responseInfo = ProxyHttpRequestUtil.proxyRequest(request, originUrl, response);
visitRequestModel.setEndTime(System.currentTimeMillis());
if (responseInfo != null) {
//添加预览记录
}
}
/**
* 返回信息
*
* @param response 响应对象
* @param data 信息
*/
private void returnResponse(HttpServletResponse response, String data) {
try {
byte[] responseData = data.getBytes();
OutputStream outputStream = response.getOutputStream();
outputStream.write(responseData);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
VisitRequestModel
import cn.com.geoscene.micro.qx.utils.StringCommonUtils;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 资源请求对象model类
*
*/
@Data
public class VisitRequestModel {
private String method; //请求方法
private String scheme; //请求协议
private String host; //请求host
private String origin;
private String referer; //请求接口的页面url,即refer
private String token; //token信息
private String queryString; //query信息,及url中?后的信息
private String requestURI; //请求地址,例如:'/server/rest/*******/MapServer/0/query'
private String code; //代理标识,requestURI的第一层目录:'*******'
private Map<String, String> cookieMap; //请求cookie信息 暂未使用
private String key; //应用code
private long beginTime; //请求开始时间
private long endTime; //请求结束时间
private String type; //资源类型
public VisitRequestModel(HttpServletRequest request, String url) {
beginTime = System.currentTimeMillis();
method = request.getMethod();
host = request.getHeader("host");
scheme = request.getHeader("X-Scheme");
referer = request.getHeader("referer");
origin = request.getHeader("origin");
requestURI = StringCommonUtils.ChangeUTFToISO(request.getRequestURI());
String rUri = requestURI.replaceFirst(url, "");
String[] split = rUri.split("/");
code = split[1];
queryString = request.getQueryString();
if(StringUtils.isNotBlank(queryString)){
if(queryString.contains("key")){
String[] params = queryString.split("&");
String newParam = "";
for (String param : params) {
if(param.contains("key")){
key = param.replace("key=", "");
}else {
newParam = newParam + param + "&";
}
if(newParam.endsWith("&")){
newParam = newParam.substring(0, newParam.length() - 1);
}
queryString = newParam;
}
}
}
}
public long getRunningTime() {
return endTime - beginTime;
}
}
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
/**
* 字符工具类
**/
public class StringCommonUtils {
/**
* 将服务地址中url中文改成ASCII字符集
*
* @param serverUrlUTF utf-8字符串
* @return ascii字符串
*/
public static String ChangeUTFToISO(String serverUrlUTF) {
StringBuilder resultStr = new StringBuilder();
if (StringUtils.isBlank(serverUrlUTF)) return serverUrlUTF;
for (int i = 0; i < serverUrlUTF.length(); i++) {
char c = serverUrlUTF.charAt(i);
if (c <= 255) {
resultStr.append(c);
} else {
byte[] b;
try {
b = String.valueOf(c).getBytes(StandardCharsets.UTF_8);
} catch (Exception ex) {
System.out.println(ex);
b = new byte[0];
}
for (int value : b) {
int k = value;
if (k < 0)
k += 256;
resultStr.append("%").append(Integer.toHexString(k).toUpperCase());
}
}
}
return resultStr.toString();
}
}
import org.apache.commons.lang3.StringUtils;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.*;
/**
*/
public class ProxyHttpRequestUtil {
/**
* 代理请求
*
* @param request 请求信息
* @param proxyUrl 代理地址
* @param response 接收的答复
*/
public static ResponseInfo proxyRequest(HttpServletRequest request, String proxyUrl, HttpServletResponse response) {
ResponseInfo responseInfo = null;
try {
RequestInfo requestInfo = ReadRequestInfo(request);
HttpURLConnection httpURLConnection = CreateProxyRequestConnection(proxyUrl, requestInfo);
httpURLConnection.setConnectTimeout(60000);//设置超时时间1分钟
responseInfo = ReadResponseInfo(httpURLConnection);
WriteResponseInfo(responseInfo, response);
} catch (IOException e) {
e.printStackTrace();
}
return responseInfo;
}
/**
* 从HttpServletRequest中读取请求信息
*
* @param request HttpServletRequest
* @return RequestInfo 请求体对象
*/
public static RequestInfo ReadRequestInfo(HttpServletRequest request) throws IOException {
RequestInfo requestInfo = new RequestInfo();
String method = request.getMethod();
requestInfo.setMethod(method);
String requestUrl = request.getRequestURL().toString();
requestInfo.setRequestUrl(requestUrl);
String queryString = request.getQueryString();
requestInfo.setQueryString(queryString);
String characterEncoding = request.getCharacterEncoding();
requestInfo.setCharacterEncoding(characterEncoding);
Map<String, String> headMap = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headName = headerNames.nextElement();
String headerValue = request.getHeader(headName);
headMap.put(headName, headerValue);
}
requestInfo.setHeader(headMap);
int contentLength = request.getContentLength();
if (contentLength > 0) {
requestInfo.setContentLength(contentLength);
byte[] requestData = new byte[contentLength];
DataInputStream dataInputStream = new DataInputStream(request.getInputStream());
dataInputStream.readFully(requestData);
dataInputStream.close();
requestInfo.setRequestData(requestData);
}
return requestInfo;
}
/**
* 读取响应信息
*
* @param connection HttpURLConnection 请求连接
* @return ResponseInfo 响应体对象
*/
public static ResponseInfo ReadResponseInfo(HttpURLConnection connection) {
long begin = System.currentTimeMillis();
ResponseInfo responseInfo = new ResponseInfo();
if (connection != null) {
try {
int responseCode = connection.getResponseCode();//响应行
long end = System.currentTimeMillis() - begin;//本次请求用时
responseInfo.setWaitTime(end);
responseInfo.setResponseCode(responseCode);
Map<String, List<String>> headerFields = connection.getHeaderFields();//响应头
responseInfo.setHeader(headerFields);
InputStream byteStream;//响应体
if (responseCode >= 400 && connection.getErrorStream() != null) {
byteStream = connection.getErrorStream();
} else {
byteStream = connection.getInputStream();
}
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
final int length = 4096;
byte[] bytes = new byte[length];
int bytesRead;
while ((bytesRead = byteStream.read(bytes, 0, length)) > 0) {
buffer.write(bytes, 0, bytesRead);
}
buffer.flush();
byte[] responseData = buffer.toByteArray();
responseInfo.setResponseData(responseData);
byteStream.close();
} catch (SocketTimeoutException e) {
responseInfo.setResponseCode(408);
responseInfo.setWaitTime(-1);
System.out.println("请求超时");
}
catch (IOException e) {
responseInfo.setResponseCode(500);
responseInfo.setWaitTime(-1);
System.out.println("请求错误");
}
}
return responseInfo;
}
/**
* 创建代理请求链接
*
* @param url 请求地址,必须是完整的url,http[s]://domain[:port]/****
* @param requestInfo 请求信息对象
*/
public static HttpURLConnection CreateProxyRequestConnection(String url, RequestInfo requestInfo) throws IOException {
HttpURLConnection httpURLConnection = null;
if (StringUtils.isNotBlank(url)) {
if (url.toUpperCase().startsWith("HTTPS")) {
httpURLConnection = DoRequest(url, requestInfo.getMethod(), requestInfo.getHeader(),
requestInfo.getRequestData(), true);
} else {
httpURLConnection = DoRequest(url, requestInfo.getMethod(), requestInfo.getHeader(),
requestInfo.getRequestData(), false);
}
}
return httpURLConnection;
}
/**
* 将响应信息写入HttpServletResponse中
*
* @param responseInfo 响应信息
* @param response HttpServletResponse
*/
public static void WriteResponseInfo(ResponseInfo responseInfo, HttpServletResponse response) throws IOException {
if (null != responseInfo && null != responseInfo.getResponseCode()) {
Map<String, List<String>> headerFields = responseInfo.getHeader();
passHeadersInfo(response, headerFields);
int code = responseInfo.getResponseCode();
response.setStatus(code);
if (code > 0 && code < 400) {
byte[] responseData = responseInfo.getResponseData();
OutputStream outputStream = response.getOutputStream();
outputStream.write(responseData);
outputStream.close();
}
}
}
/**
* 创建HTTP请求链接
*
* @param urlStr 请求完整url地址 http[s]://Host/Path?QueryString
* @param method 请求方式
* @param head 请求头信息
* @param body 请求体信息
* @param security 安全协议,https->true
* @return
* @throws IOException
*/
private static HttpURLConnection DoRequest(String urlStr, String method, Map<String, String> head, byte[] body, boolean security) throws IOException {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// https
if (security) {
}
connection.setConnectTimeout(5000);
connection.setReadTimeout(10000);
connection.setRequestMethod(method);
passHeadersInfo(connection, head);
if (body != null && body.length > 0 || method.equals("POST")) {
if (body == null) {
body = new byte[0];
}
connection.setRequestMethod("POST");
connection.setDoOutput(true);
OutputStream os = connection.getOutputStream();
os.write(body);
os.close();
}
return connection;
}
/**
* 将Header信息 放到请求头中
*
* @param connection
* @param header
*/
private static void passHeadersInfo(HttpURLConnection connection, Map<String, String> header) {
if (null != header && header.size() > 0) {
for (String headerName : header.keySet()) {
String headerValue = header.get(headerName);
connection.setRequestProperty(headerName, headerValue);
}
}
}
/**
* 将Header信息 放到响应头中
*
* @param response
* @param headerFields
*/
private static void passHeadersInfo(HttpServletResponse response, Map<String, List<String>> headerFields) {
if (null == headerFields) {
return;
}
Set<String> headerFieldsSet = headerFields.keySet();
for (String headerFieldKey : headerFieldsSet) {
if (headerFieldKey != null && headerFieldKey.toLowerCase().equals("accept-ranges")) {
continue;
}
List<String> headerFieldValue = headerFields.get(headerFieldKey);
StringBuilder sb = new StringBuilder();
for (String value : headerFieldValue) {
if (headerFieldKey != null && headerFieldKey.toLowerCase().equals("content-type")) {
if (value != null && value.toLowerCase().contains("application/vnd.ogc.wms_xml")) {
value = "text/xml";
}
}
if (headerFieldKey != null && headerFieldKey.toLowerCase().equals("transfer-encoding")) {
if (value != null && value.toLowerCase().equals("chunked")) {
continue;
}
}
sb.append(value);
sb.append("");
}
if (headerFieldKey != null) {
response.addHeader(headerFieldKey, removeCRLF(sb.toString()));
}
}
}
/**
* 将ServletInputStream转ByteArrayInputStream 因为 ServletInputStream
* 被Tomcat做了优化,ServletInputStream.available()是得不到数据长度的,这样得到的就是一个字节数。
* 所以把ServletInputStream转换为ByteArrayInputStream拿到数据长度,否则后续使用该ServletInputStream可能为空
*
* @param sis
* @return
*/
public static byte[] convertServletInputStream(ServletInputStream sis) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int n = 0;
try {
while ((n = sis.read(buffer)) != -1) {
output.write(buffer, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
}
return output.toByteArray();
}
public static String removeCRLF(String inputLine) {
String filteredLine = inputLine;
if (hasCRLF(inputLine)) {
filteredLine = filteredLine.replace("\n", "").replace("\r", "");
}
return filteredLine;
}
public static String replaceCRLF(String inputLine, String replaceString) {
String filteredLine = inputLine;
if (hasCRLF(inputLine)) {
filteredLine = filteredLine.replace("\n", replaceString).replace("\r", replaceString);
}
return filteredLine;
}
public static boolean hasCRLF(String inputLine) {
return inputLine.contains("\n") || inputLine.contains("\r");
}
public static class RequestInfo {
private String method; //请求方法
private String requestUrl; //请求url
private String queryString; //参数
private String characterEncoding; //编码方式
private Integer contentLength; //请求内容长度
private byte[] requestData; //请求内容数据
private Map<String, String> header; //请求头信息
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getRequestUrl() {
return requestUrl;
}
public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
}
public String getQueryString() {
return queryString;
}
public void setQueryString(String queryString) {
this.queryString = queryString;
}
public String getCharacterEncoding() {
return characterEncoding;
}
public void setCharacterEncoding(String characterEncoding) {
this.characterEncoding = characterEncoding;
}
public Integer getContentLength() {
return contentLength;
}
public void setContentLength(Integer contentLength) {
this.contentLength = contentLength;
}
public byte[] getRequestData() {
return requestData;
}
public void setRequestData(byte[] requestData) {
this.requestData = requestData;
}
public Map<String, String> getHeader() {
return header;
}
public void setHeader(Map<String, String> header) {
this.header = header;
}
}
public static class ResponseInfo {
private Integer responseCode;
private Map<String, List<String>> header;
private byte[] responseData;
private String responseString;
private long waitTime;
public Integer getResponseCode() {
return responseCode;
}
public void setResponseCode(Integer responseCode) {
this.responseCode = responseCode;
}
public Map<String, List<String>> getHeader() {
return header;
}
public void setHeader(Map<String, List<String>> header) {
this.header = header;
}
public byte[] getResponseData() {
return responseData;
}
public void setResponseData(byte[] responseData) {
this.responseData = responseData;
}
public String getResponseString() {
return responseString;
}
public void setResponseString(String responseString) {
this.responseString = responseString;
}
public long getWaitTime() {
return waitTime;
}
public void setWaitTime(long waitTime) {
this.waitTime = waitTime;
}
}
}