sdk/pom.xml
对比新文件 @@ -0,0 +1,86 @@ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>com.iailab</groupId> <artifactId>iailab-plat</artifactId> <version>${revision}</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>sdk</artifactId> <packaging>jar</packaging> <name>${project.artifactId}</name> <description> sdk </description> <dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.55</version> </dependency> <dependency> <groupId>com.iailab</groupId> <artifactId>iailab-common</artifactId> <version>${revision}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency> <dependency> <groupId>com.iailab</groupId> <artifactId>iailab-common-test</artifactId> </dependency> </dependencies> <!-- <build>--> <!-- <!– 设置构建的 jar 包名 –>--> <!-- <finalName>${project.artifactId}</finalName>--> <!-- <plugins>--> <!-- <!– 打包 –>--> <!-- <plugin>--> <!-- <groupId>org.springframework.boot</groupId>--> <!-- <artifactId>spring-boot-maven-plugin</artifactId>--> <!-- <version>${spring.boot.version}</version>--> <!-- <executions>--> <!-- <execution>--> <!-- <goals>--> <!-- <goal>repackage</goal> <!– 将引入的 jar 打入其中 –>--> <!-- </goals>--> <!-- </execution>--> <!-- </executions>--> <!-- </plugin>--> <!-- </plugins>--> <!-- </build>--> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> </archive> </configuration> </plugin> </plugins> </build> </project> sdk/src/main/java/com/iailab/sdk/SdkMain.java
对比新文件 @@ -0,0 +1,14 @@ package com.iailab.sdk; /** * 项目的主类 * * @author iailab */ public class SdkMain { public static void main(String[] args) { System.out.println("I am the main class"); } } sdk/src/main/java/com/iailab/sdk/auth/client/IailabAuthClient.java
对比新文件 @@ -0,0 +1,94 @@ package com.iailab.sdk.auth.client; import com.fasterxml.jackson.databind.ObjectMapper; import com.iailab.sdk.auth.client.dto.TokenDTO; import com.iailab.sdk.auth.client.vo.AuthLoginReqVO; import com.iailab.sdk.auth.config.SdkConfiguration; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.web.client.RestTemplate; import java.util.*; /** * @author Houzhongjian * @Description * @createTime 2025年02月18日 */ @Component @Service public class IailabAuthClient { /** * 平台地址 */ public static String BASE_URL = "http://172.16.8.100:48080/admin-api"; /** * 租户编号 */ public static String TENANT_ID = "1"; private static final RestTemplate restTemplate = new RestTemplate(); /** * 用户名密码方式获取平台token */ public static synchronized TokenDTO login(AuthLoginReqVO loginReqVO) throws Exception { System.out.println("登录获取平台token"); ObjectMapper objectMapper = new ObjectMapper(); String paramString = objectMapper.writeValueAsString(loginReqVO); // 1.1 构建请求头 HttpHeaders headers = new HttpHeaders(); addClientHeader(headers); headers.setContentType(MediaType.APPLICATION_JSON_UTF8); // 1.2 构建authenticate请求URL String authenticateUrl = BASE_URL + "/system/auth/login"; // 2. 执行请求 ResponseEntity<Map<String, Object>> exchange = restTemplate.exchange( authenticateUrl, HttpMethod.POST, new HttpEntity<>(paramString, headers), new ParameterizedTypeReference<Map<String, Object>>() { }); return handleResponse(exchange); } public static synchronized TokenDTO refreshToken(String refreshToken) { System.out.println("刷新token"); // 1.1 构建请求头 HttpHeaders headers = new HttpHeaders(); addClientHeader(headers); // 1.2 构建authenticate请求URL String authenticateUrl = BASE_URL + "/system/auth/refresh-token?refreshToken=" + refreshToken; // 2. 执行请求 ResponseEntity<Map<String, Object>> exchange = restTemplate.exchange( authenticateUrl, HttpMethod.POST, new HttpEntity<>(headers), new ParameterizedTypeReference<Map<String, Object>>() { }); return handleResponse(exchange); } private static void addClientHeader(HttpHeaders headers) { headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); headers.set("tenant-id", TENANT_ID); } // 统一处理响应 private static <T> TokenDTO handleResponse(ResponseEntity<T> response) { Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); System.out.println(response); TokenDTO authTokenDTO = new TokenDTO(); Map<String, Object> authMap = (Map<String, Object>)response.getBody(); Map<String, Object> tokenData = (Map<String, Object>)authMap.get("data"); authTokenDTO.setAccessToken(tokenData.get("accessToken").toString()); authTokenDTO.setRefreshToken(tokenData.get("refreshToken").toString()); authTokenDTO.setExpiresTime(Long.valueOf(tokenData.get("expiresTime").toString())); return authTokenDTO; } } sdk/src/main/java/com/iailab/sdk/auth/client/dto/TokenDTO.java
对比新文件 @@ -0,0 +1,11 @@ package com.iailab.sdk.auth.client.dto; import lombok.Data; @Data public class TokenDTO { private String accessToken; private String refreshToken; private Long expiresTime; private String tokenType; } sdk/src/main/java/com/iailab/sdk/auth/client/vo/AuthLoginReqVO.java
对比新文件 @@ -0,0 +1,31 @@ package com.iailab.sdk.auth.client.vo; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; @Data @NoArgsConstructor @AllArgsConstructor @Builder public class AuthLoginReqVO { @NotEmpty(message = "登录账号不能为空") @Length(min = 4, max = 16, message = "账号长度为 4-16 位") @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") private String username; @NotEmpty(message = "密码不能为空") @Length(min = 4, max = 16, message = "密码长度为 4-16 位") private String password; // ========== 图片验证码相关 ========== private String captchaVerification; } sdk/src/main/java/com/iailab/sdk/auth/config/SdkConfiguration.java
对比新文件 @@ -0,0 +1,48 @@ package com.iailab.sdk.auth.config; import org.springframework.context.annotation.Configuration; import java.io.IOException; import java.io.InputStream; import java.util.Properties; /** * SDK配置文件 */ @Configuration public class SdkConfiguration { private String baseUrl; public String getTenantId() { return tenantId; } private String tenantId; public static SdkConfiguration load() { SdkConfiguration config = new SdkConfiguration(); try(InputStream is = SdkConfiguration.class.getResourceAsStream("/application.yaml")) { Properties props = new Properties(); props.load(is); config.baseUrl = props.getProperty("base-url"); config.tenantId = props.getProperty("tenant-id"); } catch (IOException e) { // 处理异常或使用默认值 } if(config.baseUrl == null) { throw new IllegalStateException("BaseUrl must be configured"); } if(config.tenantId == null) { throw new IllegalStateException("TenantId must be configured"); } return config; } public String getBaseUrl() { return baseUrl; } } sdk/src/main/java/com/iailab/sdk/auth/interceptor/AuthInterceptor.java
对比新文件 @@ -0,0 +1,26 @@ //package com.iailab.module.sdk.auth.interceptor; // //import org.springframework.http.HttpRequest; //import org.springframework.http.client.ClientHttpRequestExecution; //import org.springframework.http.client.ClientHttpRequestInterceptor; //import org.springframework.http.client.ClientHttpResponse; // //import java.io.IOException; // //public class AuthInterceptor implements ClientHttpRequestInterceptor { // // private final AuthTokenHolder tokenHolder; // // public AuthInterceptor(AuthTokenHolder tokenHolder) { // this.tokenHolder = tokenHolder; // } // // @Override // public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) { // if (tokenHolder.getToken() != null) { // request.getHeaders().setBearerAuth(tokenHolder.getToken()); // } // return execution.execute(request, body); // } // //} sdk/src/main/java/com/iailab/sdk/util/http/HttpClientFactory.java
对比新文件 @@ -0,0 +1,162 @@ package com.iailab.sdk.util.http; import com.alibaba.fastjson.JSON; import org.apache.http.HttpEntity; import org.apache.http.HttpRequest; import org.apache.http.HttpStatus; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; import java.io.Closeable; import java.io.IOException; import java.util.Map; public class HttpClientFactory { private static final Logger logger = LoggerFactory.getLogger(HttpClientFactory.class); private static PoolingHttpClientConnectionManager clientConnectionManager=null; // private static CloseableHttpClient httpClient=null; private static RequestConfig config = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build(); // private final static Object syncLock = new Object(); private static boolean isInited=false; /** * 创建httpclient连接池并初始化 */ private static void init(){ if(isInited)return; try { //添加对https的支持,该sslContext没有加载客户端证书 // 如果需要加载客户端证书,请使用如下sslContext,其中KEYSTORE_FILE和KEYSTORE_PASSWORD分别是你的证书路径和证书密码 //KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType() //FileInputStream instream = new FileInputStream(new File(KEYSTORE_FILE)); //keyStore.load(instream, KEYSTORE_PASSWORD.toCharArray()); //SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore,KEYSTORE_PASSWORD.toCharArray()) // .loadTrustMaterial(null, new TrustSelfSignedStrategy()) //.build(); SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(null, new TrustSelfSignedStrategy()) .build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,SSLConnectionSocketFactory.getDefaultHostnameVerifier()); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("https", sslsf) .register("http", PlainConnectionSocketFactory.getSocketFactory()) .build(); clientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); clientConnectionManager.setMaxTotal(50); clientConnectionManager.setDefaultMaxPerRoute(25); isInited=true; }catch (Exception e){ logger.warn("httpUtils init get exception:",e); } } public static CloseableHttpClient getHttpClient(){ init(); // if(httpClient == null){ // synchronized (syncLock){ // if(httpClient == null){ //// CookieStore cookieStore = new BasicCookieStore(); //// BasicClientCookie cookie = new BasicClientCookie("sessionID", "######"); //// cookie.setDomain("#####"); //// cookie.setPath("/"); //// cookieStore.addCookie(cookie); // httpClient =HttpClients.custom().setConnectionManager(clientConnectionManager) // //.setDefaultCookieStore(cookieStore) // .setDefaultRequestConfig(config).build(); // } // } // } // return httpClient; return HttpClients.custom().setConnectionManager(clientConnectionManager) //.setDefaultCookieStore(cookieStore) .setDefaultRequestConfig(config).build(); } /** * 设置请求头信息 * @param headers * @param request * @return */ private static HttpRequest setHeaders(Map<String,Object> headers, HttpRequest request) { for (Map.Entry entry : headers.entrySet()) { if (!entry.getKey().equals("Cookie")) { request.addHeader((String) entry.getKey(), (String) entry.getValue()); } else { Map<String, Object> Cookies = (Map<String, Object>) entry.getValue(); for (Map.Entry entry1 : Cookies.entrySet()) { request.addHeader(new BasicHeader("Cookie", (String) entry1.getValue())); } } } return request; } /** * post请求,使用json格式传参 * @param url * @param data * @param headers * @return */ public static <T> T httpPost(String url, String data, Map<String,Object> headers,Class<T> clazz){ CloseableHttpClient httpClient = getHttpClient(); HttpRequest request = new HttpPost(url); if(headers!=null&&!headers.isEmpty()){ request = setHeaders(headers,request); } CloseableHttpResponse response = null; try { HttpPost httpPost = (HttpPost) request; httpPost.setEntity(new StringEntity(data, ContentType.create("application/json", "UTF-8"))); response=httpClient.execute(httpPost); HttpEntity entity = response.getEntity(); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != entity) { String respBody = EntityUtils.toString(entity); logger.info("validate st response:"+respBody); T respObj = (T) JSON.parseObject(respBody, clazz); return respObj; } } catch (IOException e) { logger.error("http post exec error,msg"+e.getMessage(),e); } finally { safeClose(response,null); // safeClose(httpClient,null); } return null; } private static void safeClose(Closeable closeable,String errMsg){ try{ if(closeable!=null) closeable.close(); }catch (Exception ex){ if(errMsg!=null && "".equals(errMsg)){ logger.error(errMsg+",error:"+ex.getMessage(),ex); } } } } sdk/src/main/java/com/iailab/sdk/util/http/IailabHttpUtils.java
对比新文件 @@ -0,0 +1,106 @@ package com.iailab.sdk.util.http; import com.iailab.sdk.auth.client.IailabAuthClient; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.util.EntityUtils; import org.springframework.util.CollectionUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Map; public class IailabHttpUtils { /** * * @param url * @param map * @param charset * @return */ public static String doGet(String url, Map<String, String> map, String charset, String accessToken) { System.out.println("start doGet url: " + url); org.apache.http.client.HttpClient httpClient = null; HttpGet httpGet = null; String result = null; try { StringBuilder sb = new StringBuilder(); sb.append(url); if (!CollectionUtils.isEmpty(map)) { if ((url.indexOf("?") == -1)) { sb.append("?"); } else { sb.append("&"); } map.forEach((k, v) -> { try { sb.append(k + "=" + URLEncoder.encode(v, charset) + "&"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } }); sb.append("t=" + System.currentTimeMillis()); } httpClient = HttpClientFactory.getHttpClient(); httpGet = new HttpGet(sb.toString()); //设置参数 httpGet.addHeader("Accept", "application/json"); httpGet.addHeader("Content-Type", "application/json;charset=UTF-8"); httpGet.addHeader("Authorization", "Bearer " + accessToken); httpGet.addHeader("Tenant-Id", String.valueOf(IailabAuthClient.TENANT_ID)); HttpResponse response = httpClient.execute(httpGet); if (response != null) { HttpEntity resEntity = response.getEntity(); if (resEntity != null) { result = EntityUtils.toString(resEntity, charset); } } } catch (Exception ex) { ex.printStackTrace(); } return result; } /** * * @param url * @param json * @param charset * @return */ public static String doPost(String url, String json, String charset, String accessToken) { System.out.println("start doPost url: " + url); org.apache.http.client.HttpClient httpClient = null; HttpPost httpPost = null; String result = null; try { httpClient = HttpClientFactory.getHttpClient(); httpPost = new HttpPost(url); //设置参数 httpPost.addHeader("Accept", "application/json"); httpPost.addHeader("Content-Type", "application/json;charset=UTF-8"); httpPost.addHeader("Authorization", "Bearer " + accessToken); httpPost.addHeader("Tenant-Id", String.valueOf(IailabAuthClient.TENANT_ID)); StringEntity stringEntity = new StringEntity(json); stringEntity.setContentEncoding("UTF-8"); stringEntity.setContentType("application/json"); httpPost.setEntity(stringEntity); HttpResponse response = httpClient.execute(httpPost); if (response != null) { HttpEntity resEntity = response.getEntity(); if (resEntity != null) { result = EntityUtils.toString(resEntity, charset); } } } catch (Exception ex) { ex.printStackTrace(); } return result; } } sdk/src/main/java/com/iailab/sdk/util/package-info.java
对比新文件 @@ -0,0 +1,4 @@ /** * 每个模块的 util 包,放专属当前模块的 Utils 工具类 */ package com.iailab.sdk.util; sdk/src/main/resources/application-dev.yaml
对比新文件 @@ -0,0 +1,6 @@ --- #################### SDK相关配置 #################### iailab: auth: base-url: http://172.16.8.100:48080/admin-api tenant-id: 1 sdk/src/main/resources/application.yaml
对比新文件 @@ -0,0 +1,13 @@ spring: application: name: sdk profiles: active: dev iailab: auth: base-url: http://172.16.8.100:48080/admin-api tenant-id: 1 sdk/src/test/java/com/iailab/sdk/IailabClientTest.java
对比新文件 @@ -0,0 +1,39 @@ package com.iailab.sdk; import com.iailab.framework.test.core.ut.BaseMockitoUnitTest; import com.iailab.sdk.auth.client.IailabAuthClient; import com.iailab.sdk.auth.client.dto.TokenDTO; import com.iailab.sdk.auth.client.vo.AuthLoginReqVO; import org.junit.jupiter.api.Test; import javax.annotation.Resource; /** * {@link IailabClientTest} 的单元测试 * * @author iailab */ public class IailabClientTest extends BaseMockitoUnitTest { @Test public void testLogin() throws Exception { // 准备参数 AuthLoginReqVO authLoginReqVO = new AuthLoginReqVO(); authLoginReqVO.setUsername("sysadmin"); authLoginReqVO.setPassword("iailab2019"); TokenDTO login = IailabAuthClient.login(authLoginReqVO); System.out.println(login.getAccessToken()); System.out.println(login.getRefreshToken()); } @Test public void testRefreshToken() { // 准备参数 String refreshToken = "1d62031562364ed29d6d414fea97e2dd"; TokenDTO tokenDTO = IailabAuthClient.refreshToken(refreshToken); System.out.println(tokenDTO); } } sdk/src/test/resources/application-unit-test.yaml
对比新文件 @@ -0,0 +1,57 @@ spring: main: lazy-initialization: true # 开启懒加载,加快速度 banner-mode: off # 单元测试,禁用 Banner --- #################### 数据库相关配置 #################### spring: # 数据源配置项 datasource: name: ruoyi-vue-pro url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 driver-class-name: org.h2.Driver username: sa password: druid: async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 initial-size: 1 # 单元测试,配置为 1,提升启动速度 sql: init: schema-locations: classpath:/sql/create_tables.sql # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: host: 127.0.0.1 # 地址 port: 16379 # 端口(单元测试,使用 16379 端口) database: 0 # 数据库索引 mybatis: lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 mybatis-plus: global-config: db-config: id-type: AUTO # H2 主键递增 --- #################### 定时任务相关配置 #################### --- #################### 配置中心相关配置 #################### --- #################### 服务保障相关配置 #################### # Lock4j 配置项(单元测试,禁用 Lock4j) --- #################### 监控相关配置 #################### --- #################### 平台相关配置 #################### # 平台配置项,设置当前项目所有自定义的配置 iailab: info: base-package: com.iailab.module captcha: timeout: 5m width: 160 height: 60 enable: true