四要素用户身份认证
This commit is contained in:
364
src/main/java/com/sqx/common/utils/DesensitizedUtil.java
Normal file
364
src/main/java/com/sqx/common/utils/DesensitizedUtil.java
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
package com.sqx.common.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.CharUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脱敏工具类,支持以下类型信息的脱敏自动处理:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>用户ID</li>
|
||||||
|
* <li>中文名</li>
|
||||||
|
* <li>身份证</li>
|
||||||
|
* <li>座机号</li>
|
||||||
|
* <li>手机号</li>
|
||||||
|
* <li>地址</li>
|
||||||
|
* <li>电子邮件</li>
|
||||||
|
* <li>密码</li>
|
||||||
|
* <li>车牌</li>
|
||||||
|
* <li>银行卡号</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author dazer and neusoft and qiaomu
|
||||||
|
* @since 5.6.2
|
||||||
|
*/
|
||||||
|
public class DesensitizedUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的脱敏类型枚举
|
||||||
|
*
|
||||||
|
* @author dazer and neusoft and qiaomu
|
||||||
|
*/
|
||||||
|
public enum DesensitizedType {
|
||||||
|
/**
|
||||||
|
* 用户id
|
||||||
|
*/
|
||||||
|
USER_ID,
|
||||||
|
/**
|
||||||
|
* 中文名
|
||||||
|
*/
|
||||||
|
CHINESE_NAME,
|
||||||
|
/**
|
||||||
|
* 身份证号
|
||||||
|
*/
|
||||||
|
ID_CARD,
|
||||||
|
/**
|
||||||
|
* 座机号
|
||||||
|
*/
|
||||||
|
FIXED_PHONE,
|
||||||
|
/**
|
||||||
|
* 手机号
|
||||||
|
*/
|
||||||
|
MOBILE_PHONE,
|
||||||
|
/**
|
||||||
|
* 地址
|
||||||
|
*/
|
||||||
|
ADDRESS,
|
||||||
|
/**
|
||||||
|
* 电子邮件
|
||||||
|
*/
|
||||||
|
EMAIL,
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
PASSWORD,
|
||||||
|
/**
|
||||||
|
* 中国大陆车牌,包含普通车辆、新能源车辆
|
||||||
|
*/
|
||||||
|
CAR_LICENSE,
|
||||||
|
/**
|
||||||
|
* 银行卡
|
||||||
|
*/
|
||||||
|
BANK_CARD,
|
||||||
|
/**
|
||||||
|
* IPv4地址
|
||||||
|
*/
|
||||||
|
IPV4,
|
||||||
|
/**
|
||||||
|
* IPv6地址
|
||||||
|
*/
|
||||||
|
IPV6,
|
||||||
|
/**
|
||||||
|
* 定义了一个first_mask的规则,只显示第一个字符。
|
||||||
|
*/
|
||||||
|
FIRST_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脱敏,使用默认的脱敏策略
|
||||||
|
* <pre>
|
||||||
|
* DesensitizedUtil.desensitized("100", DesensitizedUtil.DesensitizedType.USER_ID)) = "0"
|
||||||
|
* DesensitizedUtil.desensitized("段正淳", DesensitizedUtil.DesensitizedType.CHINESE_NAME)) = "段**"
|
||||||
|
* DesensitizedUtil.desensitized("51343620000320711X", DesensitizedUtil.DesensitizedType.ID_CARD)) = "5***************1X"
|
||||||
|
* DesensitizedUtil.desensitized("09157518479", DesensitizedUtil.DesensitizedType.FIXED_PHONE)) = "0915*****79"
|
||||||
|
* DesensitizedUtil.desensitized("18049531999", DesensitizedUtil.DesensitizedType.MOBILE_PHONE)) = "180****1999"
|
||||||
|
* DesensitizedUtil.desensitized("北京市海淀区马连洼街道289号", DesensitizedUtil.DesensitizedType.ADDRESS)) = "北京市海淀区马********"
|
||||||
|
* DesensitizedUtil.desensitized("duandazhi-jack@gmail.com.cn", DesensitizedUtil.DesensitizedType.EMAIL)) = "d*************@gmail.com.cn"
|
||||||
|
* DesensitizedUtil.desensitized("1234567890", DesensitizedUtil.DesensitizedType.PASSWORD)) = "**********"
|
||||||
|
* DesensitizedUtil.desensitized("苏D40000", DesensitizedUtil.DesensitizedType.CAR_LICENSE)) = "苏D4***0"
|
||||||
|
* DesensitizedUtil.desensitized("11011111222233333256", DesensitizedUtil.DesensitizedType.BANK_CARD)) = "1101 **** **** **** 3256"
|
||||||
|
* DesensitizedUtil.desensitized("192.168.1.1", DesensitizedUtil.DesensitizedType.IPV4)) = "192.*.*.*"
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param str 字符串
|
||||||
|
* @param desensitizedType 脱敏类型;可以脱敏:用户id、中文名、身份证号、座机号、手机号、地址、电子邮件、密码
|
||||||
|
* @return 脱敏之后的字符串
|
||||||
|
* @author dazer and neusoft and qiaomu
|
||||||
|
* @since 5.6.2
|
||||||
|
*/
|
||||||
|
public static String desensitized(CharSequence str, DesensitizedUtil.DesensitizedType desensitizedType) {
|
||||||
|
if (StrUtil.isBlank(str)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
String newStr = String.valueOf(str);
|
||||||
|
switch (desensitizedType) {
|
||||||
|
case USER_ID:
|
||||||
|
newStr = String.valueOf(userId());
|
||||||
|
break;
|
||||||
|
case CHINESE_NAME:
|
||||||
|
newStr = chineseName(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
case ID_CARD:
|
||||||
|
newStr = idCardNum(String.valueOf(str), 1, 2);
|
||||||
|
break;
|
||||||
|
case FIXED_PHONE:
|
||||||
|
newStr = fixedPhone(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
case MOBILE_PHONE:
|
||||||
|
newStr = mobilePhone(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
case ADDRESS:
|
||||||
|
newStr = address(String.valueOf(str), 8);
|
||||||
|
break;
|
||||||
|
case EMAIL:
|
||||||
|
newStr = email(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
case PASSWORD:
|
||||||
|
newStr = password(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
case CAR_LICENSE:
|
||||||
|
newStr = carLicense(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
case BANK_CARD:
|
||||||
|
newStr = bankCard(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
case IPV4:
|
||||||
|
newStr = ipv4(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
case IPV6:
|
||||||
|
newStr = ipv6(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
case FIRST_MASK:
|
||||||
|
newStr = firstMask(String.valueOf(str));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return newStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【用户id】不对外提供userId
|
||||||
|
*
|
||||||
|
* @return 脱敏后的主键
|
||||||
|
*/
|
||||||
|
public static Long userId() {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定义了一个first_mask的规则,只显示第一个字符。<br>
|
||||||
|
* 脱敏前:123456789;脱敏后:1********。
|
||||||
|
*
|
||||||
|
* @param str 字符串
|
||||||
|
* @return 脱敏后的字符串
|
||||||
|
*/
|
||||||
|
public static String firstMask(String str) {
|
||||||
|
if (StrUtil.isBlank(str)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
return StrUtil.hide(str, 1, str.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李**
|
||||||
|
*
|
||||||
|
* @param fullName 姓名
|
||||||
|
* @return 脱敏后的姓名
|
||||||
|
*/
|
||||||
|
public static String chineseName(String fullName) {
|
||||||
|
return firstMask(fullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【身份证号】前1位 和后2位
|
||||||
|
*
|
||||||
|
* @param idCardNum 身份证
|
||||||
|
* @param front 保留:前面的front位数;从1开始
|
||||||
|
* @param end 保留:后面的end位数;从1开始
|
||||||
|
* @return 脱敏后的身份证
|
||||||
|
*/
|
||||||
|
public static String idCardNum(String idCardNum, int front, int end) {
|
||||||
|
//身份证不能为空
|
||||||
|
if (StrUtil.isBlank(idCardNum)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
//需要截取的长度不能大于身份证号长度
|
||||||
|
if ((front + end) > idCardNum.length()) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
//需要截取的不能小于0
|
||||||
|
if (front < 0 || end < 0) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
return StrUtil.hide(idCardNum, front, idCardNum.length() - end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【固定电话 前四位,后两位
|
||||||
|
*
|
||||||
|
* @param num 固定电话
|
||||||
|
* @return 脱敏后的固定电话;
|
||||||
|
*/
|
||||||
|
public static String fixedPhone(String num) {
|
||||||
|
if (StrUtil.isBlank(num)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
return StrUtil.hide(num, 4, num.length() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【手机号码】前三位,后4位,其他隐藏,比如135****2210
|
||||||
|
*
|
||||||
|
* @param num 移动电话;
|
||||||
|
* @return 脱敏后的移动电话;
|
||||||
|
*/
|
||||||
|
public static String mobilePhone(String num) {
|
||||||
|
if (StrUtil.isBlank(num)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
return StrUtil.hide(num, 3, num.length() - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****
|
||||||
|
*
|
||||||
|
* @param address 家庭住址
|
||||||
|
* @param sensitiveSize 敏感信息长度
|
||||||
|
* @return 脱敏后的家庭地址
|
||||||
|
*/
|
||||||
|
public static String address(String address, int sensitiveSize) {
|
||||||
|
if (StrUtil.isBlank(address)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
int length = address.length();
|
||||||
|
return StrUtil.hide(address, length - sensitiveSize, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【电子邮箱】邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com
|
||||||
|
*
|
||||||
|
* @param email 邮箱
|
||||||
|
* @return 脱敏后的邮箱
|
||||||
|
*/
|
||||||
|
public static String email(String email) {
|
||||||
|
if (StrUtil.isBlank(email)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
int index = StrUtil.indexOf(email, '@');
|
||||||
|
if (index <= 1) {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
return StrUtil.hide(email, 1, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【密码】密码的全部字符都用*代替,比如:******
|
||||||
|
*
|
||||||
|
* @param password 密码
|
||||||
|
* @return 脱敏后的密码
|
||||||
|
*/
|
||||||
|
public static String password(String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
return StrUtil.repeat('*', password.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【中国车牌】车牌中间用*代替
|
||||||
|
* eg1:null -》 ""
|
||||||
|
* eg1:"" -》 ""
|
||||||
|
* eg3:苏D40000 -》 苏D4***0
|
||||||
|
* eg4:陕A12345D -》 陕A1****D
|
||||||
|
* eg5:京A123 -》 京A123 如果是错误的车牌,不处理
|
||||||
|
*
|
||||||
|
* @param carLicense 完整的车牌号
|
||||||
|
* @return 脱敏后的车牌
|
||||||
|
*/
|
||||||
|
public static String carLicense(String carLicense) {
|
||||||
|
if (StrUtil.isBlank(carLicense)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
// 普通车牌
|
||||||
|
if (carLicense.length() == 7) {
|
||||||
|
carLicense = StrUtil.hide(carLicense, 3, 6);
|
||||||
|
} else if (carLicense.length() == 8) {
|
||||||
|
// 新能源车牌
|
||||||
|
carLicense = StrUtil.hide(carLicense, 3, 7);
|
||||||
|
}
|
||||||
|
return carLicense;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 银行卡号脱敏
|
||||||
|
* eg: 1101 **** **** **** 3256
|
||||||
|
*
|
||||||
|
* @param bankCardNo 银行卡号
|
||||||
|
* @return 脱敏之后的银行卡号
|
||||||
|
* @since 5.6.3
|
||||||
|
*/
|
||||||
|
public static String bankCard(String bankCardNo) {
|
||||||
|
if (StrUtil.isBlank(bankCardNo)) {
|
||||||
|
return bankCardNo;
|
||||||
|
}
|
||||||
|
bankCardNo = StrUtil.trim(bankCardNo);
|
||||||
|
if (bankCardNo.length() < 9) {
|
||||||
|
return bankCardNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int length = bankCardNo.length();
|
||||||
|
final int midLength = length - 8;
|
||||||
|
final StringBuilder buf = new StringBuilder();
|
||||||
|
|
||||||
|
buf.append(bankCardNo, 0, 4);
|
||||||
|
for (int i = 0; i < midLength; ++i) {
|
||||||
|
if (i % 4 == 0) {
|
||||||
|
buf.append(CharUtil.SPACE);
|
||||||
|
}
|
||||||
|
buf.append('*');
|
||||||
|
}
|
||||||
|
buf.append(CharUtil.SPACE).append(bankCardNo, length - 4, length);
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IPv4脱敏,如:脱敏前:192.0.2.1;脱敏后:192.*.*.*。
|
||||||
|
*
|
||||||
|
* @param ipv4 IPv4地址
|
||||||
|
* @return 脱敏后的地址
|
||||||
|
*/
|
||||||
|
public static String ipv4(String ipv4) {
|
||||||
|
return StrUtil.subBefore(ipv4, '.', false) + ".*.*.*";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IPv4脱敏,如:脱敏前:2001:0db8:86a3:08d3:1319:8a2e:0370:7344;脱敏后:2001:*:*:*:*:*:*:*
|
||||||
|
*
|
||||||
|
* @param ipv6 IPv4地址
|
||||||
|
* @return 脱敏后的地址
|
||||||
|
*/
|
||||||
|
public static String ipv6(String ipv6) {
|
||||||
|
return StrUtil.subBefore(ipv6, ':', false) + ":*:*:*:*:*:*:*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,14 +3,17 @@ package com.sqx.modules.app.controller.app;
|
|||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.lang.Validator;
|
||||||
import cn.hutool.core.util.IdcardUtil;
|
import cn.hutool.core.util.IdcardUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.sqx.common.annotation.Debounce;
|
import com.sqx.common.annotation.Debounce;
|
||||||
import com.sqx.common.utils.ApiAccessLimitUtil;
|
import com.sqx.common.utils.ApiAccessLimitUtil;
|
||||||
import com.sqx.common.utils.DataLimitUtil;
|
import com.sqx.common.utils.DataLimitUtil;
|
||||||
|
import com.sqx.common.utils.DesensitizedUtil;
|
||||||
import com.sqx.common.utils.Result;
|
import com.sqx.common.utils.Result;
|
||||||
import com.sqx.modules.app.annotation.Login;
|
import com.sqx.modules.app.annotation.Login;
|
||||||
import com.sqx.modules.app.annotation.LoginUser;
|
import com.sqx.modules.app.annotation.LoginUser;
|
||||||
@@ -35,6 +38,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* APP登录授权
|
* APP登录授权
|
||||||
|
*
|
||||||
* @author mac
|
* @author mac
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@@ -101,8 +105,13 @@ public class AppController {
|
|||||||
@ApiOperation("用户修改个人信息")
|
@ApiOperation("用户修改个人信息")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@Debounce(interval = 3000, value = "#userId")
|
@Debounce(interval = 3000, value = "#userId")
|
||||||
public Result updateUserImageUrl(@RequestAttribute("userId") Long userId, @RequestParam(required = false) String zhiFuBao,
|
public Result updateUserImageUrl(@RequestAttribute("userId") Long userId,
|
||||||
@RequestParam String certName, @RequestParam(required = false) String certNum) {
|
@RequestParam(required = false) String zhiFuBao,
|
||||||
|
@RequestParam String certName,
|
||||||
|
@RequestParam(required = false) String certNum,
|
||||||
|
@RequestParam(required = false) String accountNo,
|
||||||
|
@RequestParam(required = false) String mobile
|
||||||
|
) {
|
||||||
if (StrUtil.isAllBlank(zhiFuBao, certNum)) {
|
if (StrUtil.isAllBlank(zhiFuBao, certNum)) {
|
||||||
return Result.error("支付宝账号或实名身份证号码必须传递一个");
|
return Result.error("支付宝账号或实名身份证号码必须传递一个");
|
||||||
}
|
}
|
||||||
@@ -140,7 +149,11 @@ public class AppController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!certNum.equals(userInfo.getCertNo()) || !certName.equals(userInfo.getCertName())) {
|
if (!certNum.equals(userInfo.getCertNo())
|
||||||
|
|| !certName.equals(userInfo.getCertName())
|
||||||
|
|| !accountNo.equals(userInfo.getAccountNo())
|
||||||
|
|| !mobile.equals(userInfo.getMobile())
|
||||||
|
) {
|
||||||
if (StrUtil.isNotBlank(userEntity.getZhiFuBaoName()) && !certName.equals(userEntity.getZhiFuBaoName())) {
|
if (StrUtil.isNotBlank(userEntity.getZhiFuBaoName()) && !certName.equals(userEntity.getZhiFuBaoName())) {
|
||||||
return Result.error("实名修改失败: 姓名与绑定支付宝信息不相符");
|
return Result.error("实名修改失败: 姓名与绑定支付宝信息不相符");
|
||||||
}
|
}
|
||||||
@@ -153,14 +166,6 @@ public class AppController {
|
|||||||
return Result.error("实名修改失败: 此身份证信息已绑定过");
|
return Result.error("实名修改失败: 此身份证信息已绑定过");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ApiAccessLimitUtil.getCertAuthIsAccessAllowed(String.valueOf(userId), "updateAuthCertInfo", 1)) {
|
|
||||||
return Result.error("实名修改失败: 每月可修改次数已用完,请联系管理员");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ApiAccessLimitUtil.getCertAuthIsAccessAllowed(certNum, "updateAuthCertInfoByIdCard", 1)) {
|
|
||||||
return Result.error("实名修改失败: 每月可修改次数已用完,请联系管理员");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 校验实名信息是否在黑名单里面
|
// 校验实名信息是否在黑名单里面
|
||||||
Integer count = tbUserBlacklistMapper.selectCount(new LambdaQueryWrapper<TbUserBlacklist>().eq(TbUserBlacklist::getRealName, certName)
|
Integer count = tbUserBlacklistMapper.selectCount(new LambdaQueryWrapper<TbUserBlacklist>().eq(TbUserBlacklist::getRealName, certName)
|
||||||
@@ -172,16 +177,17 @@ public class AppController {
|
|||||||
return Result.error("异常行为: 您的实名信息存在异常行为");
|
return Result.error("异常行为: 您的实名信息存在异常行为");
|
||||||
}
|
}
|
||||||
|
|
||||||
aliService.authCertNo(certName, certNum);
|
String respJson = aliService.auth(certName, certNum, accountNo, mobile);
|
||||||
userInfo.setCertName(certName);
|
userInfo.setCertName(certName);
|
||||||
userInfo.setCertNo(certNum);
|
userInfo.setCertNo(certNum);
|
||||||
|
userInfo.setAccountNo(accountNo);
|
||||||
|
userInfo.setMobile(mobile);
|
||||||
|
userInfo.setRespJson(respJson);
|
||||||
userInfo.setUpdateTime(DateUtil.date());
|
userInfo.setUpdateTime(DateUtil.date());
|
||||||
boolean update = userInfoService.update(userInfo, new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getUserId, userId).eq(UserInfo::getId, userInfo.getId()));
|
boolean update = userInfoService.update(userInfo, new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getUserId, userId).eq(UserInfo::getId, userInfo.getId()));
|
||||||
if (!update) {
|
if (!update) {
|
||||||
return Result.error("实名修改失败: 请稍后重试");
|
return Result.error("实名修改失败: 请稍后重试");
|
||||||
}
|
}
|
||||||
ApiAccessLimitUtil.setCertAuthIsAccessAllowed(String.valueOf(userId), "updateAuthCertInfo", 1, "month");
|
|
||||||
ApiAccessLimitUtil.setCertAuthIsAccessAllowed(certNum, "updateAuthCertInfoByIdCard", 1, "month");
|
|
||||||
return Result.success();
|
return Result.success();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return Result.error("实名修改失败: 身份证信息不匹配");
|
return Result.error("实名修改失败: 身份证信息不匹配");
|
||||||
@@ -198,7 +204,10 @@ public class AppController {
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Result updateUsers(@RequestAttribute("userId") Long userId, @RequestBody UserEntity userEntity) {
|
public Result updateUsers(@RequestAttribute("userId") Long userId, @RequestBody UserEntity userEntity) {
|
||||||
userEntity.setUserId(userId);
|
userEntity.setUserId(userId);
|
||||||
userService.update(userEntity, new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getUserId, userId));
|
userService.update(null, Wrappers.<UserEntity>lambdaUpdate().eq(UserEntity::getUserId, userId)
|
||||||
|
.set(StrUtil.isNotBlank(userEntity.getAvatar()), UserEntity::getAvatar, userEntity.getAvatar())
|
||||||
|
.set(StrUtil.isNotBlank(userEntity.getUserName()), UserEntity::getUserName, userEntity.getUserName())
|
||||||
|
);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +237,23 @@ public class AppController {
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Result selectUserById(@LoginUser UserEntity user) {
|
public Result selectUserById(@LoginUser UserEntity user) {
|
||||||
UserInfo userInfo = userInfoService.getByUserId(user.getUserId());
|
UserInfo userInfo = userInfoService.getByUserId(user.getUserId());
|
||||||
|
userInfo.setRespJson(null);
|
||||||
|
if (StrUtil.isNotEmpty(userInfo.getAccountNo())) {
|
||||||
|
userInfo.setAccountNo(DesensitizedUtil.bankCard(userInfo.getAccountNo()));
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotEmpty(userInfo.getMobile())) {
|
||||||
|
userInfo.setMobile(DesensitizedUtil.mobilePhone(userInfo.getMobile()));
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotEmpty(userInfo.getCertNo())) {
|
||||||
|
userInfo.setCertNo(DesensitizedUtil.idCardNum(userInfo.getCertNo(), 3, 2));
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotEmpty(user.getZhiFuBao())) {
|
||||||
|
if (Validator.isEmail(user.getZhiFuBao())) {
|
||||||
|
user.setZhiFuBao(DesensitizedUtil.email(user.getZhiFuBao()));
|
||||||
|
} else {
|
||||||
|
user.setZhiFuBao(DesensitizedUtil.mobilePhone(user.getZhiFuBao()));
|
||||||
|
}
|
||||||
|
}
|
||||||
Map<String, Object> map = BeanUtil.beanToMap(user);
|
Map<String, Object> map = BeanUtil.beanToMap(user);
|
||||||
map.putAll(BeanUtil.beanToMap(userInfo));
|
map.putAll(BeanUtil.beanToMap(userInfo));
|
||||||
if (StrUtil.isBlank(user.getZhiFuBaoName()) && StrUtil.isNotBlank(userInfo.getCertName())) {
|
if (StrUtil.isBlank(user.getZhiFuBaoName()) && StrUtil.isNotBlank(userInfo.getCertName())) {
|
||||||
|
|||||||
19
src/main/java/com/sqx/modules/app/dao/AuthDTO.java
Normal file
19
src/main/java/com/sqx/modules/app/dao/AuthDTO.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package com.sqx.modules.app.dao;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AuthDTO {
|
||||||
|
@NotBlank
|
||||||
|
private String name;
|
||||||
|
@NotBlank
|
||||||
|
private String idNum;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String accountNo;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String mobile;
|
||||||
|
}
|
||||||
@@ -39,6 +39,21 @@ public class UserInfo implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private String certNo;
|
private String certNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 银行账号
|
||||||
|
*/
|
||||||
|
private String accountNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 银行预留手机号
|
||||||
|
*/
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 四要素接口响应报文
|
||||||
|
*/
|
||||||
|
private String respJson;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改时间
|
* 修改时间
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,4 +3,6 @@ package com.sqx.modules.app.service;
|
|||||||
|
|
||||||
public interface AliService {
|
public interface AliService {
|
||||||
void authCertNo(String name, String idCard);
|
void authCertNo(String name, String idCard);
|
||||||
|
|
||||||
|
String auth(String name, String idCard, String accountNo, String bankPreMobile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.sqx.modules.app.service;
|
package com.sqx.modules.app.service;
|
||||||
|
|
||||||
import com.sqx.modules.app.entity.UserInfo;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.sqx.modules.app.entity.UserInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Administrator
|
* @author Administrator
|
||||||
@@ -14,4 +14,6 @@ public interface UserInfoService extends IService<UserInfo> {
|
|||||||
|
|
||||||
Integer countCertCount(String name, String idNum);
|
Integer countCertCount(String name, String idNum);
|
||||||
|
|
||||||
|
Integer countCertCount(String name, String idNum, String accountNo, String mobile);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||||||
import com.sqx.common.utils.PageUtils;
|
import com.sqx.common.utils.PageUtils;
|
||||||
import com.sqx.common.utils.Result;
|
import com.sqx.common.utils.Result;
|
||||||
import com.sqx.modules.app.dao.AuthCertNoDTO;
|
import com.sqx.modules.app.dao.AuthCertNoDTO;
|
||||||
|
import com.sqx.modules.app.dao.AuthDTO;
|
||||||
import com.sqx.modules.app.entity.UserEntity;
|
import com.sqx.modules.app.entity.UserEntity;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@@ -232,6 +233,12 @@ public interface UserService extends IService<UserEntity> {
|
|||||||
*/
|
*/
|
||||||
Object authCertNo(long userId, AuthCertNoDTO authCertNoDTO);
|
Object authCertNo(long userId, AuthCertNoDTO authCertNoDTO);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 四要素身份证认证
|
||||||
|
*/
|
||||||
|
Object auth(long userId, AuthDTO authDTO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 封禁拉黑用户
|
* 封禁拉黑用户
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
package com.sqx.modules.app.service.impl;
|
package com.sqx.modules.app.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Validator;
|
||||||
|
import cn.hutool.core.util.IdcardUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.core.util.URLUtil;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.aliyun.dytnsapi20200217.Client;
|
import com.aliyun.dytnsapi20200217.Client;
|
||||||
import com.aliyun.dytnsapi20200217.models.CertNoTwoElementVerificationRequest;
|
import com.aliyun.dytnsapi20200217.models.CertNoTwoElementVerificationRequest;
|
||||||
import com.aliyun.dytnsapi20200217.models.CertNoTwoElementVerificationResponse;
|
import com.aliyun.dytnsapi20200217.models.CertNoTwoElementVerificationResponse;
|
||||||
@@ -12,6 +19,10 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -64,4 +75,64 @@ public class AliServiceImpl implements AliService {
|
|||||||
throw new SqxException(e.getMessage());
|
throw new SqxException(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final Pattern CHINESE_NAME = Pattern.compile("^[\u2E80-\u9FFF·]{2,60}$");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阿里云四要素认证
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param idCard
|
||||||
|
* @param accountNo
|
||||||
|
* @param bankPreMobile
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String auth(String name, String idCard, String accountNo, String bankPreMobile) {
|
||||||
|
log.info("阿里云四要素认证请求参数: {} {} {} {}", name, idCard, accountNo, bankPreMobile);
|
||||||
|
if (StrUtil.isBlank(name)) {
|
||||||
|
throw new SqxException("持卡人姓名不能为空");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(idCard)) {
|
||||||
|
throw new SqxException("身份证号码不能为空");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(accountNo)) {
|
||||||
|
throw new SqxException("银行卡卡号不能为空");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(bankPreMobile)) {
|
||||||
|
throw new SqxException("银行预留手机号码不能为空");
|
||||||
|
}
|
||||||
|
if (!Validator.isMactchRegex(CHINESE_NAME, name)) {
|
||||||
|
throw new SqxException("持卡人姓名不合法");
|
||||||
|
}
|
||||||
|
boolean validCard = IdcardUtil.isValidCard(idCard);
|
||||||
|
if (!validCard) {
|
||||||
|
throw new SqxException("身份证号码不合法");
|
||||||
|
}
|
||||||
|
validCard = Validator.isMobile(bankPreMobile);
|
||||||
|
if (!validCard) {
|
||||||
|
throw new SqxException("银行预留手机号码格式不正确");
|
||||||
|
}
|
||||||
|
String appcode = "f98606b602564d209f37fc02b0bd590c";
|
||||||
|
Map<String, String> headers = new HashMap<>(2);
|
||||||
|
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
|
||||||
|
headers.put("Authorization", "APPCODE " + appcode);
|
||||||
|
//根据API的要求,定义相对应的Content-Type
|
||||||
|
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("accountNo", accountNo);
|
||||||
|
params.put("name", name);
|
||||||
|
params.put("idCardCode", idCard);
|
||||||
|
params.put("bankPreMobile", bankPreMobile);
|
||||||
|
String reqBody = URLUtil.encode(StrUtil.format("accountNo={}&name={}&idCardCode={}&bankPreMobile={}", accountNo, name, idCard, bankPreMobile), Charset.defaultCharset());
|
||||||
|
String respBody = HttpUtil.createPost("https://bkvip.market.alicloudapi.com/v3/bcheck").headerMap(headers, true).body(reqBody).timeout(15 * 1000).execute().body();
|
||||||
|
// {"error_code":0,"reason":"成功","result":{"respCode":"0","respMsg":"银行卡鉴权成功","bancardInfor":{"bankName":"招商银行","BankCode":"03080000","BankId":5,"type":"借记卡","cardname":"一卡通(银联卡)","tel":"95555","Icon":"2014121619271052743.gif"}},"sn":"010817431025283426800706871"}
|
||||||
|
// {"error_code":10028,"reason":"成功","result":{"respCode":"6","respMsg":"身份证格式有误","bancardInfor":{"bankName":"招商银行","BankCode":"03080000","BankId":5,"type":"借记卡","cardname":"一卡通(银联卡)","tel":"95555","Icon":"2014121619271052743.gif"}},"sn":"010817575524183118006799233"}
|
||||||
|
JSONObject ret = JSONUtil.parseObj(respBody);
|
||||||
|
Integer errorCode = ret.getInt("error_code");
|
||||||
|
if (errorCode != 0) {
|
||||||
|
JSONObject result = ret.getJSONObject("result");
|
||||||
|
throw new SqxException(result.getStr("respMsg"));
|
||||||
|
}
|
||||||
|
return respBody;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,20 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
|
|||||||
public Integer countCertCount(String name, String idNum) {
|
public Integer countCertCount(String name, String idNum) {
|
||||||
return count(new LambdaQueryWrapper<UserInfo>()
|
return count(new LambdaQueryWrapper<UserInfo>()
|
||||||
.eq(UserInfo::getCertName, name)
|
.eq(UserInfo::getCertName, name)
|
||||||
.eq(UserInfo::getCertNo, idNum));
|
.eq(UserInfo::getCertNo, idNum)
|
||||||
|
.isNotNull(UserInfo::getAccountNo)
|
||||||
|
.isNotNull(UserInfo::getMobile)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countCertCount(String name, String idNum, String accountNo, String mobile) {
|
||||||
|
return count(new LambdaQueryWrapper<UserInfo>()
|
||||||
|
.eq(UserInfo::getCertName, name)
|
||||||
|
.eq(UserInfo::getCertNo, idNum)
|
||||||
|
.eq(UserInfo::getAccountNo, accountNo)
|
||||||
|
.eq(UserInfo::getMobile, mobile)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,7 @@ import com.sqx.common.exception.SqxException;
|
|||||||
import com.sqx.common.utils.DateUtils;
|
import com.sqx.common.utils.DateUtils;
|
||||||
import com.sqx.common.utils.PageUtils;
|
import com.sqx.common.utils.PageUtils;
|
||||||
import com.sqx.common.utils.Result;
|
import com.sqx.common.utils.Result;
|
||||||
import com.sqx.modules.app.dao.AuthCertNoDTO;
|
import com.sqx.modules.app.dao.*;
|
||||||
import com.sqx.modules.app.dao.MsgDao;
|
|
||||||
import com.sqx.modules.app.dao.UserDao;
|
|
||||||
import com.sqx.modules.app.dao.UserVipDao;
|
|
||||||
import com.sqx.modules.app.entity.*;
|
import com.sqx.modules.app.entity.*;
|
||||||
import com.sqx.modules.app.mapper.TbUserBlacklistMapper;
|
import com.sqx.modules.app.mapper.TbUserBlacklistMapper;
|
||||||
import com.sqx.modules.app.service.*;
|
import com.sqx.modules.app.service.*;
|
||||||
@@ -1670,6 +1667,39 @@ public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements
|
|||||||
return userInfoService.updateById(userInfo);
|
return userInfoService.updateById(userInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object auth(long userId, AuthDTO authDTO) {
|
||||||
|
authDTO.setName(StrUtil.trim(authDTO.getName()));
|
||||||
|
authDTO.setIdNum(StrUtil.trim(authDTO.getIdNum()));
|
||||||
|
authDTO.setAccountNo(StrUtil.trim(authDTO.getAccountNo()));
|
||||||
|
authDTO.setMobile(StrUtil.trim(authDTO.getMobile()));
|
||||||
|
|
||||||
|
UserEntity userEntity = baseMapper.selectById(userId);
|
||||||
|
if (userEntity == null) {
|
||||||
|
throw new SqxException("用户信息不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
UserInfo userInfo = userInfoService.getByUserId(userId);
|
||||||
|
if (StrUtil.isNotEmpty(userInfo.getCertName()) && StrUtil.isNotEmpty(userInfo.getAccountNo()) && StrUtil.isNotEmpty(userInfo.getMobile())) {
|
||||||
|
throw new SqxException("此账号已认证");
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer count = userInfoService.countCertCount(authDTO.getName(), authDTO.getIdNum(), authDTO.getAccountNo(), authDTO.getMobile());
|
||||||
|
if (count > 1) {
|
||||||
|
throw new SqxException("此实名信息已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
String respJson = aliService.auth(authDTO.getName(), authDTO.getIdNum(), authDTO.getAccountNo(), authDTO.getMobile());
|
||||||
|
|
||||||
|
userInfo.setCertName(authDTO.getName());
|
||||||
|
userInfo.setCertNo(authDTO.getIdNum());
|
||||||
|
userInfo.setAccountNo(authDTO.getAccountNo());
|
||||||
|
userInfo.setMobile(authDTO.getMobile());
|
||||||
|
userInfo.setRespJson(respJson);
|
||||||
|
userInfo.setUpdateTime(DateUtil.date());
|
||||||
|
return userInfoService.updateById(userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addBlackUser(Long userId) {
|
public void addBlackUser(Long userId) {
|
||||||
log.info("异常用户id, 异常操作: {}", userId);
|
log.info("异常用户id, 异常操作: {}", userId);
|
||||||
|
|||||||
@@ -528,6 +528,9 @@ public class CashOutServiceImpl extends ServiceImpl<CashOutDao, CashOut> impleme
|
|||||||
if (userDetailInfo == null || StrUtil.isBlank(userDetailInfo.getCertName())) {
|
if (userDetailInfo == null || StrUtil.isBlank(userDetailInfo.getCertName())) {
|
||||||
return Result.error(9991, "请先实名认证!");
|
return Result.error(9991, "请先实名认证!");
|
||||||
}
|
}
|
||||||
|
if (StrUtil.isEmpty(userDetailInfo.getAccountNo()) || StrUtil.isEmpty(userDetailInfo.getMobile())) {
|
||||||
|
return Result.error(9991, "需重新完成实名认证后才可提现!");
|
||||||
|
}
|
||||||
|
|
||||||
if (!userInfo.getZhiFuBaoName().equals(userDetailInfo.getCertName())) {
|
if (!userInfo.getZhiFuBaoName().equals(userDetailInfo.getCertName())) {
|
||||||
return Result.error(500, "支付宝和实名姓名不符无法提现!");
|
return Result.error(500, "支付宝和实名姓名不符无法提现!");
|
||||||
@@ -622,11 +625,11 @@ public class CashOutServiceImpl extends ServiceImpl<CashOutDao, CashOut> impleme
|
|||||||
}
|
}
|
||||||
// 禁止该姓名的用户提现
|
// 禁止该姓名的用户提现
|
||||||
Integer count = tbWithdrawBlacklistMapper.selectCount(Wrappers.<TbWithdrawBlacklist>lambdaQuery().in(TbWithdrawBlacklist::getRealName, withdrawCheckNameSet));
|
Integer count = tbWithdrawBlacklistMapper.selectCount(Wrappers.<TbWithdrawBlacklist>lambdaQuery().in(TbWithdrawBlacklist::getRealName, withdrawCheckNameSet));
|
||||||
if(count > 0){
|
if (count > 0) {
|
||||||
userMoneyDetails.setContent("成功提现:" + money);
|
userMoneyDetails.setContent("成功提现:" + money);
|
||||||
cashOut.setState(3);
|
cashOut.setState(3);
|
||||||
cashOut.setRelationId("提现黑名单用户,请谨慎审核!");
|
cashOut.setRelationId("提现黑名单用户,请谨慎审核!");
|
||||||
}else{
|
} else {
|
||||||
if (wuyouPay.checkCanCash(userId, WithdrawTypeEnum.MANUAL, new BigDecimal(money.toString()))) {
|
if (wuyouPay.checkCanCash(userId, WithdrawTypeEnum.MANUAL, new BigDecimal(money.toString()))) {
|
||||||
cashOut.setStatus(4);
|
cashOut.setStatus(4);
|
||||||
BaseResp baseResp = wuyouPay.extractOrder(outOrderNo, cashOut.getUserId(), true, cashOut.getMoney(), cashOut.getZhifubao(), cashOut.getZhifubaoName());
|
BaseResp baseResp = wuyouPay.extractOrder(outOrderNo, cashOut.getUserId(), true, cashOut.getMoney(), cashOut.getZhifubao(), cashOut.getZhifubaoName());
|
||||||
|
|||||||
Reference in New Issue
Block a user