diff --git a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/constant/CacheConstants.java b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/constant/CacheConstants.java index 612ff1d..da4c1a5 100644 --- a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/constant/CacheConstants.java +++ b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/constant/CacheConstants.java @@ -76,4 +76,18 @@ public class CacheConstants * 微信小程序 access_token */ public static final long WX_ACCESS_TOKEN_EXPIRATION = 100; + + /** + * 数云 access_token Key 缓存 + */ + public static final String SHUYUN_ACCESS_TOKEN_CACHE = "shuyun_access_token_cache"; + + /** + * 数云 access_token 值 HKey缓存 + */ + public static final String SHUYUN_ACCESS_TOKEN_CACHE_VALUE = "value"; + /** + * 数云 access_token 有效期 HKey缓存 + */ + public static final String SHUYUN_ACCESS_TOKEN_CACHE_EXPIRY = "expiry"; } diff --git a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/constant/SecurityConstants.java b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/constant/SecurityConstants.java index a9d58c2..c75b740 100644 --- a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/constant/SecurityConstants.java +++ b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/constant/SecurityConstants.java @@ -61,4 +61,9 @@ public class SecurityConstants * 角色权限 */ public static final String ROLE_PERMISSION = "role_permission"; + + /** + * + */ + public static final String SHUYUN_REQUEST_TIME = "Gateway-Request-Time"; } diff --git a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/domain/shuyun/AccessToken.java b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/domain/shuyun/AccessToken.java new file mode 100644 index 0000000..6feda3d --- /dev/null +++ b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/domain/shuyun/AccessToken.java @@ -0,0 +1,85 @@ +package com.flossom.common.core.domain.shuyun; + +/** + * 数云api的 accessToken 有效期为 1个月 + * 数云建议一天更新一次 + */ +public class AccessToken { + + /** + * 代表租户的授权值,可能过期的,是否过期看isOverDue + */ + private String accessToken; + + /** + * 应用ID + */ + private String appId; + + /** + * 0:代表租户级别,1:店铺授权 此字段是为了扩展业务 + */ + private String authType; + + /** + * 如果是租户级别的授权,则此值代表是哪个租户的授权 + * 如果是店铺授权,则此值代表是哪个店铺的授权 + */ + private String authValue; + + /** + * 0:代表已更新 1:代表已过期,如果是授权过期,则需要租户在页面进行手动授权,产生新accessToken + */ + private String isOverDue; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getAuthType() { + return authType; + } + + public void setAuthType(String authType) { + this.authType = authType; + } + + public String getAuthValue() { + return authValue; + } + + public void setAuthValue(String authValue) { + this.authValue = authValue; + } + + public String getIsOverDue() { + return isOverDue; + } + + public void setIsOverDue(String isOverDue) { + this.isOverDue = isOverDue; + } + + @Override + public String toString() { + return "AccessToken{" + + "accessToken='" + accessToken + '\'' + + ", appId='" + appId + '\'' + + ", authType='" + authType + '\'' + + ", authValue='" + authValue + '\'' + + ", isOverDue='" + isOverDue + '\'' + + '}'; + } +} diff --git a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/utils/http/HttpClientUtils.java b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/utils/http/HttpClientUtils.java new file mode 100644 index 0000000..97e1de8 --- /dev/null +++ b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/utils/http/HttpClientUtils.java @@ -0,0 +1,394 @@ +package com.flossom.common.core.utils.http; + + +import com.flossom.common.core.utils.StringUtils; +import org.apache.http.Consts; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.config.RequestConfig.Builder; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContextBuilder; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.conn.ssl.X509HostnameVerifier; +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.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.security.GeneralSecurityException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class HttpClientUtils { + + private static final Logger log = LoggerFactory.getLogger(HttpClientUtils.class); + + public static final int connTimeout = 10000; + public static final int readTimeout = 10000; + public static final String charset = "UTF-8"; + private static HttpClient client = null; + + static { + PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); + cm.setMaxTotal(128); + cm.setDefaultMaxPerRoute(128); + client = HttpClients.custom().setConnectionManager(cm).build(); + } + + public static String postJsonParameters(String url, String parameterStr) throws Exception { + return postJson(url, parameterStr, charset, connTimeout, readTimeout); + } + + public static String postParameters(String url, String parameterStr) throws Exception { + return post(url, parameterStr, "application/x-www-form-urlencoded", charset, connTimeout, readTimeout); + } + + public static String postParameters(String url, String parameterStr, String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception { + return post(url, parameterStr, "application/x-www-form-urlencoded", charset, connTimeout, readTimeout); + } + + public static String postParameters(String url, Map params) throws ConnectTimeoutException, + SocketTimeoutException, Exception { + return postForm(url, params, null, connTimeout, readTimeout); + } + + public static String postParameters(String url, Map params, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, + SocketTimeoutException, Exception { + return postForm(url, params, null, connTimeout, readTimeout); + } + + public static String get(String url) throws Exception { + return get(url, charset, null, null); + } + + public static String get(String url, String charset) throws Exception { + return get(url, charset, connTimeout, readTimeout); + } + + public static String getParameters(String url, Map params) throws Exception { + URIBuilder builder = new URIBuilder(url); + if (params != null) { + for (String key : params.keySet()) { + builder.addParameter(key, params.get(key)); + } + } + URI uri = builder.build(); + return get(uri.toString(), charset, null, null); + } + + /** + * 发送一个 Post 请求, 使用指定的字符集编码. + * + * @param url + * @param body RequestBody + * @param charset 编码 + * @param connTimeout 建立链接超时时间,毫秒. + * @param readTimeout 响应超时时间,毫秒. + * @return ResponseBody, 使用指定的字符集编码. + * @throws ConnectTimeoutException 建立链接超时异常 + * @throws SocketTimeoutException 响应超时 + * @throws Exception + */ + public static String postJson(String url, String body, String charset, Integer connTimeout, Integer readTimeout) throws Exception { + HttpClient client = null; + HttpPost post = new HttpPost(url); + String result = ""; + try { + if (StringUtils.isNotBlank(body)) { + HttpEntity entity = new StringEntity(body, ContentType.APPLICATION_JSON); + post.setEntity(entity); + } + + // 设置参数 + Builder customReqConf = RequestConfig.custom(); + if (connTimeout != null) { + customReqConf.setConnectTimeout(connTimeout); + } + if (readTimeout != null) { + customReqConf.setSocketTimeout(readTimeout); + } + post.setConfig(customReqConf.build()); + + HttpResponse res; + if (url.startsWith("https")) { + // 执行 Https 请求. + client = createSSLInsecureClient(); + res = client.execute(post); + } else { + // 执行 Http 请求. + client = HttpClientUtils.client; + res = client.execute(post); + } + result = EntityUtils.toString(res.getEntity(), charset); + } catch (Exception ex) { + log.error("HttpClient request error!", ex); + throw ex; + } finally { + post.releaseConnection(); + if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) { + ((CloseableHttpClient) client).close(); + } + } + return result; + } + + /** + * 发送一个 Post 请求, 使用指定的字符集编码. + * + * @param url + * @param body RequestBody + * @param mimeType 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3 + * @param charset 编码 + * @param connTimeout 建立链接超时时间,毫秒. + * @param readTimeout 响应超时时间,毫秒. + * @return ResponseBody, 使用指定的字符集编码. + * @throws ConnectTimeoutException 建立链接超时异常 + * @throws SocketTimeoutException 响应超时 + * @throws Exception + */ + public static String post(String url, String body, String mimeType, String charset, Integer connTimeout, Integer readTimeout) + throws ConnectTimeoutException, SocketTimeoutException, Exception { + HttpClient client = null; + HttpPost post = new HttpPost(url); + String result = ""; + try { + if (StringUtils.isNotBlank(body)) { + HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset)); + post.setEntity(entity); + } + // 设置参数 + Builder customReqConf = RequestConfig.custom(); + if (connTimeout != null) { + customReqConf.setConnectTimeout(connTimeout); + } + if (readTimeout != null) { + customReqConf.setSocketTimeout(readTimeout); + } + post.setConfig(customReqConf.build()); + + HttpResponse res; + if (url.startsWith("https")) { + // 执行 Https 请求. + client = createSSLInsecureClient(); + res = client.execute(post); + } else { + // 执行 Http 请求. + client = HttpClientUtils.client; + res = client.execute(post); + } + result = EntityUtils.toString(res.getEntity(), charset); + } catch (Exception ex) { + log.error("HttpClient request error!", ex); + throw ex; + } finally { + post.releaseConnection(); + if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) { + ((CloseableHttpClient) client).close(); + } + } + return result; + } + + /** + * 提交form表单 + * + * @param url + * @param params + * @param connTimeout + * @param readTimeout + * @return + * @throws ConnectTimeoutException + * @throws SocketTimeoutException + * @throws Exception + */ + public static String postForm(String url, Map params, Map headers, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, + SocketTimeoutException, Exception { + + HttpClient client = null; + HttpPost post = new HttpPost(url); + try { + if (params != null && !params.isEmpty()) { + List formParams = new ArrayList(); + Set> entrySet = params.entrySet(); + for (Entry entry : entrySet) { + formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); + } + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8); + post.setEntity(entity); + } + + if (headers != null && !headers.isEmpty()) { + for (Entry entry : headers.entrySet()) { + post.addHeader(entry.getKey(), entry.getValue()); + } + } + // 设置参数 + Builder customReqConf = RequestConfig.custom(); + if (connTimeout != null) { + customReqConf.setConnectTimeout(connTimeout); + } + if (readTimeout != null) { + customReqConf.setSocketTimeout(readTimeout); + } + post.setConfig(customReqConf.build()); + HttpResponse res = null; + if (url.startsWith("https")) { + // 执行 Https 请求. + client = createSSLInsecureClient(); + res = client.execute(post); + } else { + // 执行 Http 请求. + client = HttpClientUtils.client; + res = client.execute(post); + } + return EntityUtils.toString(res.getEntity(), charset); + } catch (Exception ex) { + log.error("HttpClient request error!", ex); + throw ex; + } finally { + post.releaseConnection(); + if (url.startsWith("https") && client != null + && client instanceof CloseableHttpClient) { + ((CloseableHttpClient) client).close(); + } + } + } + + /** + * 发送一个 GET 请求 + * + * @param url + * @param charset + * @param connTimeout 建立链接超时时间,毫秒. + * @param readTimeout 响应超时时间,毫秒. + * @return + * @throws ConnectTimeoutException 建立链接超时 + * @throws SocketTimeoutException 响应超时 + * @throws Exception + */ + public static String get(String url, String charset, Integer connTimeout, Integer readTimeout) + throws ConnectTimeoutException, SocketTimeoutException, Exception { + + HttpClient client = null; + HttpGet get = new HttpGet(url); + String result = ""; + try { + // 设置参数 + Builder customReqConf = RequestConfig.custom(); + if (connTimeout != null) { + customReqConf.setConnectTimeout(connTimeout); + } + if (readTimeout != null) { + customReqConf.setSocketTimeout(readTimeout); + } + get.setConfig(customReqConf.build()); + + HttpResponse res = null; + + if (url.startsWith("https")) { + // 执行 Https 请求. + client = createSSLInsecureClient(); + res = client.execute(get); + } else { + // 执行 Http 请求. + client = HttpClientUtils.client; + res = client.execute(get); + } + result = EntityUtils.toString(res.getEntity(), charset); + } catch (Exception ex) { + log.error("HttpClient request error!", ex); + throw ex; + } finally { + get.releaseConnection(); + if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) { + ((CloseableHttpClient) client).close(); + } + } + return result; + } + + /** + * 从 response 里获取 charset + * + * @param ressponse + * @return + */ + @SuppressWarnings("unused") + private static String getCharsetFromResponse(HttpResponse ressponse) { + // Content-Type:text/html; charset=GBK + if (ressponse.getEntity() != null && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) { + String contentType = ressponse.getEntity().getContentType().getValue(); + if (contentType.contains("charset=")) { + return contentType.substring(contentType.indexOf("charset=") + 8); + } + } + return null; + } + + /** + * 创建 SSL连接 + * + * @return + * @throws GeneralSecurityException + */ + private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException { + try { + SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return true; + } + }).build(); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() { + + @Override + public boolean verify(String arg0, SSLSession arg1) { + return true; + } + + @Override + public void verify(String host, SSLSocket ssl) + throws IOException { + } + + @Override + public void verify(String host, X509Certificate cert) + throws SSLException { + } + + @Override + public void verify(String host, String[] cns, + String[] subjectAlts) throws SSLException { + } + }); + return HttpClients.custom().setSSLSocketFactory(sslsf).build(); + } catch (GeneralSecurityException e) { + throw e; + } + } + +} diff --git a/flossom-modules/flossom-system/pom.xml b/flossom-modules/flossom-system/pom.xml index 0d15844..d4e1ce0 100644 --- a/flossom-modules/flossom-system/pom.xml +++ b/flossom-modules/flossom-system/pom.xml @@ -99,6 +99,15 @@ javase 3.5.1 + + + + com.shuyun.open + open-platform-sdk + 1.0.2 + system + ${project.basedir}/src/main/resources/lib/open-platform-sdk.jar + @@ -107,6 +116,10 @@ org.springframework.boot spring-boot-maven-plugin + + + true + diff --git a/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/shuyun/ActionMethod.java b/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/shuyun/ActionMethod.java new file mode 100644 index 0000000..7d2d4d5 --- /dev/null +++ b/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/shuyun/ActionMethod.java @@ -0,0 +1,14 @@ +package com.flossom.system.utils.shuyun; + +public class ActionMethod { + + private String accessToken; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } +} diff --git a/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/shuyun/ShuYunApiUtils.java b/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/shuyun/ShuYunApiUtils.java new file mode 100644 index 0000000..d46ec76 --- /dev/null +++ b/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/shuyun/ShuYunApiUtils.java @@ -0,0 +1,55 @@ +package com.flossom.system.utils.shuyun; + +import com.flossom.common.core.constant.CacheConstants; +import com.flossom.common.redis.service.RedisService; +import com.flossom.common.core.utils.http.HttpClientUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.ZoneId; + + +/** + * 数云工具类 + */ +@Component +public class ShuYunApiUtils { + + protected final static Logger logger = LoggerFactory.getLogger(ShuYunApiUtils.class); + + private static ShuYunConfig shuYunConfig; + + private static RedisService redisService; + + @Autowired + public void setShuYunConfig(ShuYunConfig shuYunConfig) { + ShuYunApiUtils.shuYunConfig = shuYunConfig; + } + + @Autowired + public void setRedisService(RedisService redisService) { + ShuYunApiUtils.redisService = redisService; + } + + /** + * 获取 accessToken,缓存没有就请求数云回调接口,将accessToken传递过来 + * + * @return + * @throws Exception + */ + public static String getAccessToken() throws Exception { + Integer expiryTime = redisService.getCacheMapValue(CacheConstants.SHUYUN_ACCESS_TOKEN_CACHE, CacheConstants.SHUYUN_ACCESS_TOKEN_CACHE_VALUE); + if (expiryTime == null || LocalDateTime.now().atZone(ZoneId.systemDefault()).toEpochSecond() > expiryTime) { + String accessTokenUrl = StringUtils.replace(shuYunConfig.getActionMethod().getAccessToken(), "{appid}", shuYunConfig.getAppid()); + logger.info("刷新accessToken地址:{}", accessTokenUrl); + String result = HttpClientUtils.get(accessTokenUrl); + logger.info("请求刷新accessToken结果:{}", result); + } + return redisService.getCacheMapValue(CacheConstants.SHUYUN_ACCESS_TOKEN_CACHE, CacheConstants.SHUYUN_ACCESS_TOKEN_CACHE_VALUE); + } + +} diff --git a/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/shuyun/ShuYunConfig.java b/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/shuyun/ShuYunConfig.java new file mode 100644 index 0000000..81da0a0 --- /dev/null +++ b/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/shuyun/ShuYunConfig.java @@ -0,0 +1,66 @@ +package com.flossom.system.utils.shuyun; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +/** + * 数云接口配置 + * + * @author flossom + */ +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "shuyun") +public class ShuYunConfig { + + /** + * 接口地址 + */ + private String url; + + /** + * 数云 appid + */ + private String appid; + + /** + * 数云 security + */ + private String security; + + private ActionMethod actionMethod; + + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getSecurity() { + return security; + } + + public void setSecurity(String security) { + this.security = security; + } + + public ActionMethod getActionMethod() { + return actionMethod; + } + + public void setActionMethod(ActionMethod actionMethod) { + this.actionMethod = actionMethod; + } +} diff --git a/flossom-modules/flossom-system/src/main/resources/lib/open-platform-sdk.jar b/flossom-modules/flossom-system/src/main/resources/lib/open-platform-sdk.jar new file mode 100644 index 0000000..6230e1a Binary files /dev/null and b/flossom-modules/flossom-system/src/main/resources/lib/open-platform-sdk.jar differ diff --git a/flossom-modules/flossom-system/src/main/resources/shuyun.properties b/flossom-modules/flossom-system/src/main/resources/shuyun.properties new file mode 100644 index 0000000..cd255ba --- /dev/null +++ b/flossom-modules/flossom-system/src/main/resources/shuyun.properties @@ -0,0 +1,3 @@ +http-pool.connect-timeout=60000 +http-pool.connection-request-timeout=60000 +http-pool.socket-timeout=60000 \ No newline at end of file