Commit c49817a2 c49817a2775baa55954f5bd60cb7c11800be8272 by 文鑫

获取用户信息接口调整

1 parent 33ef2583
......@@ -18,7 +18,16 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.36</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
......
package com.topdraw.dockingapi;
import com.dtflys.forest.springboot.annotation.ForestScan;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@Slf4j
@ForestScan("com.topdraw.dockingapi.http")
public class DockingApiApplication {
public static void main(String[] args) {
......
package com.topdraw.dockingapi.config;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dtflys.forest.annotation.MethodLifeCycle;
import com.dtflys.forest.annotation.RequestAttributes;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.http.ForestResponse;
import com.dtflys.forest.lifecycles.MethodAnnotationLifeCycle;
import com.dtflys.forest.reflection.ForestMethod;
import com.topdraw.dockingapi.util.DecryptUtils;
import lombok.SneakyThrows;
import java.lang.annotation.*;
/**
* @author wenxin
* @version 1.0
* @date 2024/5/23 下午5:45
*/
@Documented
@MethodLifeCycle(Decode.DecodeLifeCycle.class)
@RequestAttributes
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
public @interface Decode {
String value();
class DecodeLifeCycle implements MethodAnnotationLifeCycle<Decode, Object> {
private String key;
@Override
public void onMethodInitialized(ForestMethod method, Decode annotation) {
this.key = annotation.value();
}
@SneakyThrows
@Override
public void onSuccess(Object data, ForestRequest request, ForestResponse response) {
String value = (String) getAttribute(request, "value");
String content = response.getContent();
ForestMethod<?> method = request.getMethod();
Class<?> returnClass = method.getReturnClass();
JSONObject responseData = JSON.parseObject(content, JSONObject.class);
if (StrUtil.isNotBlank(responseData.getString("errcode"))) {
data = BeanUtil.copyProperties(responseData, returnClass);
} else {
String encrypt = responseData.getString("encrypt");
String bodyJson = DecryptUtils.decode(value, encrypt);
data = BeanUtil.copyProperties(JSONObject.parseObject(bodyJson), returnClass);
}
response.setResult(data);
}
}
}
package com.topdraw.dockingapi.config;
import com.alibaba.fastjson.JSON;
import com.dtflys.forest.annotation.MethodLifeCycle;
import com.dtflys.forest.annotation.RequestAttributes;
import com.dtflys.forest.http.ForestBody;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.lifecycles.MethodAnnotationLifeCycle;
import com.dtflys.forest.reflection.ForestMethod;
import com.topdraw.dockingapi.entity.EncryptEntity;
import com.topdraw.dockingapi.util.DecryptUtils;
import lombok.SneakyThrows;
import java.lang.annotation.*;
import java.lang.reflect.Parameter;
/**
* 用Forest自定义注解实现一个自定义的签名加密注解
* 凡用此接口修饰的方法或接口,其对应的所有请求都会执行自定义的签名加密过程
* 而自定义的签名加密过程,由这里的@MethodLifeCycle注解指定的生命周期类进行处理
* 可以将此注解用在接口类和方法上
*/
@Documented
@MethodLifeCycle(Encode.EncodeLifeCycle.class)
@RequestAttributes
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
public @interface Encode {
String value() default "";
class EncodeLifeCycle implements MethodAnnotationLifeCycle<Encode, Object> {
/**
* 当方法调用时调用此方法,此时还没有执行请求发送
* 此方法可以获得请求对应的方法调用信息,以及动态传入的方法调用参数列表
*/
@SneakyThrows
@Override
public void onInvokeMethod(ForestRequest request, ForestMethod method, Object[] args) {
String value = (String) getAttribute(request, "value");
for (Object arg : args) {
Parameter[] parameters = method.getMethod().getParameters();
for (int i = 0; i < parameters.length; i++) {
Encode annotation = parameters[i].getAnnotation(Encode.class);
if (annotation != null) {
Object body = args[i];
String jsonString = JSON.toJSONString(body);
String encode = DecryptUtils.encode(value, jsonString);
EncryptEntity encodeBody = new EncryptEntity(encode);
ForestBody forestBody = request.getBody();
//替换body
forestBody.clear();
request.addBody(encodeBody);
}
}
}
}
@Override
public void onMethodInitialized(ForestMethod method, Encode annotation) {
}
}
}
package com.topdraw.dockingapi.controller;
import cn.hutool.core.map.MapBuilder;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.digital.szzz.gateway.sdk.api.GatewaySender;
import com.digital.szzz.gateway.sdk.bean.GatewayResponse;
import com.topdraw.dockingapi.config.EnvConfiguration;
import com.topdraw.dockingapi.entity.DecodeBody;
import com.topdraw.dockingapi.http.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
......@@ -12,9 +15,11 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* 对接愉快办接口
*
* @author wenxin
* @version 1.0
* @date 2024/5/9 下午5:46
......@@ -27,6 +32,8 @@ import java.util.Map;
public class ApiController {
private final EnvConfiguration configuration;
private final UserService userService;
String url;
String appId;
String iv;
......@@ -46,6 +53,7 @@ public class ApiController {
/**
* 获取jsToken
*
* @return token信息
*/
@PostMapping("/js/token")
......@@ -55,11 +63,15 @@ public class ApiController {
JSONObject param = new JSONObject();
param.put("appId", appId);
Map<String, String> headerMap = new HashMap<>();
return GatewaySender.send(url, appId, method, iv, appKey, appSecretKey, param, authCode, headerMap, readTimeout, connTimeout);
return GatewaySender.send(url, appId, method,
iv, appKey, appSecretKey,
param, authCode, headerMap,
readTimeout, connTimeout);
}
/**
* 获取authCode
*
* @param data 用户id
* @return authCode信息
*/
......@@ -72,21 +84,46 @@ public class ApiController {
param.put("userId", userId);
param.put("forceScopes", new JSONArray().fluentAdd("ykb_user_info").fluentAdd("ykb_divide"));
Map<String, String> headerMap = new HashMap<>();
return GatewaySender.send(url, appId, method, iv, appKey, appSecretKey, param, "", headerMap, readTimeout, connTimeout);
return GatewaySender.send(url, appId, method,
iv, appKey, appSecretKey,
param, "", headerMap,
readTimeout, connTimeout);
}
/**
* 获取用户脱敏信息
*
* @param data 认证code
* @return 用户信息
*/
@PostMapping("/user/info")
public GatewayResponse getUserInfoByAuthCode(@RequestBody JSONObject data) {
String method = "app.ykb.uc.oauth.userInfo";
JSONObject param = new JSONObject();
String authCode = data.getString("authCode");
param.put("authCode", authCode);
Map<String, String> headerMap = new HashMap<>();
return GatewaySender.send(url, appId, method, iv, appKey, appSecretKey, param, authCode, headerMap, readTimeout, connTimeout);
public DecodeBody<Object> getUserInfoByAuthCode(@RequestBody JSONObject data) {
//通过authCode获取用户手机号
JSONObject userInfoByAuthCode = userService.getUserInfoByAuthCode(data);
String phone = Optional.ofNullable(userInfoByAuthCode)
.map(t -> t.getJSONObject("data"))
.map(t -> t.getJSONObject("userInfo"))
.map(t -> t.getString("phone"))
.orElseThrow(() -> new RuntimeException("获取用户信息失败"));
//判断手机号是否已经注册
DecodeBody<JSONObject> register = userService.checkIsRegisterByPhoneNum(MapBuilder.<String, String>create()
.put("phone", phone).build());
String isRegister = register.getData().getString("isRegister");
if ("0".equals(isRegister)) {
//注册用户
DecodeBody<Object> body = userService.privateRegister(MapBuilder.<String, String>create()
.put("phone", phone)
.put("token", "")
.build());
String errcode = body.getErrcode();
if (!"0".equals(errcode)) {
//注册失败直接返回失败信息
return body;
}
}
//获取用户信息
return userService.privateLogin(MapBuilder.<String, String>create()
.put("phone", phone)
.put("token", "").build());
}
}
......
package com.topdraw.dockingapi.entity;
import lombok.Data;
/**
* @author wenxin
* @version 1.0
* @date 2024/5/23 下午5:57
*/
@Data
public class DecodeBody<T> {
private String errcode;
private String errmsg;
private T data;
}
package com.topdraw.dockingapi.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @author wenxin
* @version 1.0
* @date 2024/5/23 下午5:24
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class EncryptEntity {
private String encrypt;
}
package com.topdraw.dockingapi.http;
import com.alibaba.fastjson.JSONObject;
import com.dtflys.forest.annotation.BaseRequest;
import com.dtflys.forest.annotation.Body;
import com.dtflys.forest.annotation.Post;
import com.topdraw.dockingapi.config.Decode;
import com.topdraw.dockingapi.config.Encode;
import com.topdraw.dockingapi.entity.DecodeBody;
import java.util.Map;
/**
* @author wenxin
* @version 1.0
* @date 2024/5/23 下午4:36
*/
@BaseRequest(
baseURL = "{baseUrl}", // 默认域名
headers = {
"Content-Type:application/json" // 默认请求头
},
sslProtocol = "TLS" // 默认单向SSL协议
)
public interface UserService {
/**
* 检查指定手机号是否注册
* 请求体明文
* {
* "phoneNum": "15211111111"
* }
*
* @param body 密文 key是encrypt value是加密以后的密码
* @return isRegister(是否已注册,0:未注册;1:已注册)
*/
@Post("/cpc-ms-service-user/user/yk/checkIsRegisterByPhoneNum")
@Encode("${key}")
@Decode("${key}")
DecodeBody<JSONObject> checkIsRegisterByPhoneNum(@Body @Encode Map<String,?> body);
/**
* 用户注册
* 请求体明文
* {
* "phoneNum":"15211111111",
* "token":"333333333"
* }
*
* @param body 密文 key是encrypt value是加密以后的密码
* @return errcode(错误代码,0:成功;非0:失败)
*/
@Post("/cpc-ms-service-user/user/yk/privateRegister")
@Encode("${key}")
@Decode("${key}")
DecodeBody<Object> privateRegister(@Body @Encode Map<String,?> body);
/**
* 用户登录获取用户信息
* 请求体明文
* {
* "phoneNum":"15211111111",
* "token":"2313123123"
* }
*
* @param body 密文 key是encrypt value是加密以后的密码
* @return 用户信息
*/
@Post("/cpc-ms-service-user/login/yk/privateLogin")
@Encode("${key}")
@Decode("${key}")
DecodeBody<Object> privateLogin(@Body @Encode Map<String,?> body);
/**
* 获取用户手机号码
*
* @param body authCode
* @return 手机号
*/
@Post("/cpc-ms-service-user/user/yk/getUserInfoByAuthCode")
JSONObject getUserInfoByAuthCode(@Body JSONObject body);
}
package com.topdraw.dockingapi.util;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.Key;
/**
* 加解密工具
*
* @author chenwl
* @date 2020-11-08 12:36:47
*/
public abstract class DecryptUtils {
private static final Logger log = LoggerFactory.getLogger(DecryptUtils.class);
private static final String TRANSFORMATION_DES = "DES/ECB/PKCS5Padding";
private static final String ALGORITHM_DES = "DES";
/**
* DES 加密
*
* @param key 密钥,长度不能够小于8位字节
* @param plainText 明文
* @return 密文
*/
public static String encode(String key, String plainText) throws Exception {
return encode(key, plainText.getBytes());
}
/**
* DES 加密
*
* @param key 密钥,长度不能够小于8位字节
* @param plainBytes 明文字节数组
* @return 密文
*/
public static String encode(String key, byte[] plainBytes) throws Exception {
DESKeySpec dks = new DESKeySpec(key.getBytes());
SecretKeyFactory skf = SecretKeyFactory.getInstance(ALGORITHM_DES);
Key secretKey = skf.generateSecret(dks);
Cipher cipher = Cipher.getInstance(TRANSFORMATION_DES);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] bytes = cipher.doFinal(plainBytes);
return new String(Base64.encodeBase64(bytes));
}
/**
* 解密
*
* @param key 密钥,长度不能够小于8位字节
* @param cipherText 密文
* @return 明文
*/
public static String decode(String key, String cipherText) throws Exception {
return decode(key, cipherText.getBytes());
}
/**
* DES 解密
*
* @param key 密钥,长度不能够小于8位字节
* @param cipherBytes 密文字节数组
* @return 明文
*/
private static String decode(String key, byte[] cipherBytes) throws Exception {
DESKeySpec dks = new DESKeySpec(key.getBytes());
SecretKeyFactory skf = SecretKeyFactory.getInstance(ALGORITHM_DES);
Key secretKey = skf.generateSecret(dks);
Cipher cipher = Cipher.getInstance(TRANSFORMATION_DES);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.decodeBase64(cipherBytes)));
}
}
package com.topdraw.dockingapi.util;
/**
* @author wenxin
* @version 1.0
* @date 2024/5/23 下午5:00
*/
public class DesUtil {
}
......@@ -4,4 +4,11 @@ interface:
appIv: kfalxHTE3Np6PbQbbZhLnA==
appKey: Rd+oZa5bYfhiNK9O0dc0ng==
appSecret: x6en8WE3
url: http://23.210.52.243:44207/gateway/open/api/do
\ No newline at end of file
url: http://23.210.52.243:44207/gateway/open/api/do
forest:
variables:
baseUrl: https://cpcapi.cbg.cn/
key: wmsj2021
server:
port: 7078
\ No newline at end of file
......
......@@ -4,4 +4,10 @@ interface:
appIv: ynDi62eQ8WKQtZhUMUqTmQ==
appKey: y4pBvyDY8cgSNsxukn0DlQ==
appSecret: 04t5li0m
url: https://ykbapp-test.cqdcg.com:1443/gateway/open/api/do
\ No newline at end of file
url: https://ykbapp-test.cqdcg.com:1443/gateway/open/api/do
forest:
variables:
baseUrl: https://cpcapi.cbg.cn/
key: wmsj2021
server:
port: 7077
\ No newline at end of file
......
# 应用服务 WEB 访问端口
server.port=7078
# 指定启动的时候springboot的激活环境
spring.profiles.active=test
......
forest:
backend: okhttp3 # 后端HTTP框架(默认为 okhttp3)
max-connections: 1000 # 连接池最大连接数(默认为 500)
max-route-connections: 500 # 每个路由的最大连接数(默认为 500)
max-request-queue-size: 100 # [自v1.5.22版本起可用] 最大请求等待队列大小
max-async-thread-size: 300 # [自v1.5.21版本起可用] 最大异步线程数
max-async-queue-size: 16 # [自v1.5.22版本起可用] 最大异步线程池队列大小
timeout: 3000 # [已不推荐使用] 请求超时时间,单位为毫秒(默认为 3000)
connect-timeout: 3000 # 连接超时时间,单位为毫秒(默认为 timeout)
read-timeout: 3000 # 数据读取超时时间,单位为毫秒(默认为 timeout)
max-retry-count: 0 # 请求失败后重试次数(默认为 0 次不重试)
ssl-protocol: TLS # 单向验证的HTTPS的默认TLS协议(默认为 TLS)
log-enabled: true # 打开或关闭日志(默认为 true)
log-request: true # 打开/关闭Forest请求日志(默认为 true)
log-response-status: true # 打开/关闭Forest响应状态日志(默认为 true)
log-response-content: true # 打开/关闭Forest响应内容日志(默认为 false)
async-mode: platform # [自v1.5.27版本起可用] 异步模式(默认为 platform)
variables:
userInfoUrl: https://cpcapi.cbg.cn
\ No newline at end of file
package com.topdraw.dockingapi;
import com.topdraw.dockingapi.util.DecryptUtils;
import lombok.SneakyThrows;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.jupiter.api.Test;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
/**
* @author wenxin
* @version 1.0
* @date 2024/5/23 下午4:40
*/
public class ApiTest {
@Test
public void testMain() throws Exception {
// 添加BouncyCastle提供者
Security.addProvider(new BouncyCastleProvider());
// 密钥
byte[] keyBytes = "wmsj2021".getBytes(StandardCharsets.UTF_8); // DES密钥长度为8字节
// 需要加密的数据
String text = "{\"phoneNum\":\"15983678047\"}";
byte[] data = text.getBytes(StandardCharsets.UTF_8);
// 创建密钥
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "DES");
// 创建Cipher实例
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS7Padding", "BC");
// 初始化Cipher
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
// 执行加密
byte[] encryptedData = cipher.doFinal(data);
// 打印加密结果(通常加密后的数据进行Base64编码或者十六进制编码显示)
System.out.println("Encrypted Data: " + bytesToHex(encryptedData));
}
// 将字节转换为十六进制字符串
public static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
@SneakyThrows
@Test
public void testEncode(){
String text = "{\"phoneNum\":\"15983678047\"}";
System.out.println(DecryptUtils.encode("wmsj2021", text));
String tt="U0ELEkckqqxwzutWXbySgJmGbWQovd9n7UZSWqYt5HpXOxOsQwTnrjGp7geAhD/Mp9Jy3okXkRtt0cuFDdlMjPDgsBK3SMu0";
System.out.println(DecryptUtils.decode("wmsj2021", tt));
}
}
package com.topdraw.dockingapi;
import com.alibaba.fastjson.JSONObject;
import com.topdraw.dockingapi.http.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class DockingApiApplicationTests {
@Resource
private UserService userService;
@Test
void contextLoads() {
JSONObject param = new JSONObject();
param.put("phoneNum","15636524584");
System.out.println(userService.checkIsRegisterByPhoneNum(param));
}
}
......