需求:做一个网关将外网的请求做一个转化(添加上签名,格式化等操作),然后将请求转发到内网的网关soul-gateway上
1. 思路:采用拦截器拦截掉请求(我这里是拦截固定前缀的请求),然后做一个请求的转化,最后在拦截器中做response返回
1.1 定义的拦截器 继承 handlerInterceptor接口
package com.tyyd.dyhdzzxxxt.gateway.interceptor;
import com.tyyd.dyhdzzxxxt.gateway.common.AcwsException;
import com.tyyd.dyhdzzxxxt.gateway.config.SoulConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Component
@Slf4j
public class AllRequestHandler implements HandlerInterceptor {
//内网网关的配置 主要是内网地址和端口
@Autowired
SoulConfig soulConfig;
//路由处理请求参数的处理类
@Autowired
RoutingDelegate routingDelegate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (log.isDebugEnabled()) {
long logstart = System.currentTimeMillis();
request.setAttribute("logstart", logstart);
}
String URL = "" + soulConfig.getHost() + ":" + soulConfig.getPort();
ResponseEntity responseEntity = routingDelegate.redirect(request, response, URL, "/api/v1");
if (responseEntity == null) {
throw new AcwsException("请求结果异常");
}
response.setStatus(responseEntity.getStatusCodeValue());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
try {
out = response.getWriter();
out.append(responseEntity.getBody());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
if (log.isDebugEnabled()) {
long logstart = (long) request.getAttribute("logstart");
log.debug("代理请求耗时:{}ms", System.currentTimeMillis() - logstart);
}
return false;
}
}
1.2注入定义的拦截器配置 继承webMvcConfigurer接口
package com.tyyd.dyhdzzxxxt.gateway.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@Slf4j
public class IntercepterConfig implements WebMvcConfigurer {
//注入自定义的
@Autowired
private AllRequestHandler addInterceptor;
/
配置拦截路径
@param registry
/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//配置拦截的路径
registry.addInterceptor(addInterceptor).addPathPatterns("/api/v1/");
}
}
1.3 处理请求参数的处理类
package com.tyyd.dyhdzzxxxt.gateway.interceptor;
import com.tyyd.dyhdzzxxxt.gateway.config.SoulAppConfig;
import com.tyyd.dyhdzzxxxt.gateway.config.SoulConfig;
import com.tyyd.dyhdzzxxxt.gateway.domain.AppKeyAndAppSecretEntity;
import com.tyyd.dyhdzzxxxt.gateway.util.SignUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
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.util.Collections;
import java.util.List;
import java.util.Map;
@Component
@Slf4j
public class RoutingDelegate {
//内网网关soulgateway的配置 IP地址和端口等
@Autowired
SoulConfig soulConfig;
//考虑分布式部署 每个当前的网关都会有单独的 appkey和appsecret 这里配置的就是appkey和appsecret
@Autowired
SoulAppConfig soulAppConfig;
@Autowired
RestTemplate restTemplate;
public ResponseEntity redirect(HttpServletRequest request, HttpServletResponse response, String routeUrl, String prefix) {
try {
//构造url
String redirectUrl = createRedictUrl(request, routeUrl, prefix);
//构造请求头和请求体
RequestEntity requestEntity = createRequestEntity(request, redirectUrl,prefix);
//转发请求
return route(requestEntity);
} catch (Exception e) {
log.error("请求失败", e);
return new ResponseEntity("请求失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
//构造url和路径参数
private String createRedictUrl(HttpServletRequest request, String routeUrl, String prefix) {
String queryString = request.getQueryString();
String url = request.getRequestURI().replaceAll(prefix, "");
return routeUrl + url + (queryString != null ? "?" + queryString : "");
}
//构造请求头和请求体
private RequestEntity createRequestEntity(HttpServletRequest request, String url, String prefix) throws URISyntaxException, //代码效果参考:http://www.jhylw.com.cn/380435929.html
IOException {String method = request.getMethod();
HttpMethod httpMethod = HttpMethod.resolve(method);
MultiValueMap headers = parseRequestHeader(request, prefix);
byte【】 body = parseRequestBody(request);
return new RequestEntity(body, headers, httpMethod, new URI(url));
}
private ResponseEntity route(RequestEntity requestEntity) {
log.debug("代理访问:{},参数:{}",requestEntity.getUrl(),requestEntity);
ResponseEntity result = restTemplate.exchange(requestEntity, String.class);
log.debug("代理访问:{},结果:{}",requestEntity.getUrl(),result.getBody());
return result;
}
private byte【】 parseRequestBody(HttpServletRequest request) throws IOException {
InputStream inputStream = request.getInputStream();
return //代码效果参考:http://www.jhylw.com.cn/431231091.html
StreamUtils.copyToByteArray(inputStream);}
private MultiValueMap parseRequestHeader(HttpServletRequest request, String prefix) {
HttpHeaders headers = new HttpHeaders();
List headerNames = Collections.list(request.getHeaderNames());
for (String headerName : headerNames) {
List headerValues = Collections.list(request.getHeaders(headerName));
for (String headerValue : headerValues) {
headers.add(headerName, headerValue);
}
}
String sysId = request.getHeader("sysId");
if(!StringUtils.hasText(sysId)){
sysId = request.getParameter("sysId");
}
//根据传递过来的sysId来选择不同的appkey可secret
AppKeyAndAppSecretEntity appKeyAndAppSecretEntity = soulAppConfig.getAppKeyAndAppSecretEntity(Integer.valueOf(sysId));
String url = request.getRequestURI().replaceAll(prefix, "");
//构造签名
Map signMap = SignUtils.getInstance().sign(appKeyAndAppSecretEntity, url);
headers.add("sign", signMap.get("sign"));
headers.add("timestamp", signMap.get("timestamp"));
headers.add("version", signMap.get("version"));
headers.add("path", url);
headers.add("appKey", appKeyAndAppSecretEntity.getAppKey());
headers.add("group", soulConfig.getGroup());
headers.add("tag", soulConfig.getTag());
return headers;
}
}
签名工具类Signutils:
package com.tyyd.dyhdzzxxxt.gateway.util;
import com.tyyd.dyhdzzxxxt.gateway.domain.AppKeyAndAppSecretEntity;
import org.springframework.util.DigestUtils;
import java.util.;
import java.util.stream.Collectors;
public final class SignUtils {
private static final SignUtils SIGN_UTILS = new SignUtils();
private SignUtils() {
}
public static SignUtils getInstance() {
return SIGN_UTILS;
}
public static String generateSign(final String signKey, final Map params) {
List storedKeys = Arrays.stream(params.keySet()
.toArray(new String【】{}))
.sorted(Comparator.naturalOrder())
.collect(Collectors.toList());
final String sign = storedKeys.stream()
.filter(key -> !Objects.equals(key, "sign"))
.map(key -> String.join("", key, params.get(key)))
.collect(Collectors.joining()).trim()
.concat(signKey);
return DigestUtils.md5DigestAsHex(sign.getBytes()).toUpperCase();
}
public Map sign(AppKeyAndAppSecretEntity appKeyAndAppSecretEntity , String url){
long now = System.currentTimeMillis();
HashMap params = new HashMap();
//这里要和内网的保持规则相同 如果要改请同时改 否则可能会导致签名校验不通过
//params.put("timestamp", String.valueOf(new Date()));
params.put("timestamp", String.valueOf(now));
params.put("path", url);
params.put("version", "1.0.0");
String sign = generateSign(appKeyAndAppSecretEntity.getAppSecret(), params);
params.put("sign",sign);
return params;
}
}