springboot部分转发
前言
本篇文章实现springboot在没有接口可以访问的时候,也就是正常来说服务器应该返回404的接口转发到其他的服务器上的功能
需要有一定的springboot基础
一、Interceptor
一想到转发,首先我想到的就是拦截器了,噼里啪啦就开始操作,代码类似如下:
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.aw.ccpos.util.HttpServletRequestReader;
import com.aw.ccpos.util.HttpUtils;
import com.aw.ccpos.util.SysUtil;
import com.aw.service.pos.base.model.RequestModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
public class InfoInterceptor implements HandlerInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(InfoInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;// 只有返回true才会继续向下执行,返回false取消当前请求
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
}
postHandle方法中的ModelAndView 可以自定义404的错误页面,HttpServletResponse 则是返回,但是尝试过后发现所有的接口都要这么搞一遍,意思是如果通过这个办法所有的接口都会被拦截转发,这并不是我们想要的
二、/**
左思右想过后采用了如下方法
import com.aw.ccpos.constants.Constants;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
@Api(value = "GraphDB", tags = {
"graphdb-Api"
})
public class GraphDBController {
@Autowired
private RoutingDelegate routingDelegate;
@RequestMapping(value = "/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE}, produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity catchAll(HttpServletRequest request, HttpServletResponse response) {
return routingDelegate.redirect(request, response, Constants.SERVER_URL, null);
}
}
既然我们只需要处理服务器找不到的方法,那么只需要/**然后包含我们页面的请求类型就好了,因为我代理转发的工具类需要MediaType.TEXT_PLAIN_VALUE类型,所以我这么设置,读者可以如不需要可以自己写转发,RoutingDelegate 转发工具类会在后文提供
1.RoutingDelegate
转发工具类 代码如下(示例):
import com.aw.ccpos.util.HttpsUtils;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.List;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
@Service
public class RoutingDelegate {
public ResponseEntity<String> redirect(HttpServletRequest request, HttpServletResponse response,String routeUrl, String prefix) {
try {
// build up the redirect URL
String redirectUrl = createRedictUrl(request,routeUrl, prefix);
RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
return route(requestEntity);
} catch (Exception e) {
return new ResponseEntity("REDIRECT ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private String createRedictUrl(HttpServletRequest request, String routeUrl, String prefix) {
String queryString = request.getQueryString();
return routeUrl + request.getRequestURI() +
(queryString != null ? "?" + queryString : "");
}
private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException {
String method = request.getMethod();
HttpMethod httpMethod = HttpMethod.resolve(method);
MultiValueMap<String, String> headers = parseRequestHeader(request);
byte[] body = parseRequestBody(request);
return new RequestEntity<>(body, headers, httpMethod, new URI(url));
}
private ResponseEntity<String> route(RequestEntity requestEntity) {
ResponseEntity<String> responseEntity;
responseEntity = getRestTemplate().exchange(requestEntity, String.class);
return responseEntity;
}
private byte[] parseRequestBody(HttpServletRequest request) throws IOException {
InputStream inputStream = request.getInputStream();
return StreamUtils.copyToByteArray(inputStream);
}
private MultiValueMap<String, String> parseRequestHeader(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
List<String> headerNames = Collections.list(request.getHeaderNames());
for (String headerName : headerNames) {
List<String> headerValues = Collections.list(request.getHeaders(headerName));
for (String headerValue : headerValues) {
headers.add(headerName, headerValue);
}
}
return headers;
}
public static RestTemplate getRestTemplate() {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManager[] keyManagers = HttpsUtils.prepareKeyManager(null, null);
TrustManager[] trustManagers = HttpsUtils.prepareTrustManager(null);
TrustManager trustManager = null;
if (trustManagers != null) {
trustManager = new HttpsUtils.MyTrustManager( HttpsUtils.chooseTrustManager(trustManagers));
} else {
trustManager = new HttpsUtils.UnSafeTrustManager();
}
sslContext.init(keyManagers, new TrustManager[]{trustManager}, new SecureRandom());
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext,
new String[]{"TLSv1"},
null,
NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
throw new AssertionError(e);
}
}
}
就是一个比较标准的https的转发类,比较值得一提的是getRestTemplate()方法,它封装了一个信任任何证书的RestTemplate转发,不然https请求转发会报错,工具类HttpsUtil在下文会给出
2.HttpsUtils(https支持)
代码如下(示例):
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
import org.slf4j.LoggerFactory;
public class HttpsUtils {
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(HttpsUtils.class);
public static SSLSocketFactory getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password) {
try {
TrustManager[] trustManagers = prepareTrustManager(certificates);
KeyManager[] keyManagers = prepareKeyManager(bksFile, password);
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager trustManager = null;
if (trustManagers != null) {
trustManager = new MyTrustManager(chooseTrustManager(trustManagers));
} else {
trustManager = new UnSafeTrustManager();
}
sslContext.init(keyManagers, new TrustManager[]{trustManager}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (KeyManagementException e) {
throw new AssertionError(e);
} catch (KeyStoreException e) {
throw new AssertionError(e);
}
}
public static TrustManager[] prepareTrustManager(InputStream... certificates) {
if (certificates == null || certificates.length <= 0) return null;
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e) {
Logger.error("error to close certificate", e);
}
}
TrustManagerFactory trustManagerFactory = null;
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
} catch (Exception e) {
Logger.error("fail to prepareTrustManager", e);
}
return null;
}
public static KeyManager[] prepareKeyManager(InputStream bksFile, String password) {
try {
if (bksFile == null || password == null) return null;
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();
} catch (Exception e) {
Logger.error("fail to prepareKeyManager", e);
}
return null;
}
public static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) {
for (TrustManager trustManager : trustManagers) {
if (trustManager instanceof X509TrustManager) {
return (X509TrustManager) trustManager;
}
}
return null;
}
public static void ignoreSSLCheck(OkHttpClient sClient) {
SSLContext sc = null;
try {
sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}}, new SecureRandom());
} catch (Exception e) {
Logger.error("fail to ignoreSSLCheck", e);
}
HostnameVerifier hv1 = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
String workerClassName = "okhttp3.OkHttpClient";
try {
Class workerClass = Class.forName(workerClassName);
Field hostnameVerifier = workerClass.getDeclaredField("hostnameVerifier");
hostnameVerifier.setAccessible(true);
hostnameVerifier.set(sClient, hv1);
Field sslSocketFactory = workerClass.getDeclaredField("sslSocketFactory");
sslSocketFactory.setAccessible(true);
sslSocketFactory.set(sClient, sc.getSocketFactory());
} catch (Exception e) {
Logger.error("fail to hostnameVerifier", e);
}
}
public static class UnSafeTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
public static class MyTrustManager implements X509TrustManager {
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;
public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
var4.init((KeyStore) null);
defaultTrustManager = chooseTrustManager(var4.getTrustManagers());
this.localTrustManager = localTrustManager;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkServerTrusted(chain, authType);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
private class UnSafeHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
}
总结
以上完成springboot部分代理转发,共勉