feat: integrated omada api
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
cd ../../fe.wfc/
|
||||
git pull
|
||||
pnpm i
|
||||
pnpm build
|
||||
cd ../be.wfc/docker/
|
||||
./copy.sh
|
||||
|
||||
33
pom.xml
33
pom.xml
@@ -35,6 +35,8 @@
|
||||
<minio.version>8.2.2</minio.version>
|
||||
<poi.version>4.1.2</poi.version>
|
||||
<transmittable-thread-local.version>2.14.4</transmittable-thread-local.version>
|
||||
<hutool.version>5.8.33</hutool.version>
|
||||
<lombok.version>1.18.36</lombok.version>
|
||||
</properties>
|
||||
|
||||
<!-- 依赖声明 -->
|
||||
@@ -152,6 +154,20 @@
|
||||
<version>${transmittable-thread-local.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool工具 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- lombok工具 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>org.wfc</groupId>
|
||||
@@ -222,6 +238,13 @@
|
||||
<version>${wfc.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- omada接口 -->
|
||||
<dependency>
|
||||
<groupId>org.wfc</groupId>
|
||||
<artifactId>wfc-api-omada</artifactId>
|
||||
<version>${wfc.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -340,5 +363,15 @@
|
||||
<nacosNamespace>wfc-prod</nacosNamespace>
|
||||
</properties>
|
||||
</profile>
|
||||
<!-- cyc -->
|
||||
<profile>
|
||||
<id>cyc</id>
|
||||
<properties>
|
||||
<!--当前环境-->
|
||||
<profileName>cyc</profileName>
|
||||
<nacosServerAddr>192.168.2.248:8848</nacosServerAddr>
|
||||
<nacosNamespace>wfc-cyc</nacosNamespace>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
<modules>
|
||||
<module>wfc-api-system</module>
|
||||
<module>wfc-api-omada</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>wfc-api</artifactId>
|
||||
|
||||
30
wfc-api/wfc-api-omada/pom.xml
Normal file
30
wfc-api/wfc-api-omada/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.wfc</groupId>
|
||||
<artifactId>wfc-api</artifactId>
|
||||
<version>3.6.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>wfc-api-omada</artifactId>
|
||||
|
||||
<description>
|
||||
wfc-api-omada接口模块
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- RuoYi Common Core-->
|
||||
<dependency>
|
||||
<groupId>org.wfc</groupId>
|
||||
<artifactId>wfc-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.wfc</groupId>
|
||||
<artifactId>wfc-common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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<String, String> body = new HashMap<>();
|
||||
body.put(CLIENT_ID, omadaProperties.getClientId());
|
||||
body.put(CLIENT_SECRET, omadaProperties.getClientSecret());
|
||||
LinkedMultiValueMap<String, String> 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<String> request = new HttpEntity<>(JSON.toJSONString(body));
|
||||
String uriString = UriComponentsBuilder.fromHttpUrl(url).queryParams(query).build().toUriString();
|
||||
// 发送调用请求
|
||||
RestTemplate restTemplate = new RestTemplate(new RestTemplateConfig.HttpsClientRequestFactory());
|
||||
ResponseEntity<OmadaResult<AuthorizeTokenVO>> responseEntity = restTemplate.exchange(uriString, HttpMethod.POST,
|
||||
request, new ParameterizedTypeReference<OmadaResult<AuthorizeTokenVO>>() {
|
||||
});
|
||||
OmadaResult<AuthorizeTokenVO> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer errorCode;
|
||||
|
||||
private String msg;
|
||||
|
||||
private T result;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.wfc.omada.api.config.OmadaProperties
|
||||
6
wfc-api/wfc-api-omada/src/main/resources/application.yml
Normal file
6
wfc-api/wfc-api-omada/src/main/resources/application.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Omada 配置
|
||||
omada:
|
||||
omada-url: 'https://192.168.2.248:8043'
|
||||
omadac-id: 'f3aa6e479b94222581523710cc2c2a9d'
|
||||
client-id: '5036e77c81a74008821c694a715fe2b8'
|
||||
client-secret: '29faa06fb7f244b094377b48eb3083a7'
|
||||
@@ -113,6 +113,18 @@
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool工具 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- lombok工具 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
Reference in New Issue
Block a user