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 = "http://" + soulConfig.getHost() + ":" + soulConfig.getPort(); ResponseEntity<String> 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<String> 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, IOException {
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(method);
        MultiValueMap<String, String> headers = parseRequestHeader(request, prefix);
        byte[] body = parseRequestBody(request);
        return new RequestEntity<>(body, headers, httpMethod, new URI(url));
    }

    private ResponseEntity<String> route(RequestEntity requestEntity) {
        log.debug("代理访问:{},参数:{}",requestEntity.getUrl(),requestEntity);
        ResponseEntity<String> 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 StreamUtils.copyToByteArray(inputStream);
    }

    private MultiValueMap<String, String> parseRequestHeader(HttpServletRequest request, String prefix) {
        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);
            }
        }
        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
<String, String> 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<String, String> params) {
        List<String> 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<String, String> sign(AppKeyAndAppSecretEntity appKeyAndAppSecretEntity , String url){
        long now = System.currentTimeMillis();
        HashMap<String, String> 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;
    }


}

  

 

posted @ 2021-11-30 10:18  少侠砍人不用刀  阅读(3868)  评论(0编辑  收藏  举报