Bladeren bron

添加路由token验证

Lin Qilong 1 week geleden
bovenliggende
commit
5a74178e94

+ 124 - 0
.idea/uiDesigner.xml

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+      </item>
+    </group>
+  </component>
+</project>

+ 5 - 0
pom.xml

@@ -139,6 +139,11 @@
             <version>${jwt.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
+
     </dependencies>
     <build>
         <finalName>sh-model-gateway</finalName>

+ 7 - 0
src/main/java/cn/com/goldenwater/domain/GatewayRoutes.java

@@ -28,6 +28,13 @@ public class GatewayRoutes implements Serializable {
      */
     private String resultSuccessFormat;
 
+    private String auth;
+
+    private String authQueryOptions;
+
+    private String authExpirationTime;
+
+
     public List<PredicateDefinition> getPredicateDefinition() {
         if (this.predicates != null) {
             String predicates;

+ 23 - 0
src/main/java/cn/com/goldenwater/exception/OkHttpException.java

@@ -0,0 +1,23 @@
+package cn.com.goldenwater.exception;
+
+/**
+ * @author LinQiLong
+ * @date 2022/1/11 10:27
+ */
+public class OkHttpException extends RuntimeException {
+
+    public OkHttpException() {
+    }
+
+    public OkHttpException(String message) {
+        super(message);
+    }
+
+    public OkHttpException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public OkHttpException(Throwable cause) {
+        super(cause);
+    }
+}

+ 1 - 5
src/main/java/cn/com/goldenwater/filter/ApiAuthFilter.java

@@ -1,6 +1,5 @@
 package cn.com.goldenwater.filter;
 
-import cn.com.goldenwater.auth.JwtTokenProvider;
 import cn.com.goldenwater.service.AuditService;
 import cn.com.goldenwater.service.PermissionService;
 import cn.com.goldenwater.service.PtAppService;
@@ -30,9 +29,6 @@ public class ApiAuthFilter implements GlobalFilter, Ordered {
     @Autowired
     private AuditService auditService;
 
-    @Autowired
-    private JwtTokenProvider jwtTokenProvider;
-
     @Autowired
     private PtAppService ptAppService;
 
@@ -48,7 +44,7 @@ public class ApiAuthFilter implements GlobalFilter, Ordered {
             // 如果没有获取到token,则从请求头中获取
             token = TokenUtils.getTokenFromRequest(request);
         }
-        
+
         // 获取第三方服务ID
 //        String thirdPartyId = jwtTokenProvider.getAppId(token);
         String thirdPartyId = ptAppService.getAppIdByAppToken(token);

+ 1 - 0
src/main/java/cn/com/goldenwater/filter/AuthFilter.java

@@ -1,5 +1,6 @@
 package cn.com.goldenwater.filter;
 
+import cn.com.goldenwater.domain.PtService;
 import cn.com.goldenwater.service.AuthService;
 import cn.com.goldenwater.utils.JsonUtils;
 import cn.com.goldenwater.utils.TokenUtils;

+ 103 - 0
src/main/java/cn/com/goldenwater/filter/ThirdPartyAuthFilter.java

@@ -0,0 +1,103 @@
+package cn.com.goldenwater.filter;
+
+import cn.com.goldenwater.core.redis.RedisCache;
+import cn.com.goldenwater.domain.GatewayRoutes;
+import cn.com.goldenwater.service.GatewayRoutesService;
+import cn.com.goldenwater.utils.JsonUtils;
+import cn.com.goldenwater.utils.OkHttpUtils;
+import cn.com.goldenwater.utils.StringUtils;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.JSONPath;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.util.UriComponentsBuilder;
+import reactor.core.publisher.Mono;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class ThirdPartyAuthFilter implements GlobalFilter, Ordered {
+
+    private static final String THIRD_PARTY_AUTH = "third_party_auth";
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    private GatewayRoutesService gatewayRoutesService;
+
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        // 通过URL获取服务信息
+        String requestUrl = exchange.getRequest().getURI().getPath();
+        String gatewayUrl = "/" + requestUrl.split("/")[1];
+        GatewayRoutes gatewayRoutes = gatewayRoutesService.getByPredicates(gatewayUrl);
+
+        // 需要权限
+        if ("1".equals(gatewayRoutes.getAuth())) {
+            // 获取第三方token
+            String key = THIRD_PARTY_AUTH + ":" + gatewayRoutes.getId();
+            String thirdPartyAuthToken = redisCache.getCacheObject(key);
+
+            if (StringUtils.isBlank(thirdPartyAuthToken)) {
+                thirdPartyAuthToken = requestByQueryOptions(gatewayRoutes.getAuthQueryOptions());
+                redisCache.setCacheObject(key, thirdPartyAuthToken, Integer.parseInt(gatewayRoutes.getAuthExpirationTime()), TimeUnit.MINUTES);
+            }
+
+            // 构建新的请求并将access_token添加到查询参数中
+            ServerHttpRequest request = exchange.getRequest();
+            URI uri = UriComponentsBuilder.fromUri(request.getURI())
+                    .queryParam("access_token", thirdPartyAuthToken)
+                    .build(true)
+                    .toUri();
+
+            ServerHttpRequest modifiedRequest = request.mutate()
+                    .uri(uri)
+                    .build();
+
+            ServerWebExchange modifiedExchange = exchange.mutate()
+                    .request(modifiedRequest)
+                    .build();
+
+            return chain.filter(modifiedExchange);
+        }
+
+        return chain.filter(exchange);
+    }
+
+    private String requestByQueryOptions(String queryOptions) {
+        JSONObject apiConfigObj = JsonUtils.jsonToPojo(queryOptions, JSONObject.class);
+        Map<String, Object> params = apiConfigObj.getJSONObject("params");
+        Map<String, Object> cookies = apiConfigObj.getJSONObject("cookies");
+        Map<String, Object> headers = apiConfigObj.getJSONObject("headers");
+        String responseResolution = apiConfigObj.getString("responseResolution");
+
+        String responseString = OkHttpUtils.executeRequest(
+                apiConfigObj.getString("url"),
+                apiConfigObj.getString("method"),
+                params,
+                apiConfigObj.getString("body"),
+                cookies,
+                headers
+        );
+
+        JSONObject responseObj = JsonUtils.jsonToPojo(responseString, JSONObject.class);
+        String result = responseString;
+        if (StringUtils.isNotBlank(responseResolution)) {
+            result = String.valueOf(JSONPath.eval(responseObj, responseResolution));
+        }
+        return result;
+    }
+
+    @Override
+    public int getOrder() {
+        return -70;
+    }
+}

+ 7 - 2
src/main/java/cn/com/goldenwater/service/impl/AuthServiceImpl.java

@@ -1,9 +1,9 @@
 package cn.com.goldenwater.service.impl;
 
-import cn.com.goldenwater.auth.JwtTokenProvider;
 import cn.com.goldenwater.domain.PtApp;
 import cn.com.goldenwater.service.AuthService;
 import cn.com.goldenwater.service.PtAppService;
+import cn.com.goldenwater.service.PtServiceService;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import io.jsonwebtoken.JwtException;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -14,7 +14,7 @@ import reactor.core.publisher.Mono;
 public class AuthServiceImpl implements AuthService {
 
     @Autowired
-    private JwtTokenProvider jwtTokenProvider;
+    private PtServiceService ptServiceService;
 
     @Autowired
     private PtAppService ptAppService;
@@ -22,6 +22,11 @@ public class AuthServiceImpl implements AuthService {
 
     @Override
     public boolean isPublicPath(String path) {
+        // TODO 开放条件判断
+//        String gatewayUrl = "/" + path.split("/")[1];
+//        path = path.replaceFirst(gatewayUrl, "");
+//        PtService ptService = ptServiceService.getServiceByUrl(path);
+//        return Optional.ofNullable(ptService).map(PtService::getOpenCndtn).map("1"::equals).orElse(false);
         return false;
     }
 

+ 341 - 0
src/main/java/cn/com/goldenwater/utils/OkHttpUtils.java

@@ -0,0 +1,341 @@
+package cn.com.goldenwater.utils;
+
+import cn.com.goldenwater.exception.OkHttpException;
+import okhttp3.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by admin on 2018/12/21.
+ */
+public class OkHttpUtils {
+
+    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+    public static final MediaType MEDIA_TYPE = MediaType.parse("application/json;charset=utf-8");
+    private static final Logger logger = LoggerFactory.getLogger(OkHttpUtils.class);
+    private static OkHttpClient client = new OkHttpClient().newBuilder().connectTimeout(20000, TimeUnit.MILLISECONDS)
+            .readTimeout(20000, TimeUnit.MILLISECONDS)
+            .build();
+
+    public static String postJson(String url, String json) {
+        RequestBody body = RequestBody.create(JSON, json);
+        Request request = new Request.Builder().url(url).post(body).build();
+        String bodyStr = httpCall(request);
+        return bodyStr;
+    }
+
+    public static String postJson(String url, String json, Map<String, String> headerMap) {
+        String bodyStr = "";
+        RequestBody body = RequestBody.create(MEDIA_TYPE, json);
+        Headers headers = Headers.of(headerMap);
+        Request request = new Request.Builder().url(url).post(body).headers(headers).build();
+        bodyStr = httpCall(request);
+        return bodyStr;
+    }
+
+    public static String postMap(String url, Map map, Map headerMap) {
+        /* Long sTime = System.currentTimeMillis();*/
+        try {
+            FormBody.Builder builder = new FormBody.Builder();
+            if (map != null && map.size() != 0) {
+                for (Object obj : map.keySet()) {
+                    if (map.get(obj) == null) {
+                    } else {
+                        builder.add(obj + "", map.get(obj).toString());
+                    }
+                }
+            }
+            RequestBody body = builder.build();
+            Headers headers = Headers.of(headerMap);
+            Request request = new Request.Builder().url(url).post(body).headers(headers).build();
+            String bodyStr = httpCall(request);
+            return bodyStr;
+        } catch (Exception e) {
+            logger.error(e.getLocalizedMessage(), e);
+            return "";
+        }
+    }
+
+    public static String postMap(String url, Map map) {
+        FormBody.Builder builder = new FormBody.Builder();
+        if (map != null && map.size() != 0) {
+            for (Object obj : map.keySet()) {
+                if (map.get(obj) == null) {
+                } else {
+                    builder.add(obj + "", map.get(obj).toString());
+                }
+            }
+        }
+        RequestBody body = builder.build();
+        Request request = new Request.Builder().url(url).post(body).build();
+        String bodyStr = httpCall(request);
+        return bodyStr;
+    }
+
+    public static String get(String url) {
+        Request request = new Request.Builder().url(url).build();
+        String bodyStr = httpCall(request);
+        return bodyStr;
+    }
+
+    public static String get(String url, Map headerMap) {
+        Headers headers = Headers.of(headerMap);
+        Request request = new Request.Builder().url(url).headers(headers).build();
+        String bodyStr = httpCall(request);
+        return bodyStr;
+    }
+
+    public static String getTimeUlr(String url) {
+        client = new OkHttpClient.Builder().connectTimeout(1, TimeUnit.SECONDS).readTimeout(1, TimeUnit.SECONDS).build();
+        Request request = new Request.Builder().url(url).build();
+        return httpCall(request);
+    }
+
+    public static String getMap(String url, Map map) {
+        if (map != null && map.size() != 0) {
+            int i = 0;
+            StringBuilder urlBuilder = new StringBuilder(url);
+            for (Object obj : map.keySet()) {
+                if (map.get(obj) != null) {
+                    if (i == 0) {
+                        urlBuilder.append("?");
+                    } else {
+                        urlBuilder.append("&");
+                    }
+                    urlBuilder.append(obj).append("=").append(map.get(obj));
+                    i++;
+                }
+            }
+            url = urlBuilder.toString();
+        }
+        Request request = new Request.Builder().url(url).get().build();
+        return httpCall(request);
+    }
+
+    public static String getList(String url, List list) {
+        String json = list.toString();
+        RequestBody body = RequestBody.create(JSON, json);
+        Request request = new Request.Builder().url(url).get().put(body).build();
+        String bodyStr = httpCall(request);
+        return bodyStr;
+    }
+
+    public static File postFile(String url) {
+        Request request = new Request.Builder().url(url).build();
+        Response response = null;
+        Call call = null;
+        File file = new File("../report");
+        try {
+            call = client.newCall(request);
+            response = call.execute();
+            InputStream in = response.body().byteStream();
+            FileOutputStream fos = new FileOutputStream(file);
+            byte[] buf = new byte[8096];
+            int size = 0;
+            while ((size = in.read(buf)) != -1) {
+                fos.write(buf, 0, size);
+            }
+            fos.close();
+        } catch (IOException e) {
+            logger.info("请求失败----------------url:{}" + e, url);
+        } finally {
+            call.cancel();
+        }
+        return file;
+    }
+
+    public static void download(String url) {
+        Request request = new Request.Builder()
+                .url(url)
+                .build();
+        Response response = null;
+        Call call = null;
+        File file = new File("../report");
+        try {
+            call = client.newCall(request);
+            response = call.execute();
+            InputStream in = response.body().byteStream();
+            FileOutputStream fos = new FileOutputStream(file);
+            byte[] buf = new byte[8096];
+            int size = 0;
+            while ((size = in.read(buf)) != -1) {
+                fos.write(buf, 0, size);
+            }
+            in.close();
+            fos.flush();
+            fos.close();
+        } catch (IOException e) {
+            logger.info("请求失败----------------url:{}" + e, url);
+        } finally {
+            call.cancel();
+        }
+    }
+
+    public static String multipartHttpPost(String url, MultipartFile file, String filename, String name, Map<String, Object> param) {
+        try {
+            ByteArrayOutputStream output = new ByteArrayOutputStream();
+            InputStream input = file.getInputStream();
+            byte[] buffer = new byte[4096];
+            int n = 0;
+            while (-1 != (n = input.read(buffer))) {
+                output.write(buffer, 0, n);
+            }
+            RequestBody body = RequestBody.create(JSON, output.toByteArray());
+            Request request = new Request.Builder()
+                    .url(url).post(body)
+                    .build();
+            return httpCall(request);
+        } catch (IOException e) {
+            logger.error("url:{},参数:{}", url, e);
+        }
+        return null;
+    }
+
+    private static String httpCall(Request request) {
+        Response response = null;
+        Call call = null;
+        try {
+            call = client.newCall(request);
+            response = call.execute();
+            ResponseBody responseBody = response.body();
+            return responseBody == null ? "" : responseBody.string();
+        } catch (IOException e) {
+            logger.error(e.getMessage(), e);
+            throw new OkHttpException("request error", e);
+        } finally {
+            Optional.ofNullable(call).ifPresent(Call::cancel);
+        }
+    }
+
+    /**
+     * 把request转为map
+     *
+     * @param request
+     * @return
+     */
+    public static Map<String, Object> getParameterMap(HttpServletRequest request) {
+        // 参数Map
+        Map<?, ?> properties = request.getParameterMap();
+        // 返回值Map
+        Map<String, Object> returnMap = new HashMap<String, Object>();
+        Iterator<?> entries = properties.entrySet().iterator();
+
+        Map.Entry<String, Object> entry;
+        String name = "";
+        String value = "";
+        Object valueObj = null;
+        while (entries.hasNext()) {
+            entry = (Map.Entry<String, Object>) entries.next();
+            name = (String) entry.getKey();
+            valueObj = entry.getValue();
+            if (null == valueObj) {
+                value = "";
+            } else if (valueObj instanceof String[]) {
+                String[] values = (String[]) valueObj;
+                for (String s : values) {
+                    value = s + ",";
+                }
+                value = value.substring(0, value.length() - 1);
+            } else {
+                value = valueObj.toString();
+            }
+            returnMap.put(name, value);
+        }
+        return returnMap;
+    }
+
+    /**
+     * 通用 HTTP 请求方法
+     *
+     * @param url     请求地址
+     * @param method  请求方法 (GET, POST, PUT, DELETE, PATCH)
+     * @param params  查询参数 (用于GET请求的URL参数)
+     * @param body    请求体 (用于POST/PUT等的请求体,可以是JSON或表单数据)
+     * @param cookies Cookie信息
+     * @param headers 请求头
+     * @return String 响应对象
+     * @throws IOException 当请求执行失败时抛出
+     */
+    public static String executeRequest(String url,
+                                        String method,
+                                        Map<String, Object> params,
+                                        String body,
+                                        Map<String, Object> cookies,
+                                        Map<String, Object> headers) {
+        // 构建请求URL(添加查询参数)
+        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder();
+        if (params != null) {
+            for (Map.Entry<String, Object> entry : params.entrySet()) {
+                urlBuilder.addQueryParameter(entry.getKey(), String.valueOf(entry.getValue()));
+            }
+        }
+        String requestUrl = urlBuilder.build().toString();
+
+        // 构建请求体
+        RequestBody requestBody = null;
+        if (body != null) {
+            requestBody = RequestBody.create(JSON, body);
+        }
+
+        // 构建请求
+        Request.Builder requestBuilder = new Request.Builder()
+                .url(requestUrl);
+
+        // 设置请求方法
+        switch (method.toUpperCase()) {
+            case "POST":
+                requestBuilder.post(requestBody != null ? requestBody : RequestBody.create(null, new byte[0]));
+                break;
+            case "PUT":
+                requestBuilder.put(requestBody != null ? requestBody : RequestBody.create(null, new byte[0]));
+                break;
+            case "DELETE":
+                requestBuilder.delete(requestBody != null ? requestBody : RequestBody.create(null, new byte[0]));
+                break;
+            case "PATCH":
+                requestBuilder.patch(requestBody != null ? requestBody : RequestBody.create(null, new byte[0]));
+                break;
+            default: // GET
+                requestBuilder.get();
+        }
+
+        // 添加请求头
+        if (headers != null) {
+            for (Map.Entry<String, Object> entry : headers.entrySet()) {
+                requestBuilder.addHeader(entry.getKey(), String.valueOf(entry.getValue()));
+            }
+        }
+
+        // 添加Cookies
+        if (cookies != null) {
+            for (Map.Entry<String, Object> entry : cookies.entrySet()) {
+                requestBuilder.addHeader("Cookie", entry.getKey() + "=" + entry.getValue());
+            }
+        }
+
+        // 执行请求并返回响应
+        Request request = requestBuilder.build();
+        return httpCall(request);
+    }
+
+    /**
+     * 简化方法:执行GET请求
+     */
+    public static String get(String url, Map<String, Object> params, Map<String, Object> headers) throws IOException {
+        return executeRequest(url, "GET", params, null, null, headers);
+    }
+
+    /**
+     * 简化方法:执行POST请求(JSON数据)
+     */
+    public static String postJsons(String url, String jsonData, Map<String, Object> headers) throws IOException {
+        return executeRequest(url, "POST", null, jsonData, null, headers);
+    }
+
+}