技术笔记:springboot项目使用拦截器实现一个简单的网关请求透传

简介: 技术笔记:springboot项目使用拦截器实现一个简单的网关请求透传

需求:做一个网关将外网的请求做一个转化(添加上签名,格式化等操作),然后将请求转发到内网的网关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;


}


}

相关文章
|
1天前
|
Java 数据库连接 数据库
大事件后端项目05-----springboot整合mybatis
大事件后端项目05-----springboot整合mybatis
大事件后端项目05-----springboot整合mybatis
|
2天前
|
Java API Spring
Spring Boot中使用Feign进行HTTP请求
Spring Boot中使用Feign进行HTTP请求
|
3天前
|
NoSQL 前端开发 Java
技术笔记:springboot分布式锁组件spring
技术笔记:springboot分布式锁组件spring
|
1天前
|
Java Maven
大事件后端项目02----springboot工程创建
大事件后端项目02----springboot工程创建
|
1天前
|
前端开发 Java 网络架构
大事件后端项目01-----SpringBoot快速入门
大事件后端项目01-----SpringBoot快速入门
|
2天前
|
Java API Spring
Spring Boot中使用Feign进行HTTP请求
Spring Boot中使用Feign进行HTTP请求
|
3天前
|
IDE Java 机器人
Spring Boot中的多模块项目构建
Spring Boot中的多模块项目构建
|
2天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的大学生心理健康诊断专家系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的大学生心理健康诊断专家系统的详细设计和实现(源码+lw+部署文档+讲解等)
8 0
|
2天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的大学生成绩管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的大学生成绩管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
7 0
|
2天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的大棚蔬菜管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的大棚蔬菜管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
3 0