Jelajahi Sumber

微信H5核身认证

zoujiajian 2 tahun lalu
induk
melakukan
dcae49968e

+ 6 - 0
netflix-common/pom.xml

@@ -98,6 +98,12 @@
             <artifactId>tencentcloud-sdk-java-sms</artifactId>
             <version>3.1.694</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.tencentcloudapi</groupId>
+            <artifactId>tencentcloud-sdk-java-faceid</artifactId>
+            <version>3.1.694</version>
+        </dependency>
     </dependencies>
 
 

+ 11 - 0
netflix-common/src/main/java/com/cyksj/common/util/StringUtil.java

@@ -521,4 +521,15 @@ public final class StringUtil {
 
 		return "unknown";
 	}
+
+	/**
+	 * 身份证号返回
+	 */
+	public static String deIndent(String ident) {
+		String front = ident.substring(0, 4);
+		String back = ident.substring(ident.length() - 4);
+		StringBuilder sb = new StringBuilder();
+		sb.append(front).append("****").append(back);
+		return sb.toString();
+	}
 }

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/UserCertRecordMapper.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.UserCertRecord;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserCertRecordMapper
+ * 创建者: JavaZou
+ * 创建时间:2024/2/21 10:11
+ */
+public interface UserCertRecordMapper extends BaseMapper<UserCertRecord> {
+}

+ 20 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/UserWxIndentDto.java

@@ -0,0 +1,20 @@
+package com.cyksj.model.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserWxIndentDto
+ * 创建者: JavaZou
+ * 创建时间:2024/2/21 16:00
+ */
+@Getter
+@Setter
+public class UserWxIndentDto {
+	private Long userId;
+
+	private String redirectUrl;
+
+	private String bizToken;
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/UserCertRecord.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserCert
+ * 创建者: JavaZou
+ * 创建时间:2024/2/21 10:00
+ */
+@Getter
+@Setter
+public class UserCertRecord extends BaseEntity{
+	private Long userId;
+
+	private String name;
+
+	private String sex;
+
+	private String ident;
+
+	private String front;
+
+	private String reverse;
+
+	private String addr;
+}

+ 20 - 0
netflix-dao/src/main/java/com/cyksj/model/request/UserIndentCertReq.java

@@ -0,0 +1,20 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserIndentCertReq
+ * 创建者: JavaZou
+ * 创建时间:2024/2/21 11:17
+ */
+@Getter
+@Setter
+public class UserIndentCertReq {
+	private Long userId;
+
+	private String bindState;
+
+	private String redirectUrl;
+}

+ 24 - 0
netflix-dao/src/main/java/com/cyksj/model/response/WxCertAuthTokenResp.java

@@ -0,0 +1,24 @@
+package com.cyksj.model.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserCertAuthTokenResp
+ * 创建者: JavaZou
+ * 创建时间:2024/2/21 10:16
+ */
+@Getter
+@Setter
+public class WxCertAuthTokenResp {
+	/**
+	 * 用于发起核身流程的URL,仅微信H5场景使用
+	 */
+	private String Url;
+
+	/**
+	 * 一次核身流程的标识
+	 */
+	private String BizToken;
+}

+ 3 - 1
netflix-service/src/main/java/com/cyksj/redis/RedisService.java

@@ -666,7 +666,9 @@ public class RedisService {
         CORP_CUSTOMER_ACQUISTION_UNIONID_CLICKID("corp:customer:unionId:%s","unionid 对应的clickId",60*60*24),
 
         WEEKEND_DUTY_UNDERSTAFFED_NOTIFY("weekend_duty_understaffed_notify","周末值班人员不足短信通知key",60*60),
-        DOUBLE_VERIFY_CODE_VIEW_MINUTES_KEY("double_verify_code_view_minutes_key","双重验证码查看同时间段人数限制",60)
+        DOUBLE_VERIFY_CODE_VIEW_MINUTES_KEY("double_verify_code_view_minutes_key","双重验证码查看同时间段人数限制",60),
+
+        WX_INDENT_BIND_KEY("wx_indent_bind:key:", "微信身份授权绑定key", 60 * 60 * 24),
         ;
 
         private String name;

+ 24 - 0
netflix-service/src/main/java/com/cyksj/service/user/UserIdentCertService.java

@@ -0,0 +1,24 @@
+package com.cyksj.service.user;
+
+import com.cyksj.model.dto.UserWxIndentDto;
+import com.cyksj.model.entity.UserCertRecord;
+import com.cyksj.model.request.UserIndentCertReq;
+import com.cyksj.model.response.WxCertAuthTokenResp;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserIdentCertService
+ * 创建者: JavaZou
+ * 创建时间:2024/2/21 10:21
+ */
+public interface UserIdentCertService {
+	WxCertAuthTokenResp wxH5IndentCert(UserIndentCertReq indentCertReq) throws Exception;
+
+	void wxH5IndentCertResult(UserWxIndentDto userWxIndentDto) throws Exception;
+
+	String bindWxUser(long userId);
+
+	Boolean checkCertRecord(long userId);
+
+	UserCertRecord getUserCertRecord(long userId);
+}

+ 196 - 0
netflix-service/src/main/java/com/cyksj/service/user/impl/UserIdentCertServiceImpl.java

@@ -0,0 +1,196 @@
+package com.cyksj.service.user.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.EnvCommonService;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.snowflake.Sequence;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.mapper.UserCertRecordMapper;
+import com.cyksj.mapper.UserMapper;
+import com.cyksj.model.dto.UserWxIndentDto;
+import com.cyksj.model.entity.User;
+import com.cyksj.model.entity.UserCertRecord;
+import com.cyksj.model.request.UserIndentCertReq;
+import com.cyksj.model.response.WxCertAuthTokenResp;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.market.task.BindTaskService;
+import com.cyksj.service.user.UserBindRelationService;
+import com.cyksj.service.user.UserIdentCertService;
+import com.tencentcloudapi.common.Credential;
+import com.tencentcloudapi.common.profile.ClientProfile;
+import com.tencentcloudapi.common.profile.HttpProfile;
+import com.tencentcloudapi.faceid.v20180301.FaceidClient;
+import com.tencentcloudapi.faceid.v20180301.models.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserIdentCertServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2024/2/21 10:21
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class UserIdentCertServiceImpl implements UserIdentCertService {
+	private final UserBindRelationService userBindRelationService;
+
+	private final UserCertRecordMapper userCertRecordMapper;
+
+	private final BindTaskService bindTaskService;
+
+	private final UserMapper userMapper;
+
+	private final RedisService redisService;
+
+	private final EnvCommonService envCommonService;
+
+	private final static Sequence SEQUENCE = new Sequence(0);
+
+	private static final String TENCENT_SECRET_ID = "AKIDBB3jEqR0Y95zpfTix1vd6fMqAIahc4Ka";
+
+	private static final String TENCENT_SECRET_KEY = "c6YtNm63rcQ1QdiGmEbmEPSTPF3e4KQK";
+
+	private static final String ruleId = "1";
+
+	private static final String REDIRECT_URL = "applets/user/cert/auth/result/";
+
+	@Override
+	public Boolean checkCertRecord(long userId) {
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		Integer count = userCertRecordMapper.selectCount(Wrappers.lambdaQuery(UserCertRecord.class)
+				.in(UserCertRecord::getUserId, userIdList));
+		return count > 0 ? true : false;
+	}
+
+	@Override
+	public UserCertRecord getUserCertRecord(long userId) {
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		UserCertRecord record = userCertRecordMapper.selectOne(Wrappers.lambdaQuery(UserCertRecord.class)
+				.in(UserCertRecord::getUserId, userIdList)
+				.select(UserCertRecord::getName, UserCertRecord::getIdent, UserCertRecord::getCreatedTime)
+				.last("limit 1"));
+		if (record == null) {
+			return null;
+		}
+		record.setIdent(StringUtil.deIndent(record.getIdent()));
+		return record;
+	}
+
+	@Override
+	public String bindWxUser(long userId) {
+		User user = userMapper.selectById(userId);
+		if (StrUtil.isNotBlank(user.getOpenId())) {
+			return null;
+		}
+		String state = StringUtil.randomString(6);
+		redisService.set(RedisService.key.WX_INDENT_BIND_KEY.getEnvName() + state, userId, RedisService.key.WX_INDENT_BIND_KEY.getTimeout());
+		return state;
+	}
+
+	@Override
+	public WxCertAuthTokenResp wxH5IndentCert(UserIndentCertReq indentCertReq) throws Exception {
+		Long userId = indentCertReq.getUserId();
+		String redirectUrl = indentCertReq.getRedirectUrl();
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+//		String bindState = indentCertReq.getBindState();
+//		if (userIdList.size() == 1 && StrUtil.isNotEmpty(bindState)) {
+//			User user = userMapper.selectById(userId);
+//			if (StrUtil.isEmpty(user.getOpenId())) {
+//				String key = RedisService.key.WX_INDENT_BIND_KEY.getEnvName() + bindState;
+//				Object bindInfo = redisService.get(key);
+//				if (bindInfo == null) {
+//					throw BusinessRuntimeException.getInstance("认证信息已失效,请重新操作..");
+//				}
+//				Long bindUserId = Long.parseLong(bindInfo.toString());
+//				User bindUser = userMapper.selectById(bindUserId);
+//				//绑定微信端用户
+//				UserBindInfoReq request = new UserBindInfoReq();
+//				request.setType(StrUtil.isNotBlank(bindUser.getLoginPhone()) ? 1 : 2);
+//				request.setUserId(userId);
+//				request.setAccount(request.getType() == 1 ? bindUser.getLoginPhone() : bindUser.getEmail());
+//				bindTaskService.bindOtherAccount(request);
+//				userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+//				redisService.del(key);
+//			}
+//		}
+
+		Integer count = userCertRecordMapper.selectCount(Wrappers.lambdaQuery(UserCertRecord.class)
+				.in(UserCertRecord::getUserId, userIdList));
+		if (count > 0) throw BusinessRuntimeException.getInstance("您已完成认证了..");
+
+		Credential cred = new Credential(TENCENT_SECRET_ID, TENCENT_SECRET_KEY);
+		// 实例化一个http选项,可选的,没有特殊需求可以跳过
+		HttpProfile httpProfile = new HttpProfile();
+		httpProfile.setEndpoint("faceid.tencentcloudapi.com");
+		// 实例化一个client选项,可选的,没有特殊需求可以跳过
+		ClientProfile clientProfile = new ClientProfile();
+		clientProfile.setHttpProfile(httpProfile);
+		// 实例化要请求产品的client对象,clientProfile是可选的
+		FaceidClient client = new FaceidClient(cred, "", clientProfile);
+		// 实例化一个请求对象,每个接口都会对应一个request对象
+		DetectAuthRequest req = new DetectAuthRequest();
+		req.setRuleId(ruleId);
+		Long mappingId = SEQUENCE.nextId();
+		req.setRedirectUrl(envCommonService.getHost() + REDIRECT_URL + mappingId);
+		// 返回的resp是一个DetectAuthResponse的实例,与请求对象对应
+		DetectAuthResponse resp = client.DetectAuth(req);
+
+		log.info("微信h5核身认证,返回:{}", Jsons.toJson(resp));
+
+		WxCertAuthTokenResp tokenResp = new WxCertAuthTokenResp();
+		BeanUtil.copyProperties(resp, tokenResp);
+
+		UserWxIndentDto userWxIndentDto = new UserWxIndentDto();
+		userWxIndentDto.setUserId(userId);
+		userWxIndentDto.setRedirectUrl(redirectUrl);
+		userWxIndentDto.setBizToken(tokenResp.getBizToken());
+		redisService.set(mappingId.toString(), Jsons.toJson(userWxIndentDto), 60 * 60 * 24L);
+		return tokenResp;
+	}
+
+	@Override
+	public void wxH5IndentCertResult(UserWxIndentDto userWxIndentDto) throws Exception {
+		Long userId = userWxIndentDto.getUserId();
+		Credential cred = new Credential(TENCENT_SECRET_ID, TENCENT_SECRET_KEY);
+		// 实例化一个http选项,可选的,没有特殊需求可以跳过
+		HttpProfile httpProfile = new HttpProfile();
+		httpProfile.setEndpoint("faceid.tencentcloudapi.com");
+		// 实例化一个client选项,可选的,没有特殊需求可以跳过
+		ClientProfile clientProfile = new ClientProfile();
+		clientProfile.setHttpProfile(httpProfile);
+		// 实例化要请求产品的client对象,clientProfile是可选的
+		FaceidClient client = new FaceidClient(cred, "", clientProfile);
+		// 实例化一个请求对象,每个接口都会对应一个request对象
+		GetDetectInfoEnhancedRequest req = new GetDetectInfoEnhancedRequest();
+		req.setBizToken(userWxIndentDto.getBizToken());
+		req.setRuleId(ruleId);
+		// 返回的resp是一个GetDetectInfoEnhancedResponse的实例,与请求对象对应
+		GetDetectInfoEnhancedResponse resp = client.GetDetectInfoEnhanced(req);
+		DetectInfoText text = resp.getText();
+		if (text.getErrCode() == 0) {
+			UserCertRecord certRecord = new UserCertRecord();
+			String ocrIdCard = text.getOcrIdCard();
+			Integer count = userCertRecordMapper.selectCount(Wrappers.lambdaQuery(UserCertRecord.class)
+					.eq(UserCertRecord::getIdent, ocrIdCard));
+			if (count > 0) {
+				throw BusinessRuntimeException.getInstance("该身份证号已被其他账号绑定");
+			}
+			if (count == 0) {
+				certRecord.setIdent(text.getOcrIdCard());
+				certRecord.setName(text.getOcrName());
+				certRecord.setSex(text.getOcrGender());
+				certRecord.setUserId(userId);
+				certRecord.setAddr(text.getOcrAddress());
+				userCertRecordMapper.insert(certRecord);
+			}
+		}
+	}
+}

+ 2 - 0
netflix-service/src/main/java/com/cyksj/service/user/impl/UserServiceImpl.java

@@ -117,6 +117,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 
 	private final UserDistributeSharedMapper userDistributeSharedMapper;
 
+	private final UserCertRecordMapper userCertRecordMapper;
+
 	private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
 
 	@Override

+ 0 - 1
netflix-web/src/main/java/com/cyksj/web/controller/user/DistributePopularizeController.java

@@ -135,7 +135,6 @@ public class DistributePopularizeController {
 		}
 		DistributePopularizeView distributePopularizeView = new DistributePopularizeView();
 		String dsUrl = String.format(envCommonService.DISTRIBUTE_DEFAULT_URL_PREFIX, user.getShowId());
-//		distributePopularizeView.setUrl(String.format("https://%s/8081/api/auth/weChat?url=https://%s/yinhe/web&sharedId=%s", domain, domain, userId));
 		distributePopularizeView.setUrl(dsUrl);
 		distributePopularizeView.setUrlCode(QrCodeUtil.generateAsBase64(distributePopularizeView.getUrl(), QrConfig.create(), "png"));
 		distributePopularizeView.setGotPoints(user.getPoints());

+ 92 - 0
netflix-web/src/main/java/com/cyksj/web/controller/user/UserCertController.java

@@ -0,0 +1,92 @@
+package com.cyksj.web.controller.user;
+
+import cn.hutool.core.util.StrUtil;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.dto.UserWxIndentDto;
+import com.cyksj.model.entity.UserCertRecord;
+import com.cyksj.model.request.UserIndentCertReq;
+import com.cyksj.model.response.WxCertAuthTokenResp;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.user.UserIdentCertService;
+import com.cyksj.web.util.StpUserUtil;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserCertController
+ * 创建者: JavaZou
+ * 创建时间:2024/2/21 10:13
+ */
+@RequestMapping("/applets/user/cert")
+@RestController
+@RequiredArgsConstructor
+public class UserCertController {
+
+	private final UserIdentCertService userIdentCertService;
+
+	private final RedisService redisService;
+
+
+	/**
+	 * 用户是否已授权认证
+	 */
+	@GetMapping("/check/record")
+	public Result<Boolean> checkCertRecord() {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		Boolean flag = userIdentCertService.checkCertRecord(userId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(flag);
+	}
+
+	/**
+	 * 非微信端 用户身份认证 建立微信用户绑定关系
+	 */
+	@PostMapping("/bind")
+	@Deprecated
+	public Result<String> bindWxUser() {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		String state = userIdentCertService.bindWxUser(userId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(state);
+	}
+
+
+	/**
+	 * 用户微信H5认证
+	 */
+	@PostMapping("/auth")
+	public Result<WxCertAuthTokenResp> wxH5IndentCertAuth(@RequestBody UserIndentCertReq indentCertReq) throws Exception {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		indentCertReq.setUserId(userId);
+		WxCertAuthTokenResp resp = userIdentCertService.wxH5IndentCert(indentCertReq);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(resp);
+	}
+
+	/**
+	 * 用户微信H5认证结果查询
+	 */
+	@GetMapping("/auth/result/{mappingId}")
+	public void wxH5IndentCertAuthResult(@PathVariable String mappingId, HttpServletResponse response) throws Exception {
+		String redirectParams = redisService.getStr(mappingId);
+		if (StrUtil.isNotBlank(redirectParams)) {
+			UserWxIndentDto userWxIndentDto = Jsons.parseObject(redirectParams, UserWxIndentDto.class);
+			userIdentCertService.wxH5IndentCertResult(userWxIndentDto);
+			String url = userWxIndentDto.getRedirectUrl();
+			redisService.del(mappingId);
+			response.sendRedirect(url);
+		}
+	}
+
+	/**
+	 * 查看实名认证信息
+	 */
+	@GetMapping("/get/info")
+	public Result<UserCertRecord> getUserCertRecord() {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		UserCertRecord record = userIdentCertService.getUserCertRecord(userId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(record);
+	}
+}