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;
    }


}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM