两个服务A、B,B不需要鉴权,A需要鉴权访问。客户端如果直接访问B会存在一定的风险。所以希望通过A访问B,这时候就需要A实现一个类似nginx网关转发功能
实现方式
在controller中拦截指定路径,通过转换地址,封装请求体,使用RestTemplate重发请求
实现代码
1、配置RestTemplate
@Configuration
public class BaseConfig {
@Bean(value = "routeRestTemplate")
RestTemplate routeRestTemplate(){
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
// 读写超时时间/毫秒
factory.setReadTimeout(20 * 1000);
// 连接超时时间/毫秒
factory.setConnectTimeout(5 * 1000);
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setErrorHandler(new ResponseErrorHandler(){
@Override
public boolean hasError(ClientHttpResponse response) {
// 返回false表示不管response的status是多少都返回没有错
// 这里可以自己定义那些status code你认为是可以抛Error
return false;
}
@Override
public void handleError(ClientHttpResponse response) {
// 这里面可以实现你自己遇到了Error进行合理的处理
}
});
return restTemplate;
}
}
2.编写路由工具类
直接上代码
@Slf4j
@Component
public class RoutingDelegate {
@Resource
@Qualifier("routeRestTemplate")
private RestTemplate routeRestTemplate;
public ResponseEntity<String> redirect(HttpServletRequest request, HttpServletResponse response,String routeUrl, String prefix) {
try {
String redirectUrl = createRedictUrl(request,routeUrl, prefix);
RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
log.info("路由转发,跳转地址:{},请求数据:{}",redirectUrl,requestEntity);
ResponseEntity<String> responseEntity = route(requestEntity);
log.info("路由转发,返回结果:{}",responseEntity);
return responseEntity;
} catch (Exception e) {
log.info("路由转发请求失败", e);
throw new HousekeeperException(HousekeeperErrorEnums.BaseCode.API_REQUEST_ERROR);
}
}
private String createRedictUrl(HttpServletRequest request, String routeUrl, String prefix) {
String queryString = request.getQueryString();
return routeUrl + request.getRequestURI().replace(prefix, "") +
(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) {
return routeRestTemplate.exchange(requestEntity, String.class);
}
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;
}
}
3、编写controller转发
@RefreshScope
@RestController
@RequestMapping(RouteController.DELEGATE_PREFIX)
public class RouteController {
public final static String DELEGATE_PREFIX = "/payRoute";
@Value("${route.pay.url:}")
private String redirectUrl;
@Resource
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, redirectUrl, RouteController.DELEGATE_PREFIX);
}
}
总结
如上可以实现通过服务实现类似nginx或gateway的网关转发功能
评论区