From 64abc8d59a20da627329f673d9f52547fe922c3d Mon Sep 17 00:00:00 2001 From: caiyuchao Date: Thu, 21 Nov 2024 18:23:14 +0800 Subject: [PATCH] feat: integrated omada api --- docker/start-frontend.sh | 1 + pom.xml | 33 ++++ wfc-api/pom.xml | 1 + wfc-api/wfc-api-omada/pom.xml | 30 ++++ .../org/wfc/omada/api/RemoteOmadaFeign.java | 22 +++ .../org/wfc/omada/api/config/FeignConfig.java | 101 ++++++++++++ .../omada/api/config/FeignHttpsConfig.java | 55 +++++++ .../wfc/omada/api/config/OmadaProperties.java | 21 +++ .../omada/api/config/RestTemplateConfig.java | 153 ++++++++++++++++++ .../omada/api/domain/vo/AuthorizeTokenVO.java | 23 +++ .../wfc/omada/api/domain/vo/OmadaResult.java | 22 +++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../src/main/resources/application.yml | 6 + wfc-common/wfc-common-core/pom.xml | 12 ++ 14 files changed, 481 insertions(+) create mode 100644 wfc-api/wfc-api-omada/pom.xml create mode 100644 wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/RemoteOmadaFeign.java create mode 100644 wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/FeignConfig.java create mode 100644 wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/FeignHttpsConfig.java create mode 100644 wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/OmadaProperties.java create mode 100644 wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/RestTemplateConfig.java create mode 100644 wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/domain/vo/AuthorizeTokenVO.java create mode 100644 wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/domain/vo/OmadaResult.java create mode 100644 wfc-api/wfc-api-omada/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 wfc-api/wfc-api-omada/src/main/resources/application.yml diff --git a/docker/start-frontend.sh b/docker/start-frontend.sh index 300a715..567db7a 100755 --- a/docker/start-frontend.sh +++ b/docker/start-frontend.sh @@ -2,6 +2,7 @@ cd ../../fe.wfc/ git pull +pnpm i pnpm build cd ../be.wfc/docker/ ./copy.sh diff --git a/pom.xml b/pom.xml index 1108e2b..e4079d2 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,8 @@ 8.2.2 4.1.2 2.14.4 + 5.8.33 + 1.18.36 @@ -152,6 +154,20 @@ ${transmittable-thread-local.version} + + + cn.hutool + hutool-all + ${hutool.version} + + + + + org.projectlombok + lombok + ${lombok.version} + + org.wfc @@ -222,6 +238,13 @@ ${wfc.version} + + + org.wfc + wfc-api-omada + ${wfc.version} + + @@ -340,5 +363,15 @@ wfc-prod + + + cyc + + + cyc + 192.168.2.248:8848 + wfc-cyc + + \ No newline at end of file diff --git a/wfc-api/pom.xml b/wfc-api/pom.xml index 1b57099..664960e 100644 --- a/wfc-api/pom.xml +++ b/wfc-api/pom.xml @@ -10,6 +10,7 @@ wfc-api-system + wfc-api-omada wfc-api diff --git a/wfc-api/wfc-api-omada/pom.xml b/wfc-api/wfc-api-omada/pom.xml new file mode 100644 index 0000000..636c3d9 --- /dev/null +++ b/wfc-api/wfc-api-omada/pom.xml @@ -0,0 +1,30 @@ + + + org.wfc + wfc-api + 3.6.4 + + 4.0.0 + + wfc-api-omada + + + wfc-api-omada接口模块 + + + + + + + org.wfc + wfc-common-core + + + + org.wfc + wfc-common-redis + + + + diff --git a/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/RemoteOmadaFeign.java b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/RemoteOmadaFeign.java new file mode 100644 index 0000000..09abcdb --- /dev/null +++ b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/RemoteOmadaFeign.java @@ -0,0 +1,22 @@ +package org.wfc.omada.api; + + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.wfc.omada.api.config.FeignHttpsConfig; +import org.wfc.omada.api.config.FeignConfig; + +/** + * @description: Omada接口 + * @author: caiyuchao + * @date: 2024-11-20 + */ +@FeignClient(name = "remoteOmadaFeign",url = "${omada.omada-url}", configuration = {FeignConfig.class, FeignHttpsConfig.class}) +public interface RemoteOmadaFeign { + + @RequestMapping(value = "/openapi/v1/${omada.omadac-id}/sites", method = RequestMethod.GET) + Object getData(@RequestParam("page") Integer page, @RequestParam("pageSize") Integer pageSize); + +} diff --git a/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/FeignConfig.java b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/FeignConfig.java new file mode 100644 index 0000000..3e6464f --- /dev/null +++ b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/FeignConfig.java @@ -0,0 +1,101 @@ +package org.wfc.omada.api.config; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson2.JSON; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; +import org.wfc.common.redis.service.RedisService; +import org.wfc.omada.api.domain.vo.AuthorizeTokenVO; +import org.wfc.omada.api.domain.vo.OmadaResult; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * @description: Feign 配置 + * @author: caiyuchao + * @date: 2024-11-21 + */ +@Configuration +public class FeignConfig implements RequestInterceptor { + + @Autowired + private RedisService redisService; + + @Autowired + private OmadaProperties omadaProperties; + + private static final String REDIS_ACCESS_TOKEN = "wfc-api-omada:access-token"; + private static final String REDIS_REFRESH_TOKEN = "wfc-api-omada:refresh-token"; + + private static final String OMADACID = "omadacId"; + private static final String CLIENT_ID = "client_id"; + private static final String CLIENT_SECRET = "client_secret"; + private static final String GRANT_TYPE = "grant_type"; + private static final String PRE_ACCESS_TOKEN = "AccessToken="; + private static final String REFRESH_TOKEN = "refresh_token"; + private static final String AUTHORIZATION = "Authorization"; + private static final String CLIENT_CREDENTIALS = "client_credentials"; + private static final String OPENAPI_AUTHORIZE_TOKEN_PATH = "/openapi/authorize/token"; + + @Override + public void apply(RequestTemplate requestTemplate) { + // 获取redis中的访问令牌 + String cacheAccessToken = redisService.getCacheObject(REDIS_ACCESS_TOKEN); + String authorization; + if (StrUtil.isBlank(cacheAccessToken)) { + // 访问令牌不存在获取redis中的刷新令牌 + String cacheRefreshToken = redisService.getCacheObject(REDIS_REFRESH_TOKEN); + // 构造请求参数query和body + Map body = new HashMap<>(); + body.put(CLIENT_ID, omadaProperties.getClientId()); + body.put(CLIENT_SECRET, omadaProperties.getClientSecret()); + LinkedMultiValueMap query = new LinkedMultiValueMap<>(); + String url = omadaProperties.getOmadaUrl() + OPENAPI_AUTHORIZE_TOKEN_PATH; + if (StrUtil.isBlank(cacheRefreshToken)) { + // 获取访问令牌 + // 调用客户端认证模式获取access token + body.put(OMADACID, omadaProperties.getOmadacId()); + query.add(GRANT_TYPE, CLIENT_CREDENTIALS); + } else { + // 刷新访问令牌 + query.add(GRANT_TYPE, REFRESH_TOKEN); + query.add(REFRESH_TOKEN, cacheRefreshToken); + } + HttpEntity request = new HttpEntity<>(JSON.toJSONString(body)); + String uriString = UriComponentsBuilder.fromHttpUrl(url).queryParams(query).build().toUriString(); + // 发送调用请求 + RestTemplate restTemplate = new RestTemplate(new RestTemplateConfig.HttpsClientRequestFactory()); + ResponseEntity> responseEntity = restTemplate.exchange(uriString, HttpMethod.POST, + request, new ParameterizedTypeReference>() { + }); + OmadaResult omadaResult = responseEntity.getBody(); + if (omadaResult == null) { + return; + } + String accessToken = omadaResult.getResult().getAccessToken(); + String refreshToken = omadaResult.getResult().getRefreshToken(); + authorization = PRE_ACCESS_TOKEN + accessToken; + // 保存访问令牌和刷新令牌到redis中 + redisService.setCacheObject(REDIS_ACCESS_TOKEN, accessToken, 7000L, TimeUnit.SECONDS); + redisService.setCacheObject(REDIS_REFRESH_TOKEN, refreshToken, 13L, TimeUnit.DAYS); + } else { + authorization = PRE_ACCESS_TOKEN + cacheAccessToken; + } + if (StrUtil.isNotBlank(authorization)) { + // 添加授权请求头 + requestTemplate.header(AUTHORIZATION, authorization); + } + } + +} \ No newline at end of file diff --git a/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/FeignHttpsConfig.java b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/FeignHttpsConfig.java new file mode 100644 index 0000000..7af777d --- /dev/null +++ b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/FeignHttpsConfig.java @@ -0,0 +1,55 @@ +package org.wfc.omada.api.config; + +import feign.Client; +import feign.Feign; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * @description: Feign https 配置 + * @author: caiyuchao + * @date: 2024-11-21 + */ +@Configuration +public class FeignHttpsConfig { + + + @Bean + @Scope("prototype") + public Feign.Builder feignBuilder() { + return Feign.builder(); + } + + @Bean + public Client generateClient() { + try { + SSLContext ctx = SSLContext.getInstance("SSL"); + X509TrustManager tm = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + ctx.init(null, new TrustManager[]{tm}, null); + return new Client.Default(ctx.getSocketFactory(), (hostname, session) -> true); + } catch (Exception e) { + return null; + } + } + +} \ No newline at end of file diff --git a/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/OmadaProperties.java b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/OmadaProperties.java new file mode 100644 index 0000000..5a38425 --- /dev/null +++ b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/OmadaProperties.java @@ -0,0 +1,21 @@ +package org.wfc.omada.api.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @description: omada配置属性 + * @author: caiyuchao + * @date: 2024-11-21 + */ +@Data +@Component +@ConfigurationProperties(prefix = "omada") +public class OmadaProperties { + + private String omadaUrl; + private String omadacId; + private String clientId; + private String clientSecret; +} diff --git a/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/RestTemplateConfig.java b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/RestTemplateConfig.java new file mode 100644 index 0000000..16886ea --- /dev/null +++ b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/config/RestTemplateConfig.java @@ -0,0 +1,153 @@ +package org.wfc.omada.api.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; + +/** + * @description: RestTemplate配置 + * @author: caiyuchao + * @date: 2024-11-20 + */ +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); + return restTemplate; + } + + //支持https + public static class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory { + + @Override + protected void prepareConnection(HttpURLConnection connection, String httpMethod) { + try { + if (!(connection instanceof HttpsURLConnection)) { + throw new RuntimeException("An instance of HttpsURLConnection is expected"); + } + + HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; + + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + + } + }; + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); + + httpsConnection.setHostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String s, SSLSession sslSession) { + return true; + } + }); + + super.prepareConnection(httpsConnection, httpMethod); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // SSLSocketFactory用于创建 SSLSockets + private static class MyCustomSSLSocketFactory extends SSLSocketFactory { + + private final SSLSocketFactory delegate; + + public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { + this.delegate = delegate; + } + + // 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。 + // 这些默认的服务的最低质量要求保密保护和服务器身份验证 + @Override + public String[] getDefaultCipherSuites() { + return delegate.getDefaultCipherSuites(); + } + + // 返回的密码套件可用于SSL连接启用的名字 + @Override + public String[] getSupportedCipherSuites() { + return delegate.getSupportedCipherSuites(); + } + + + @Override + public Socket createSocket(final Socket socket, final String host, final int port, + final boolean autoClose) throws IOException { + final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose); + return overrideProtocol(underlyingSocket); + } + + + @Override + public Socket createSocket(final String host, final int port) throws IOException { + final Socket underlyingSocket = delegate.createSocket(host, port); + return overrideProtocol(underlyingSocket); + } + + @Override + public Socket createSocket(final String host, final int port, final InetAddress localAddress, + final int localPort) throws + IOException { + final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); + return overrideProtocol(underlyingSocket); + } + + @Override + public Socket createSocket(final InetAddress host, final int port) throws IOException { + final Socket underlyingSocket = delegate.createSocket(host, port); + return overrideProtocol(underlyingSocket); + } + + @Override + public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, + final int localPort) throws + IOException { + final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); + return overrideProtocol(underlyingSocket); + } + + private Socket overrideProtocol(final Socket socket) { + if (!(socket instanceof SSLSocket)) { + throw new RuntimeException("An instance of SSLSocket is expected"); + } + ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.2"}); + return socket; + } + } + } +} diff --git a/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/domain/vo/AuthorizeTokenVO.java b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/domain/vo/AuthorizeTokenVO.java new file mode 100644 index 0000000..93d9415 --- /dev/null +++ b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/domain/vo/AuthorizeTokenVO.java @@ -0,0 +1,23 @@ +package org.wfc.omada.api.domain.vo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @description: 授权码VO + * @author: caiyuchao + * @date: 2024-11-21 + */ +@Data +public class AuthorizeTokenVO implements Serializable { + private static final long serialVersionUID = 1L; + + private String accessToken; + + private String refreshToken; + + private String tokenType; + + private Integer expiresIn; +} diff --git a/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/domain/vo/OmadaResult.java b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/domain/vo/OmadaResult.java new file mode 100644 index 0000000..0e0c9be --- /dev/null +++ b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/domain/vo/OmadaResult.java @@ -0,0 +1,22 @@ +package org.wfc.omada.api.domain.vo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @description: 结果VO + * @author: caiyuchao + * @date: 2024-11-21 + */ +@Data +public class OmadaResult implements Serializable { + + private static final long serialVersionUID = 1L; + + private Integer errorCode; + + private String msg; + + private T result; +} diff --git a/wfc-api/wfc-api-omada/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/wfc-api/wfc-api-omada/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..943069e --- /dev/null +++ b/wfc-api/wfc-api-omada/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.wfc.omada.api.config.OmadaProperties \ No newline at end of file diff --git a/wfc-api/wfc-api-omada/src/main/resources/application.yml b/wfc-api/wfc-api-omada/src/main/resources/application.yml new file mode 100644 index 0000000..f74e073 --- /dev/null +++ b/wfc-api/wfc-api-omada/src/main/resources/application.yml @@ -0,0 +1,6 @@ +# Omada 配置 +omada: + omada-url: 'https://192.168.2.248:8043' + omadac-id: 'f3aa6e479b94222581523710cc2c2a9d' + client-id: '5036e77c81a74008821c694a715fe2b8' + client-secret: '29faa06fb7f244b094377b48eb3083a7' diff --git a/wfc-common/wfc-common-core/pom.xml b/wfc-common/wfc-common-core/pom.xml index d57f2c6..f5689fe 100644 --- a/wfc-common/wfc-common-core/pom.xml +++ b/wfc-common/wfc-common-core/pom.xml @@ -113,6 +113,18 @@ swagger-annotations + + + cn.hutool + hutool-all + + + + + org.projectlombok + lombok + +