Commit ff3adc54 authored by 唐峰's avatar 唐峰

Merge branch 'feature-migration-20230628' of...

Merge branch 'feature-migration-20230628' of git.quantgroup.cn:head_group/xyqb-user2 into feature-migration-20230628
parents ae29eb07 737ac45e
...@@ -37,6 +37,7 @@ public interface Constants { ...@@ -37,6 +37,7 @@ public interface Constants {
String X_AUTH_APP_ID = "qg-app-id"; String X_AUTH_APP_ID = "qg-app-id";
String X_AUTH_FROM = "qg-registered-from"; String X_AUTH_FROM = "qg-registered-from";
String X_STMS_TOKEN = "qg-stms-token";
String X_BEHAVIOR_TYPE = "qg-behavior-type"; String X_BEHAVIOR_TYPE = "qg-behavior-type";
String X_BEHAVIOR_ID = "qg-behavior-id"; String X_BEHAVIOR_ID = "qg-behavior-id";
......
package cn.quantgroup.xyqb.config.http; package cn.quantgroup.xyqb.config.http;
import cn.quantgroup.xyqb.filter.BehaviorInterceptor; import cn.quantgroup.xyqb.filter.BehaviorInterceptor;
import cn.quantgroup.xyqb.filter.InnerInterceptor;
import cn.quantgroup.xyqb.filter.TokenInterceptor; import cn.quantgroup.xyqb.filter.TokenInterceptor;
import cn.quantgroup.xyqb.service.captcha.IQuantgroupCaptchaService; import cn.quantgroup.xyqb.remote.StmsRemoteService;
import cn.quantgroup.xyqb.service.session.ISessionService; import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.service.v2.BehaviorContext; import cn.quantgroup.xyqb.service.v2.BehaviorContext;
import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService;
import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.format.FormatterRegistry; import org.springframework.format.FormatterRegistry;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
...@@ -27,6 +26,8 @@ public class MyWebMvcConfigurer extends WebMvcConfigurerAdapter { ...@@ -27,6 +26,8 @@ public class MyWebMvcConfigurer extends WebMvcConfigurerAdapter {
private ISessionService sessionService; private ISessionService sessionService;
@Autowired @Autowired
private BehaviorContext behaviorContext; private BehaviorContext behaviorContext;
@Autowired
private StmsRemoteService stmsRemoteService;
@Override @Override
public void addFormatters(FormatterRegistry registry) { public void addFormatters(FormatterRegistry registry) {
...@@ -53,9 +54,12 @@ public class MyWebMvcConfigurer extends WebMvcConfigurerAdapter { ...@@ -53,9 +54,12 @@ public class MyWebMvcConfigurer extends WebMvcConfigurerAdapter {
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor(sessionService)).addPathPatterns("/v2/**") registry.addInterceptor(new TokenInterceptor(sessionService)).addPathPatterns("/v2/**")
.excludePathPatterns("/v2/behavior/code"); .excludePathPatterns("/v2/behavior/code");
registry.addInterceptor(new BehaviorInterceptor(behaviorContext)).addPathPatterns("/v2/**","/api/v2") registry.addInterceptor(new BehaviorInterceptor(behaviorContext)).addPathPatterns("/v2/**", "/api/v2/**")
.excludePathPatterns("/v2/behavior/code"); .excludePathPatterns("/v2/behavior/code");
registry.addInterceptor(new InnerInterceptor(sessionService, stmsRemoteService)).addPathPatterns("/api/v2/**");
} }
} }
\ No newline at end of file
package cn.quantgroup.xyqb.controller.api.v2;
import cn.quantgroup.xyqb.controller.req.v2.LoginReq;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.LoginBean;
import cn.quantgroup.xyqb.service.v2.LoginContext;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v2/oauth")
public class OauthApiV2Controller {
private final LoginContext loginContext;
public OauthApiV2Controller(LoginContext loginContext) {
this.loginContext = loginContext;
}
/**
* 对外内部登录接口
* @param loginReq LoginReq
* @return JsonResult<LoginBean>
* @see <a href="http://yapi.quantgroups.com/project/17/interface/api/65779">对外统一登录接口</a>
*/
@PostMapping("/login")
public JsonResult<LoginBean> login(@RequestBody LoginReq loginReq){
return JsonResult.buildSuccessResultGeneric(loginContext.login(loginReq));
}
}
package cn.quantgroup.xyqb.controller.api.v2;
import cn.quantgroup.xyqb.controller.req.v2.BatchInfoReq;
import cn.quantgroup.xyqb.controller.req.v2.UserInfoReq;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.exception.BizException;
import cn.quantgroup.xyqb.exception.BizExceptionEnum;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.repository.IUserRepository;
import cn.quantgroup.xyqb.service.wechat.IWechatService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/v2/user")
public class UserApiV2Controller {
private final IUserRepository userRepository;
private final IWechatService wechatService;
public UserApiV2Controller(IUserRepository userRepository, IWechatService wechatService) {
this.userRepository = userRepository;
this.wechatService = wechatService;
}
/**
* 查询用户信息
*
* @param userInfoReq 查询条件
* @return JsonResult<User>
* @see <a href="http://yapi.quantgroups.com/project/17/interface/api/65724">查询用户信息</a>
*/
@PostMapping("info")
public JsonResult<User> info(@RequestBody UserInfoReq userInfoReq) {
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
User user = null;
//1、校验
if (userInfoReq.getUserId() == null && StringUtils.isEmpty(userInfoReq.getUuid())
&& StringUtils.isEmpty(userInfoReq.getPhoneNo()) && (StringUtils.isEmpty(userInfoReq.getAppId()) && StringUtils.isEmpty(userInfoReq.getOpenId()))
&& (StringUtils.isEmpty(userInfoReq.getAppId()) && StringUtils.isEmpty(userInfoReq.getUnionId()))) {
throw new BizException(BizExceptionEnum.ERROR_PARAM);
}
//2、查询
if (userInfoReq.getUserId() != null) {
user = userRepository.findByIdAndTenantId(userInfoReq.getUserId(), sessionStruct.getTenantId());
}
if( StringUtils.isNotEmpty(userInfoReq.getUuid())){
user = userRepository.findByUuidAndTenantId(userInfoReq.getUuid(), sessionStruct.getTenantId());
}
if(StringUtils.isNotEmpty(userInfoReq.getPhoneNo())){
user = userRepository.findByPhoneNoAndTenantId(userInfoReq.getPhoneNo(), sessionStruct.getTenantId());
}
if(StringUtils.isNotEmpty(userInfoReq.getAppId()) && StringUtils.isNotEmpty(userInfoReq.getOpenId())){
WechatUserInfo wechatUserInfo = wechatService.findWechatUserInfoFromDb(userInfoReq.getOpenId(), userInfoReq.getAppId(), sessionStruct.getTenantId());
if (wechatUserInfo == null || wechatUserInfo.getUserId() == null) {
throw new BizException(BizExceptionEnum.UN_EXIT_USER);
}
user = userRepository.findByIdAndTenantId(wechatUserInfo.getUserId(), sessionStruct.getTenantId());
}
if(StringUtils.isNotEmpty(userInfoReq.getAppId()) && StringUtils.isNotEmpty(userInfoReq.getUnionId())){
WechatUserInfo wechatUserInfo = wechatService.findByUnionIdAndAppIdAndTenantId(userInfoReq.getUnionId(), userInfoReq.getAppId(), sessionStruct.getTenantId());
if (wechatUserInfo == null || wechatUserInfo.getUserId() == null) {
throw new BizException(BizExceptionEnum.UN_EXIT_USER);
}
user = userRepository.findByIdAndTenantId(wechatUserInfo.getUserId(), sessionStruct.getTenantId());
}
//3、异常处理
if (user == null) {
throw new BizException(BizExceptionEnum.UN_EXIT_USER);
}
return JsonResult.buildSuccessResultGeneric(user);
}
/**
* 批量查询用户信息
*
* @param batchInfoReq 批量查询条件
* @return JsonResult<List < User>>
* @see <a href="http://yapi.quantgroups.com/project/17/interface/api/65734">批量查询用户信息</a>
*/
@PostMapping("/batchInfo")
public JsonResult<List<User>> batchInfo(@RequestBody BatchInfoReq batchInfoReq) {
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
List<User> userList = null;
//1、校验
if (CollectionUtils.isEmpty(batchInfoReq.getUserIds())
&& CollectionUtils.isEmpty(batchInfoReq.getUuids())
&& CollectionUtils.isEmpty(batchInfoReq.getPhoneNos())
&& (StringUtils.isEmpty(batchInfoReq.getAppId()) && CollectionUtils.isEmpty(batchInfoReq.getOpenIds()))
&& (StringUtils.isEmpty(batchInfoReq.getAppId()) && CollectionUtils.isEmpty(batchInfoReq.getUnionIds()))) {
throw new BizException(BizExceptionEnum.ERROR_PARAM);
}
//2、查询
if (CollectionUtils.isNotEmpty(batchInfoReq.getUserIds())) {
userList = userRepository.findByIdInAndTenantId(batchInfoReq.getUserIds(), sessionStruct.getTenantId());
}
if( CollectionUtils.isNotEmpty(batchInfoReq.getUuids())){
userList = userRepository.findByUuidInAndTenantId(batchInfoReq.getUuids(), sessionStruct.getTenantId());
}
if(CollectionUtils.isNotEmpty(batchInfoReq.getPhoneNos())){
userList = userRepository.findByPhoneNoInAndTenantId(batchInfoReq.getPhoneNos(), sessionStruct.getTenantId());
}
if(StringUtils.isNotEmpty(batchInfoReq.getAppId()) && CollectionUtils.isNotEmpty(batchInfoReq.getOpenIds())){
List<WechatUserInfo> wechatUserInfo = wechatService.findWechatUserInfoFromDb(batchInfoReq.getOpenIds(), batchInfoReq.getAppId(), sessionStruct.getTenantId());
List<Long> userIds = wechatUserInfo.stream().map(WechatUserInfo::getUserId).collect(Collectors.toList());;
if (CollectionUtils.isEmpty(wechatUserInfo) || CollectionUtils.isEmpty(userIds)) {
throw new BizException(BizExceptionEnum.UN_EXIT_USER);
}
userList = userRepository.findByIdInAndTenantId(userIds, sessionStruct.getTenantId());
}
if(StringUtils.isNotEmpty(batchInfoReq.getAppId()) && CollectionUtils.isNotEmpty(batchInfoReq.getUnionIds())){
List<WechatUserInfo> wechatUserInfo = wechatService.findUnionIdsAndOpenIdAndTenantId(batchInfoReq.getUnionIds(), batchInfoReq.getAppId(), sessionStruct.getTenantId());
List<Long> userIds = wechatUserInfo.stream().map(WechatUserInfo::getUserId).collect(Collectors.toList());;
if (CollectionUtils.isEmpty(wechatUserInfo) || CollectionUtils.isEmpty(userIds)) {
throw new BizException(BizExceptionEnum.UN_EXIT_USER);
}
userList = userRepository.findByIdInAndTenantId(userIds, sessionStruct.getTenantId());
}
//3、异常处理
if (CollectionUtils.isEmpty(userList)) {
throw new BizException(BizExceptionEnum.UN_EXIT_USER);
}
return JsonResult.buildSuccessResultGeneric(userList);
}
}
...@@ -4,10 +4,13 @@ import cn.quantgroup.sms.MsgParams; ...@@ -4,10 +4,13 @@ import cn.quantgroup.sms.MsgParams;
import cn.quantgroup.tech.util.TechEnvironment; import cn.quantgroup.tech.util.TechEnvironment;
import cn.quantgroup.xyqb.Constants; import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator; import cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator;
import cn.quantgroup.xyqb.constant.UserConstant;
import cn.quantgroup.xyqb.controller.IBaseController; import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.model.sms.SmsMerchant; import cn.quantgroup.xyqb.model.sms.SmsMerchant;
import cn.quantgroup.xyqb.service.sms.ISmsService; import cn.quantgroup.xyqb.service.sms.ISmsService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import cn.quantgroup.xyqb.util.DateUtils; import cn.quantgroup.xyqb.util.DateUtils;
import cn.quantgroup.xyqb.util.IpUtil; import cn.quantgroup.xyqb.util.IpUtil;
import cn.quantgroup.xyqb.util.ValidationUtil; import cn.quantgroup.xyqb.util.ValidationUtil;
...@@ -16,7 +19,6 @@ import org.apache.commons.lang3.RandomStringUtils; ...@@ -16,7 +19,6 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -44,19 +46,33 @@ public class SmsController implements IBaseController { ...@@ -44,19 +46,33 @@ public class SmsController implements IBaseController {
private static final String IMAGE_IP_COUNT = "image:ip"; private static final String IMAGE_IP_COUNT = "image:ip";
private static final String IMAGE_PHONE_COUNT = "image:phone"; private static final String IMAGE_PHONE_COUNT = "image:phone";
private static final String IMAGE_DEVICEID_COUNT = "image:deviceId:"; private static final String IMAGE_DEVICEID_COUNT = "image:deviceId:";
/** ip上限 */ /**
* ip上限
*/
private static final Long IP_MAX_PER_DAY = 5000L; private static final Long IP_MAX_PER_DAY = 5000L;
/** 手机号短信上限 */ /**
* 手机号短信上限
*/
private static final Long PHONE_MAX_PER_DAY = 20L; private static final Long PHONE_MAX_PER_DAY = 20L;
/** 手机号语音上限 */ /**
* 手机号语音上限
*/
private static final Long PHONE_VOICE_MAX_PER_DAY = 5L; private static final Long PHONE_VOICE_MAX_PER_DAY = 5L;
/** 设备每天上限 */ /**
* 设备每天上限
*/
private static final Long DEVICE_MAX_PER_DAY = 20L; private static final Long DEVICE_MAX_PER_DAY = 20L;
/** 手机号语音上限-KEY-4 */ /**
* 手机号语音上限-KEY-4
*/
private static final String PHONE_VOICE_MAX_PER_DAY_KEY_4 = "4"; private static final String PHONE_VOICE_MAX_PER_DAY_KEY_4 = "4";
/** 手机号语音上限-KEY-5 */ /**
* 手机号语音上限-KEY-5
*/
private static final String PHONE_VOICE_MAX_PER_DAY_KEY_5 = "5"; private static final String PHONE_VOICE_MAX_PER_DAY_KEY_5 = "5";
/** 手机号语音上限-KEY-6 */ /**
* 手机号语音上限-KEY-6
*/
private static final String PHONE_VOICE_MAX_PER_DAY_KEY_6 = "6"; private static final String PHONE_VOICE_MAX_PER_DAY_KEY_6 = "6";
/** /**
...@@ -198,8 +214,13 @@ public class SmsController implements IBaseController { ...@@ -198,8 +214,13 @@ public class SmsController implements IBaseController {
if (!ValidationUtil.validatePhoneNo(phoneNo)) { if (!ValidationUtil.validatePhoneNo(phoneNo)) {
return JsonResult.buildErrorStateResult("手机号格式有误", null); return JsonResult.buildErrorStateResult("手机号格式有误", null);
} }
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo; String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
if (sessionStruct != null && !UserConstant.defaultTenantId.equals(sessionStruct.getTenantId())) {
key = Constants.REDIS_PREFIX_VERIFICATION_CODE + sessionStruct.getTenantId() + "_" +phoneNo;
}
long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES); long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES);
if (expire >= EXPIRE_MINUTES - 1) { if (expire >= EXPIRE_MINUTES - 1) {
return JsonResult.buildErrorStateResult("1分钟内不能重复获取验证码", null); return JsonResult.buildErrorStateResult("1分钟内不能重复获取验证码", null);
...@@ -207,9 +228,6 @@ public class SmsController implements IBaseController { ...@@ -207,9 +228,6 @@ public class SmsController implements IBaseController {
String uniqueId = phoneNo + UUID.randomUUID().toString().replaceAll("-", ""); String uniqueId = phoneNo + UUID.randomUUID().toString().replaceAll("-", "");
List<String> newList = new ArrayList<>(); List<String> newList = new ArrayList<>();
newList.add(randomCode); newList.add(randomCode);
/*ConfirmableMsg confirmableMsg = new ConfirmableMsg(
uniqueId, newList, "1", "1", phoneNo
);*/
MsgParams message = new MsgParams.Builder() MsgParams message = new MsgParams.Builder()
.typeList(Collections.singletonList(2)) .typeList(Collections.singletonList(2))
.phoneNo(phoneNo) .phoneNo(phoneNo)
...@@ -220,7 +238,6 @@ public class SmsController implements IBaseController { ...@@ -220,7 +238,6 @@ public class SmsController implements IBaseController {
.channel(appName) .channel(appName)
.build(); .build();
try { try {
//smsService.getSmsSender().sendConfirmableMessage(confirmableMsg);
smsService.getSmsSender().sendMsg(message); smsService.getSmsSender().sendMsg(message);
redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES); redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES);
deleteRetSendCode(phoneNo);//删除用户重置密码,多次错误逻辑 deleteRetSendCode(phoneNo);//删除用户重置密码,多次错误逻辑
...@@ -242,7 +259,13 @@ public class SmsController implements IBaseController { ...@@ -242,7 +259,13 @@ public class SmsController implements IBaseController {
if (!ValidationUtil.validatePhoneNo(phoneNo)) { if (!ValidationUtil.validatePhoneNo(phoneNo)) {
return JsonResult.buildErrorStateResult("手机号格式有误", null); return JsonResult.buildErrorStateResult("手机号格式有误", null);
} }
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo; String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
if (sessionStruct != null && !UserConstant.defaultTenantId.equals(sessionStruct.getTenantId())) {
key = Constants.REDIS_PREFIX_VERIFICATION_CODE + sessionStruct.getTenantId() + "_" +phoneNo;
}
long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES); long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES);
if (expire >= EXPIRE_MINUTES - 1) { if (expire >= EXPIRE_MINUTES - 1) {
return JsonResult.buildErrorStateResult("1分钟内不能重复获取验证码", null); return JsonResult.buildErrorStateResult("1分钟内不能重复获取验证码", null);
...@@ -340,10 +363,10 @@ public class SmsController implements IBaseController { ...@@ -340,10 +363,10 @@ public class SmsController implements IBaseController {
} }
public String getRandomCode(int count) { public String getRandomCode(int count) {
if(TechEnvironment.isPro()) { if (TechEnvironment.isPro()) {
return RandomStringUtils.random(count, RANDOM_CHARS); return RandomStringUtils.random(count, RANDOM_CHARS);
} }
switch (count){ switch (count) {
case 6: case 6:
return "000000"; return "000000";
default: default:
...@@ -398,7 +421,13 @@ public class SmsController implements IBaseController { ...@@ -398,7 +421,13 @@ public class SmsController implements IBaseController {
return JsonResult.buildErrorStateResult("您当前ip已经达到获取今天短信验证码上限", null); return JsonResult.buildErrorStateResult("您当前ip已经达到获取今天短信验证码上限", null);
} }
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo; String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
if (sessionStruct != null && !UserConstant.defaultTenantId.equals(sessionStruct.getTenantId())) {
key = Constants.REDIS_PREFIX_VERIFICATION_CODE + sessionStruct.getTenantId() + "_" +phoneNo;
}
long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES); long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES);
if (expire >= EXPIRE_MINUTES - 1) { if (expire >= EXPIRE_MINUTES - 1) {
log.info("sendVerificationCode2New1分钟内不能重复获取验证码:phoneNo:{},deviceId:{},ip:{}", phoneNo, deviceId, clientIp); log.info("sendVerificationCode2New1分钟内不能重复获取验证码:phoneNo:{},deviceId:{},ip:{}", phoneNo, deviceId, clientIp);
...@@ -479,7 +508,13 @@ public class SmsController implements IBaseController { ...@@ -479,7 +508,13 @@ public class SmsController implements IBaseController {
return JsonResult.buildErrorStateResult("您当前ip已经达到获取今天语音验证码上限", null); return JsonResult.buildErrorStateResult("您当前ip已经达到获取今天语音验证码上限", null);
} }
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo; String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
if (sessionStruct != null && !UserConstant.defaultTenantId.equals(sessionStruct.getTenantId())) {
key = Constants.REDIS_PREFIX_VERIFICATION_CODE + sessionStruct.getTenantId() + "_" +phoneNo;
}
long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES); long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES);
if (expire >= EXPIRE_MINUTES - 1) { if (expire >= EXPIRE_MINUTES - 1) {
return JsonResult.buildErrorStateResult("1分钟内不能重复获取验证码", null); return JsonResult.buildErrorStateResult("1分钟内不能重复获取验证码", null);
......
package cn.quantgroup.xyqb.controller.req.v2;
import lombok.Data;
import lombok.Getter;
import java.util.List;
/**
* 每组有且仅能有一组userId、phoneNo、uuid、openId和appId、unionId和appId
*/
@Data
public class BatchInfoReq {
/**
* 用户id
*/
private List<Long> userIds;
/**
* 手机号码
*/
private List<String> phoneNos;
/**
* 用户uuid
*/
private List<String> uuids;
/**
* 微信openId
*/
private List<String> openIds;
/**
* 微信unionId
*/
private List<String> unionIds;
/**
* 微信appid
*/
private String appId;
}
...@@ -18,7 +18,7 @@ public class BehaviorReq { ...@@ -18,7 +18,7 @@ public class BehaviorReq {
/** /**
* 手机号码 * 手机号码
*/ */
@Size(min = 11,max = 20) @Size(max = 20)
private String phone; private String phone;
/** /**
......
package cn.quantgroup.xyqb.controller.req.v2;
import lombok.Data;
import javax.validation.constraints.Size;
@Data
public class SMSReq {
/**
* 0:是短信,1是语音,默认值是0
*/
private Integer type = 0;
/**
* 手机号码,必填
*/
@Size(min = 11,max = 18)
private String phoneNo;
/**
* 场景类型,0:是普通默认短信,1是登录,2是重置密码
*/
private Integer sceneType = 0;
/**
* 指定验证码长度,可选,4-10位
*/
private Integer codeLength;
/**
* 指定模版参数,可选,需要自己指定模版时选用
*/
private String smsMerchant;
}
package cn.quantgroup.xyqb.controller.req.v2;
import lombok.Data;
import javax.validation.constraints.Size;
@Data
public class SMSVerifyReq {
/**
* 手机号码,必填
*/
@Size(min = 11,max = 18)
private String phoneNo;
/**
* 验证码,必填,4-10位
*/
@Size(min = 4,max = 10)
private String code;
}
package cn.quantgroup.xyqb.controller.req.v2;
import lombok.Data;
/**
* 每组有且仅能有一组userId、phoneNo、uuid、openId和appId、unionId和appId
*/
@Data
public class UserInfoReq {
/**
* 用户id
*/
private Long userId;
/**
* 手机号码
*/
private String phoneNo;
/**
* 用户uuid
*/
private String uuid;
/**
* 微信openId
*/
private String openId;
/**
* 微信unionId
*/
private String unionId;
/**
* 微信appid
*/
private String appId;
}
...@@ -10,6 +10,8 @@ import org.springframework.web.bind.annotation.RequestBody; ...@@ -10,6 +10,8 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController @RestController
@RequestMapping("/v2/behavior") @RequestMapping("/v2/behavior")
public class BehaviorV2Controller { public class BehaviorV2Controller {
...@@ -22,7 +24,7 @@ public class BehaviorV2Controller { ...@@ -22,7 +24,7 @@ public class BehaviorV2Controller {
* @see <a href="http://yapi.quantgroups.com/project/17/interface/api/65824">统一获取行为验证码</a> * @see <a href="http://yapi.quantgroups.com/project/17/interface/api/65824">统一获取行为验证码</a>
*/ */
@PostMapping("/code") @PostMapping("/code")
public JsonResult<BehaviorCodeBean> getCode(@RequestBody BehaviorReq behaviorReq){ public JsonResult<BehaviorCodeBean> getCode(@Valid @RequestBody BehaviorReq behaviorReq){
return JsonResult.buildSuccessResultGeneric(behaviorContext.generate(behaviorReq)); return JsonResult.buildSuccessResultGeneric(behaviorContext.generate(behaviorReq));
} }
} }
package cn.quantgroup.xyqb.controller.v2; package cn.quantgroup.xyqb.controller.v2;
import cn.quantgroup.xyqb.controller.req.v2.LoginReq; import cn.quantgroup.xyqb.controller.req.v2.SMSReq;
import cn.quantgroup.xyqb.controller.req.v2.SMSVerifyReq;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.LoginBean;
import cn.quantgroup.xyqb.model.SMSCodeBean; import cn.quantgroup.xyqb.model.SMSCodeBean;
import cn.quantgroup.xyqb.service.v2.VerificationCodeContext;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -13,15 +14,20 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -13,15 +14,20 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/v2/sms") @RequestMapping("/v2/sms")
public class SMSV2controller { public class SMSV2controller {
private final VerificationCodeContext verificationCodeContext;
public SMSV2controller(VerificationCodeContext verificationCodeContext) {
this.verificationCodeContext = verificationCodeContext;
}
/** /**
* 统一获取短信验证码 * 统一获取短信验证码
* @return JsonResult<SMSCodeBean> * @return JsonResult<SMSCodeBean>
* @see <a href="http://yapi.quantgroups.com/project/17/interface/api/65709">统一获取短信验证码</a> * @see <a href="http://yapi.quantgroups.com/project/17/interface/api/65709">统一获取短信验证码</a>
*/ */
@PostMapping("/code") @PostMapping("/code")
public JsonResult<SMSCodeBean> getCode(){ public JsonResult<SMSCodeBean> getCode(@RequestBody SMSReq smsReq){
return JsonResult.buildSuccessResultGeneric(verificationCodeContext.send(smsReq));
return null;
} }
/** /**
...@@ -30,9 +36,9 @@ public class SMSV2controller { ...@@ -30,9 +36,9 @@ public class SMSV2controller {
* @see <a href="http://yapi.quantgroups.com/project/17/interface/api/65829">...</a> * @see <a href="http://yapi.quantgroups.com/project/17/interface/api/65829">...</a>
*/ */
@PostMapping("/verify") @PostMapping("/verify")
public JsonResult<Boolean> verify(){ public JsonResult<Boolean> verify(@RequestBody SMSVerifyReq smsVerifyReq){
return null; return JsonResult.buildSuccessResultGeneric(verificationCodeContext.verify(smsVerifyReq));
} }
......
...@@ -19,6 +19,12 @@ public enum BizExceptionEnum { ...@@ -19,6 +19,12 @@ public enum BizExceptionEnum {
ERROR_DIRECT_LOGIN("1008","极验登录验证失败"), ERROR_DIRECT_LOGIN("1008","极验登录验证失败"),
ERROR_WECHAT_APP_ID("1009","请先配置微信appId"), ERROR_WECHAT_APP_ID("1009","请先配置微信appId"),
ERROR_WECHAT_LOGIN("1010","微信登录失败"), ERROR_WECHAT_LOGIN("1010","微信登录失败"),
ERROR_INTERNAL_LOGIN("1011","公开接口不能调用内部登录方式"),
ERROR_LOGIN_PARAM("1012","登录参数异常,请按接口文档对接"),
UN_EXIT_STMS_TOKEN("1013","stms的token不存在"),
UN_VALID_STMS_TOKEN("1014","无效的stms的token"),
UN_PERMISSION_STMS("1015","没有内部接口访问权限"),
...@@ -28,8 +34,16 @@ public enum BizExceptionEnum { ...@@ -28,8 +34,16 @@ public enum BizExceptionEnum {
UN_EXIT_VERIFY_TYPE("2003","不存在的验证码方式"), UN_EXIT_VERIFY_TYPE("2003","不存在的验证码方式"),
UN_EXIT_VERIFY_CODE("2004","验证模式下验证码参数不能为空"), UN_EXIT_VERIFY_CODE("2004","验证模式下验证码参数不能为空"),
EX_GET_VERIFY_CODE("2005","获取验证码失败"), EX_GET_VERIFY_CODE("2005","获取验证码失败"),
UN_EXIT_GEETEST_LOG("2006","极验记录不存在"); UN_EXIT_GEETEST_LOG("2006","极验记录不存在"),
PHONE_MAX_PER_DAY("2007","今天已获取20次短信验证码,请使用语音验证码或明天再试"),
DEVICE_MAX_PER_DAY("2008","您设备已经达到获取今天验证码上限"),
IP_MAX_PER_DAY("2009","您当前ip已经达到获取今天短信验证码上限"),
DUPLICATE_MIN("2010","1分钟内不能重复获取验证码"),
PHONE_VOICE_MAX_PER_DAY("2011","今天已获取5次语音验证码,请使用短信验证码或明天再试"),
ERROR_USAGE("2022","参数校验失败,用户登录语音验证码usage不正确"),
ERROR_SEND_SMS("2023","验证码发送失败"),
//通用记录
ERROR_PARAM("4000","参数不错误");
private final String businessCode; private final String businessCode;
private final String msg; private final String msg;
......
package cn.quantgroup.xyqb.filter;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.exception.BizException;
import cn.quantgroup.xyqb.exception.BizExceptionEnum;
import cn.quantgroup.xyqb.model.OauthResult;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.remote.StmsRemoteService;
import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import cn.quantgroup.xyqb.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
public class InnerInterceptor implements HandlerInterceptor {
private final ISessionService sessionService;
private final StmsRemoteService stmsRemoteService;
public InnerInterceptor(ISessionService sessionService, StmsRemoteService stmsRemoteService) {
this.sessionService = sessionService;
this.stmsRemoteService = stmsRemoteService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//所有开放出去的外部接口,都需要验证租户id和注册来源
String tenantId = request.getHeader(Constants.X_AUTH_TENANT);
String registeredFrom = request.getHeader(Constants.X_AUTH_FROM);
String stmsToken = request.getHeader(Constants.X_AUTH_TOKEN);
if (StringUtils.isEmpty(tenantId)) {
throw new BizException(BizExceptionEnum.UN_EXIT_TENANT_ID);
}
if (StringUtils.isEmpty(registeredFrom)) {
throw new BizException(BizExceptionEnum.UN_EXIT_REGISTERED_FROM);
}
if (StringUtils.isEmpty(stmsToken)) {
throw new BizException(BizExceptionEnum.UN_EXIT_STMS_TOKEN);
}
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSessionFromRedis(stmsToken);
if (sessionStruct == null) {
OauthResult oauthResult = stmsRemoteService.checkToken(stmsToken);
if (oauthResult != null && 2000 == oauthResult.getCode()) {
LinkedHashMap<String,Object> linkedHashMap = (LinkedHashMap<String, Object>) oauthResult.getData();
String userId = String.valueOf(linkedHashMap.get("id"));
String userName = String.valueOf(linkedHashMap.get("name"));
sessionStruct = XyqbSessionContextHolder.initSTMSSession(stmsToken, userId, userName);
sessionService.persistSession(sessionStruct.getSid(), sessionStruct.getValues(), sessionStruct.getTenantId());
} else {
throw new BizException(BizExceptionEnum.UN_PERMISSION_STMS);
}
} else {
//session续期
sessionService.persistSession(sessionStruct.getSid(), sessionStruct.getValues(), sessionStruct.getTenantId());
}
//校验接口权限
OauthResult permissionResult = stmsRemoteService.checkPermission(stmsToken,
request.getRequestURI(), tenantId);
if (permissionResult != null && 2000 == permissionResult.getCode()) {
return true;
} else {
throw new BizException(BizExceptionEnum.UN_VALID_STMS_TOKEN);
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
XyqbSessionContextHolder.releaseSession();
}
}
...@@ -21,12 +21,14 @@ public class TokenInterceptor implements HandlerInterceptor { ...@@ -21,12 +21,14 @@ public class TokenInterceptor implements HandlerInterceptor {
public TokenInterceptor(ISessionService sessionService) { public TokenInterceptor(ISessionService sessionService) {
this.sessionService = sessionService; this.sessionService = sessionService;
excludes.add("/v2/oauth/login"); excludes.add("/v2/oauth/login");
excludes.add("/v2/sms/code");
excludes.add("/v2/sms/verify");
} }
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//所有开放出去的外部接口,都需要验证租户id和注册来源 //所有开放出去的外部接口,都需要验证租户id和注册来源
String tenantId = request.getHeader(Constants.X_AUTH_TENANT); String tenantId = request.getHeader(Constants.X_AUTH_TENANT);
String registered_from = request.getHeader(Constants.X_AUTH_FROM); String registered_from = request.getHeader(Constants.X_AUTH_FROM);
if (StringUtils.isEmpty(tenantId)) { if (StringUtils.isEmpty(tenantId)) {
...@@ -48,7 +50,9 @@ public class TokenInterceptor implements HandlerInterceptor { ...@@ -48,7 +50,9 @@ public class TokenInterceptor implements HandlerInterceptor {
} else { } else {
//如果是排除的接口,同时sessionStruct为空,这时候初始化租户和来源字段 //如果是排除的接口,同时sessionStruct为空,这时候初始化租户和来源字段
if (sessionStruct == null) { if (sessionStruct == null) {
XyqbSessionContextHolder.initXSession(); sessionStruct = XyqbSessionContextHolder.initXSession();
sessionService.persistSession(sessionStruct.getSid(), sessionStruct.getValues(), sessionStruct.getTenantId());
} }
} }
......
...@@ -13,7 +13,13 @@ public class BehaviorCodeBean { ...@@ -13,7 +13,13 @@ public class BehaviorCodeBean {
* 当客户端给的是极验,但是由于极验不可用的时候,会返回降级量化派图形验证码 * 当客户端给的是极验,但是由于极验不可用的时候,会返回降级量化派图形验证码
*/ */
private Integer type; private Integer type;
/**
* 量化派图形验证码时返回
*/
private QtCode qtCode; private QtCode qtCode;
/**
* 极光验证时返回
*/
private GeetestCode geetestCode; private GeetestCode geetestCode;
private String id; private String id;
......
...@@ -38,10 +38,24 @@ public class LoginProperties { ...@@ -38,10 +38,24 @@ public class LoginProperties {
//租户ID //租户ID
private Integer tenantId; private Integer tenantId;
private String stmsUserId;
private String stmsUserName;
public LoginProperties(int action, Long createdFrom, Integer tenantId) { public LoginProperties(int action, Long createdFrom, Integer tenantId) {
this.action = action; this.action = action;
this.createdFrom = createdFrom; this.createdFrom = createdFrom;
this.tenantId = tenantId; this.tenantId = tenantId;
} }
public LoginProperties(String dimension, int action, Long channelId, Long createdFrom, String appChannel, Long merchantId, String merchantName, Integer tenantId) {
this.dimension = dimension;
this.action = action;
this.channelId = channelId;
this.createdFrom = createdFrom;
this.appChannel = appChannel;
this.merchantId = merchantId;
this.merchantName = merchantName;
this.tenantId = tenantId;
}
} }
...@@ -4,4 +4,5 @@ import lombok.Data; ...@@ -4,4 +4,5 @@ import lombok.Data;
@Data @Data
public class SMSCodeBean { public class SMSCodeBean {
private String code;
} }
...@@ -28,6 +28,9 @@ public class SessionStruct implements Serializable { ...@@ -28,6 +28,9 @@ public class SessionStruct implements Serializable {
private String scDeviceId; private String scDeviceId;
private String terminal; private String terminal;
private String stmsUserId;
private String stmsUserName;
public void setAttribute(String key, String value) { public void setAttribute(String key, String value) {
if (value == null) { if (value == null) {
......
package cn.quantgroup.xyqb.model.v2.login;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* userId、uuid、openId与appId、unionId与appId有且只能有一组
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class InterLoginParam extends BaseLoginParam{
//用户id
private Long userId;
private String uuid;
private String openId;
private String unionId;
private String appId;
}
...@@ -2,11 +2,18 @@ package cn.quantgroup.xyqb.remote; ...@@ -2,11 +2,18 @@ package cn.quantgroup.xyqb.remote;
import cn.quantgroup.xyqb.model.OauthResult; import cn.quantgroup.xyqb.model.OauthResult;
import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "stms", url = "${stms.https}") @FeignClient(name = "stms", url = "${stms.https}")
public interface StmsRemoteService { public interface StmsRemoteService {
@PostMapping("/v2/oauth/currentuserinfo") @PostMapping("/v2/oauth/currentuserinfo")
OauthResult checkToken(@RequestHeader("Access-Token") String accessToken); OauthResult checkToken(@RequestHeader("Access-Token") String accessToken);
@GetMapping(value = "/v2/oauth/permission")
OauthResult checkPermission(@RequestHeader("Access-Token") String accessToken,
@RequestHeader("Referer") String referer, @RequestParam("tenantId") String tenantId);
} }
...@@ -16,6 +16,7 @@ import java.util.List; ...@@ -16,6 +16,7 @@ import java.util.List;
public interface IUserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { public interface IUserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
User findByPhoneNoAndTenantId(String phoneNo,Integer tenantId); User findByPhoneNoAndTenantId(String phoneNo,Integer tenantId);
List<User> findByPhoneNoInAndTenantId(List<String> phoneNo,Integer tenantId);
User findByEncryptedPhoneNoAndTenantId(String phoneNo,Integer tenantId); User findByEncryptedPhoneNoAndTenantId(String phoneNo,Integer tenantId);
......
...@@ -16,8 +16,15 @@ import static org.springframework.transaction.annotation.Propagation.MANDATORY; ...@@ -16,8 +16,15 @@ import static org.springframework.transaction.annotation.Propagation.MANDATORY;
* modify by djh 20200527 http://confluence.quantgroup.cn/pages/viewpage.action?pageId=30657427 * modify by djh 20200527 http://confluence.quantgroup.cn/pages/viewpage.action?pageId=30657427
*/ */
public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Long> { public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Long> {
WechatUserInfo findByOpenIdAndAppNameAndAppIdAndTenantId(String openId, String appName,String appId,Integer tenantId); WechatUserInfo findByOpenIdAndAppNameAndAppIdAndTenantId(String openId, String appName, String appId, Integer tenantId);
WechatUserInfo findByOpenIdAndAppIdAndTenantId(String openId,String appId,Integer tenantId);
List<WechatUserInfo> findByOpenIdInAndAppIdAndTenantId(List<String> openId, String appId, Integer tenantId);
List<WechatUserInfo> findByUnionIdInAndAppIdAndTenantId(List<String> unionIds, String appId, Integer tenantId);
WechatUserInfo findByUnionIdAndAppIdAndTenantId(String openId, String appId, Integer tenantId);
WechatUserInfo findByOpenIdAndAppIdAndTenantId(String openId, String appId, Integer tenantId);
WechatUserInfo findByPhoneNoAndAppNameAndTenantId(String phoneNo, String appName, Integer tenantId); WechatUserInfo findByPhoneNoAndAppNameAndTenantId(String phoneNo, String appName, Integer tenantId);
...@@ -25,7 +32,7 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon ...@@ -25,7 +32,7 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon
WechatUserInfo findFirstByUserIdAndAppNameAndTenantIdOrderByCreatedAtDesc(Long userId, String appName, Integer tenantId); WechatUserInfo findFirstByUserIdAndAppNameAndTenantIdOrderByCreatedAtDesc(Long userId, String appName, Integer tenantId);
long countByOpenIdAndAppNameAndAppIdAndTenantId(String openId, String appName,String appId,Integer tenantId); long countByOpenIdAndAppNameAndAppIdAndTenantId(String openId, String appName, String appId, Integer tenantId);
/** /**
* 解除关联关系 -- 当前用户的已关联微信 * 解除关联关系 -- 当前用户的已关联微信
...@@ -50,7 +57,7 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon ...@@ -50,7 +57,7 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Modifying @Modifying
@Query(value = "update wechat_userinfo set user_id=?1,phone_no=?2 where open_id=?3 and user_id is null and app_name=?4 and app_id=?5 and tenant_id=?6", nativeQuery = true) @Query(value = "update wechat_userinfo set user_id=?1,phone_no=?2 where open_id=?3 and user_id is null and app_name=?4 and app_id=?5 and tenant_id=?6", nativeQuery = true)
int relateUser(Long userId, String phoneNo, String openId, String appName,String appId,Integer tenantId); int relateUser(Long userId, String phoneNo, String openId, String appName, String appId, Integer tenantId);
/** /**
* 解除关联关系 -- 包括:1、当前微信旧的关联用户;2、当前用户旧的关联微信 * 解除关联关系 -- 包括:1、当前微信旧的关联用户;2、当前用户旧的关联微信
...@@ -63,11 +70,12 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon ...@@ -63,11 +70,12 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon
@Transactional(propagation = MANDATORY, rollbackFor = Exception.class) @Transactional(propagation = MANDATORY, rollbackFor = Exception.class)
@Modifying @Modifying
@Query(value = "update wechat_userinfo set user_id=null,phone_no='*' where (open_id=?1 or user_id=?2) and app_name=?3 and app_id=?4 and tenant_id=?5", nativeQuery = true) @Query(value = "update wechat_userinfo set user_id=null,phone_no='*' where (open_id=?1 or user_id=?2) and app_name=?3 and app_id=?4 and tenant_id=?5", nativeQuery = true)
int dissociateUser(String openId, Long userId, String appName,String appId,Integer tenantId); int dissociateUser(String openId, Long userId, String appName, String appId, Integer tenantId);
/** /**
* 更新unionId * 更新unionId
*
* @param userId * @param userId
* @param appName * @param appName
* @param unionId * @param unionId
...@@ -76,7 +84,7 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon ...@@ -76,7 +84,7 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Modifying @Modifying
@Query(value = "update wechat_userinfo set union_id=?3 where user_id=?1 and app_name=?2 and app_id=?3 and tenant_id=?4", nativeQuery = true) @Query(value = "update wechat_userinfo set union_id=?3 where user_id=?1 and app_name=?2 and app_id=?3 and tenant_id=?4", nativeQuery = true)
int updateUserUnionId(Long userId, String appName, String unionId,String appId,Integer tenantId); int updateUserUnionId(Long userId, String appName, String unionId, String appId, Integer tenantId);
@Transactional @Transactional
void deleteByUserIdAndTenantId(Long userId, Integer tenantId); void deleteByUserIdAndTenantId(Long userId, Integer tenantId);
...@@ -84,9 +92,9 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon ...@@ -84,9 +92,9 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon
/** /**
* 通过userId查询相关绑定微信记录 * 通过userId查询相关绑定微信记录
*/ */
List<WechatUserInfo> findByUserIdAndTenantId(Long userId,Integer tenantId); List<WechatUserInfo> findByUserIdAndTenantId(Long userId, Integer tenantId);
List<WechatUserInfo> findByPhoneNoAndTenantId(String phoneNo,Integer tenantId); List<WechatUserInfo> findByPhoneNoAndTenantId(String phoneNo, Integer tenantId);
/** /**
* 通过unionId查询相关绑定微信记录 * 通过unionId查询相关绑定微信记录
......
package cn.quantgroup.xyqb.service.sms; package cn.quantgroup.xyqb.service.sms;
import cn.quantgroup.sms.SmsSender; import cn.quantgroup.sms.SmsSender;
import cn.quantgroup.xyqb.controller.req.v2.SMSVerifyReq;
/** /**
* 短信发送服务 * 短信发送服务
...@@ -25,6 +26,8 @@ public interface ISmsService { ...@@ -25,6 +26,8 @@ public interface ISmsService {
*/ */
boolean verifyPhoneAndCode(String phoneNo, String verificationCode); boolean verifyPhoneAndCode(String phoneNo, String verificationCode);
boolean verifyPhoneAndCode(SMSVerifyReq smsVerifyReq);
/** /**
* 是否需要重新发送短信验证码 * 是否需要重新发送短信验证码
* *
......
...@@ -4,13 +4,18 @@ import cn.quantgroup.sms.MsgParams; ...@@ -4,13 +4,18 @@ import cn.quantgroup.sms.MsgParams;
import cn.quantgroup.sms.SmsSender; import cn.quantgroup.sms.SmsSender;
import cn.quantgroup.tech.util.TechEnvironment; import cn.quantgroup.tech.util.TechEnvironment;
import cn.quantgroup.xyqb.Constants; import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.constant.UserConstant;
import cn.quantgroup.xyqb.controller.req.v2.SMSVerifyReq;
import cn.quantgroup.xyqb.exception.BizException;
import cn.quantgroup.xyqb.exception.BizExceptionEnum;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.service.sms.ISmsService; import cn.quantgroup.xyqb.service.sms.ISmsService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import lombok.Synchronized; import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -98,6 +103,37 @@ public class SmsServiceImpl implements ISmsService { ...@@ -98,6 +103,37 @@ public class SmsServiceImpl implements ISmsService {
return confirmSms(verificationCode, uniqueId, code); return confirmSms(verificationCode, uniqueId, code);
} }
public boolean verifyPhoneAndCode(SMSVerifyReq smsVerifyReq) {
// 非生产环境直接跳过验证码检验
if (!TechEnvironment.isPro()) {
return true;
}
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
String key;
if (sessionStruct == null) {
throw new BizException(BizExceptionEnum.UN_EXIT_TENANT_ID);
}
if (UserConstant.defaultTenantId.equals(sessionStruct.getTenantId())) {
key = Constants.REDIS_PREFIX_VERIFICATION_CODE + smsVerifyReq.getPhoneNo();
} else {
key = Constants.REDIS_PREFIX_VERIFICATION_CODE + sessionStruct.getTenantId() + "_" + smsVerifyReq.getPhoneNo();
}
String randomCode = stringRedisTemplate.opsForValue().get(key);
if (StringUtils.isBlank(randomCode)) {
return false;
}
String[] arr = randomCode.split(":");
if (arr.length != Constants.VERIFICATION_LEN) {
return false;
}
String uniqueId = arr[0];
String code = arr[1];
return confirmSms(smsVerifyReq.getCode(), uniqueId, code);
}
private boolean confirmSms(String smsVerificationCode, String unqiueId, String code) { private boolean confirmSms(String smsVerificationCode, String unqiueId, String code) {
try { try {
MsgParams message = new MsgParams(Collections.singletonList(2), unqiueId); MsgParams message = new MsgParams(Collections.singletonList(2), unqiueId);
...@@ -114,11 +150,11 @@ public class SmsServiceImpl implements ISmsService { ...@@ -114,11 +150,11 @@ public class SmsServiceImpl implements ISmsService {
@Override @Override
public boolean needResendCode(String phoneNo) { public boolean needResendCode(String phoneNo) {
return needResendCode(phoneNo, Constants.VERIFICATION_CODE_FINITE_COUNT,true); return needResendCode(phoneNo, Constants.VERIFICATION_CODE_FINITE_COUNT, true);
} }
@Override @Override
public boolean needResendCode(String phoneNo, Long threshold,boolean isDelTryCount) { public boolean needResendCode(String phoneNo, Long threshold, boolean isDelTryCount) {
String verificationCountKey = Constants.REDIS_VERIFICATION_COUNT + phoneNo; String verificationCountKey = Constants.REDIS_VERIFICATION_COUNT + phoneNo;
Long getVerificationCount = stringRedisTemplate.opsForHash().increment(verificationCountKey, Constants.REDIS_VERIFICATION_COUNT, 1); Long getVerificationCount = stringRedisTemplate.opsForHash().increment(verificationCountKey, Constants.REDIS_VERIFICATION_COUNT, 1);
...@@ -143,7 +179,12 @@ public class SmsServiceImpl implements ISmsService { ...@@ -143,7 +179,12 @@ public class SmsServiceImpl implements ISmsService {
@Override @Override
public void deleteOnlyCodeFromCache(String phoneNo) { public void deleteOnlyCodeFromCache(String phoneNo) {
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo; String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
if (sessionStruct != null && !UserConstant.defaultTenantId.equals(sessionStruct.getTenantId())) {
key = Constants.REDIS_PREFIX_VERIFICATION_CODE + sessionStruct.getTenantId() + "_" +phoneNo;
}
stringRedisTemplate.delete(key); stringRedisTemplate.delete(key);
} }
......
package cn.quantgroup.xyqb.service.v2;
import cn.quantgroup.xyqb.constant.enums.LoginType;
import cn.quantgroup.xyqb.controller.req.v2.LoginReq;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.exception.BizException;
import cn.quantgroup.xyqb.exception.BizExceptionEnum;
import cn.quantgroup.xyqb.model.AuthBean;
import cn.quantgroup.xyqb.model.LoginBean;
import cn.quantgroup.xyqb.model.LoginProperties;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.model.v2.login.BaseLoginParam;
import cn.quantgroup.xyqb.model.v2.login.InterLoginParam;
import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.service.user.ILockIpv4Service;
import cn.quantgroup.xyqb.service.user.IUserService;
import cn.quantgroup.xyqb.service.wechat.IWechatService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* 短信验证码登录
*/
@Service
@Slf4j
public class InterLoginStrategy implements LoginStrategy {
private final ILockIpv4Service lockIpv4Service;
private final IUserService userService;
private final IWechatService wechatService;
private final ISessionService sessionService;
public InterLoginStrategy(ILockIpv4Service lockIpv4Service, IUserService userService, IWechatService wechatService, ISessionService sessionService) {
this.lockIpv4Service = lockIpv4Service;
this.userService = userService;
this.wechatService = wechatService;
this.sessionService = sessionService;
}
@Override
public Integer getType() {
return 9000;
}
@Override
public LoginBean login(BaseLoginParam param) {
InterLoginParam interLoginParam = (InterLoginParam) param;
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
User user = null;
//1、根据前端的条件,进行查询用户信息
if (interLoginParam.getUserId() != null) {
user = userService.findById(interLoginParam.getUserId(), sessionStruct.getTenantId());
}
if (StringUtils.isNotEmpty(interLoginParam.getUuid())) {
user = userService.findByUuidInDb(interLoginParam.getUuid(), sessionStruct.getTenantId());
}
if (StringUtils.isNotEmpty(interLoginParam.getAppId()) && StringUtils.isNotEmpty(interLoginParam.getOpenId())) {
WechatUserInfo wechatUserInfo = wechatService.findWechatUserInfoFromDb(interLoginParam.getOpenId(), interLoginParam.getAppId(), sessionStruct.getTenantId());
if (wechatUserInfo == null || wechatUserInfo.getUserId() == null) {
throw new BizException(BizExceptionEnum.UN_EXIT_USER);
}
user = userService.findById(wechatUserInfo.getUserId(), sessionStruct.getTenantId());
}
if (StringUtils.isNotEmpty(interLoginParam.getAppId()) && StringUtils.isNotEmpty(interLoginParam.getUnionId())) {
WechatUserInfo wechatUserInfo = wechatService.findByUnionIdAndAppIdAndTenantId(interLoginParam.getUnionId(), interLoginParam.getAppId(), sessionStruct.getTenantId());
if (wechatUserInfo == null || wechatUserInfo.getUserId() == null) {
throw new BizException(BizExceptionEnum.UN_EXIT_USER);
}
user = userService.findById(wechatUserInfo.getUserId(), sessionStruct.getTenantId());
}
if (user == null) {
throw new BizException(BizExceptionEnum.UN_EXIT_USER);
}
//2、需要判断是否是否启用
if (!user.getEnable()) {
log.info("用户不存在,或者已经注销,phoneNo:{}", user.getPhoneNo());
throw new BizException(BizExceptionEnum.ERROR_OR_ENABLE_ERROR);
}
LoginProperties loginProperties = new LoginProperties(1, sessionStruct.getRegisteredFrom(), sessionStruct.getTenantId());
AuthBean authBean = sessionService.createSession(user, loginProperties, LoginType.SUPERLOGIN.ordinal(), sessionStruct.getTenantId());
LoginBean loginBean = new LoginBean();
if (authBean != null) {
loginBean.setToken(authBean.getToken());
loginBean.setPhoneNo(authBean.getPhoneNo());
loginBean.setUuid(authBean.getUuid());
loginBean.setHasPassword(authBean.isHasPassword());
loginBean.setUserId(user.getId());
loginBean.setTenantId(user.getTenantId());
}
loginBean.setRegister(false);
lockIpv4Service.unLockPhone(user.getPhoneNo());
return loginBean;
}
@Override
public BaseLoginParam checkParam(LoginReq loginReq) {
InterLoginParam param = loginReq.getData().toJavaObject(InterLoginParam.class);
if (param.getUserId() == null && StringUtils.isEmpty(param.getUuid())
&& (StringUtils.isEmpty(param.getOpenId()) && StringUtils.isEmpty(param.getAppId())) && (StringUtils.isEmpty(param.getUnionId()) && StringUtils.isEmpty(param.getAppId()))) {
throw new BizException(BizExceptionEnum.ERROR_LOGIN_PARAM);
}
return param;
}
}
...@@ -6,6 +6,8 @@ import cn.quantgroup.xyqb.entity.UserInfoEntity; ...@@ -6,6 +6,8 @@ import cn.quantgroup.xyqb.entity.UserInfoEntity;
import cn.quantgroup.xyqb.entity.UserTag; import cn.quantgroup.xyqb.entity.UserTag;
import cn.quantgroup.xyqb.entity.WechatUserInfo; import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.event.UserLoginEvent; import cn.quantgroup.xyqb.event.UserLoginEvent;
import cn.quantgroup.xyqb.exception.BizException;
import cn.quantgroup.xyqb.exception.BizExceptionEnum;
import cn.quantgroup.xyqb.model.LoginBean; import cn.quantgroup.xyqb.model.LoginBean;
import cn.quantgroup.xyqb.model.UserInfo; import cn.quantgroup.xyqb.model.UserInfo;
import cn.quantgroup.xyqb.model.session.SessionStruct; import cn.quantgroup.xyqb.model.session.SessionStruct;
...@@ -25,7 +27,6 @@ import java.util.Map; ...@@ -25,7 +27,6 @@ import java.util.Map;
@Service @Service
public class LoginContext { public class LoginContext {
private final Map<Integer, LoginStrategy> loginStrategyMap = new HashMap<>();
private final ApplicationEventPublisher applicationEventPublisher; private final ApplicationEventPublisher applicationEventPublisher;
private final IWechatService wechatService; private final IWechatService wechatService;
...@@ -33,17 +34,33 @@ public class LoginContext { ...@@ -33,17 +34,33 @@ public class LoginContext {
private final IUserInfoRepository userInfoRepository; private final IUserInfoRepository userInfoRepository;
private final Map<Integer, LoginStrategy> interloginStrategyMap = new HashMap<>();
public LoginContext(List<LoginStrategy> loginStrategies, ApplicationEventPublisher applicationEventPublisher, IWechatService wechatService, IUserRepository userRepository, IUserInfoRepository userInfoRepository) { public LoginContext(List<LoginStrategy> loginStrategies, ApplicationEventPublisher applicationEventPublisher, IWechatService wechatService, IUserRepository userRepository, IUserInfoRepository userInfoRepository) {
this.applicationEventPublisher = applicationEventPublisher; this.applicationEventPublisher = applicationEventPublisher;
this.wechatService = wechatService; this.wechatService = wechatService;
this.userRepository = userRepository; this.userRepository = userRepository;
this.userInfoRepository = userInfoRepository; this.userInfoRepository = userInfoRepository;
loginStrategies.forEach(i -> loginStrategyMap.put(i.getType(), i)); loginStrategies.forEach(i -> interloginStrategyMap.put(i.getType(), i));
} }
public LoginBean login(LoginReq loginReq) { public LoginBean login(LoginReq loginReq) {
if(loginReq.getType()!=null && loginReq.getType()==9000){
throw new BizException(BizExceptionEnum.ERROR_INTERNAL_LOGIN);
}
return this.interLogin(loginReq);
}
/**
* 内部统一登录
* @param loginReq LoginReq
* @return LoginBean
*/
public LoginBean interLogin(LoginReq loginReq){
//第一步、校验参数 //第一步、校验参数
LoginStrategy loginStrategy = loginStrategyMap.get(loginReq.getType()); LoginStrategy loginStrategy = interloginStrategyMap.get(loginReq.getType());
BaseLoginParam loginParam = loginStrategy.checkParam(loginReq); BaseLoginParam loginParam = loginStrategy.checkParam(loginReq);
......
...@@ -41,7 +41,7 @@ public class QuantGroupBehaviorStrategy implements BehaviorStrategy { ...@@ -41,7 +41,7 @@ public class QuantGroupBehaviorStrategy implements BehaviorStrategy {
@Override @Override
public boolean validate(ValidateBehavior validateBehavior) { public boolean validate(ValidateBehavior validateBehavior) {
boolean result = quantgroupCaptchaService.validQuantgroupCaptcha(validateBehavior.getData().get("id"),validateBehavior.getData().get(Constants.X_BEHAVIOR_IMAGEVALUE)); boolean result = quantgroupCaptchaService.validQuantgroupCaptcha(validateBehavior.getData().get(Constants.X_BEHAVIOR_ID),validateBehavior.getData().get(Constants.X_BEHAVIOR_IMAGEVALUE));
if(result){ if(result){
return true; return true;
}else{ }else{
......
package cn.quantgroup.xyqb.service.v2;
import cn.quantgroup.sms.MsgParams;
import cn.quantgroup.tech.util.TechEnvironment;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.constant.UserConstant;
import cn.quantgroup.xyqb.controller.req.v2.SMSReq;
import cn.quantgroup.xyqb.exception.BizException;
import cn.quantgroup.xyqb.exception.BizExceptionEnum;
import cn.quantgroup.xyqb.model.SMSCodeBean;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.model.sms.SmsMerchant;
import cn.quantgroup.xyqb.service.sms.ISmsService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import cn.quantgroup.xyqb.util.DateUtils;
import cn.quantgroup.xyqb.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class SMSVerificationCodeStrategy implements VerificationCodeStrategy {
@Autowired
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
@Autowired
private ISmsService smsService;
private static final long EXPIRE_MINUTES = 10;
private static final Long PHONE_MAX_PER_DAY = 20L;
/**
* 设备每天上限
*/
private static final Long DEVICE_MAX_PER_DAY = 20L;
/**
* ip上限
*/
private static final Long IP_MAX_PER_DAY = 5000L;
@Override
public Integer getType() {
return 0;
}
@Override
public SMSCodeBean send(SMSReq smsReq) {
SMSCodeBean smsCodeBean = new SMSCodeBean();
int codeLength = Constants.SMS_CODE_LEN_4;
if (smsReq.getCodeLength() != null) {
codeLength = smsReq.getCodeLength();
}
String randomCode = getRandomCode(codeLength);
if (sendVerificationCode2New(smsReq.getPhoneNo(), randomCode, smsReq.getSmsMerchant())) {
smsCodeBean.setCode(randomCode);
return smsCodeBean;
} else {
throw new BizException(BizExceptionEnum.ERROR_SEND_SMS);
}
}
private final static String RANDOM_CHARS = "0123456789";
private String getRandomCode(int count) {
if (TechEnvironment.isPro()) {
return RandomStringUtils.random(count, RANDOM_CHARS);
}
if (count == 6) {
return "000000";
}
return Constants.SUCCESS_CODE;
}
/**
* 新版本短信验证码
*
* @param phoneNo 手机号码
* @param randomCode 随机码
* @param smsMerchant 模版
* @return true成功,false失败
*/
private boolean sendVerificationCode2New(String phoneNo, String randomCode, String smsMerchant) {
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
String clientIp = sessionStruct.getIp();
// 手机号计数器
Long getPhoneVerificationCount = redisTemplate.opsForHash().increment(Constants.REDIS_SMS_CODE_COUNT, phoneNo, 1);
redisTemplate.expire(Constants.REDIS_SMS_CODE_COUNT, DateUtils.getSeconds(), TimeUnit.SECONDS);
// 设备号计数器
Long getDeviceVerificationCount = 0L;
if (StringUtils.isNotBlank(sessionStruct.getScDeviceId())) {
getDeviceVerificationCount = redisTemplate.opsForHash().increment(Constants.REDIS_SMS_DEVICE_COUNT, sessionStruct.getScDeviceId(), 1);
redisTemplate.expire(Constants.REDIS_SMS_DEVICE_COUNT, DateUtils.getSeconds(), TimeUnit.SECONDS);
}
// IP计数器
Long getIPVerificationCount = 0L;
if (StringUtils.isNotBlank(clientIp)) {
getIPVerificationCount = redisTemplate.opsForHash().increment(Constants.REDIS_SMS_IP_COUNT, clientIp, 1);
redisTemplate.expire(Constants.REDIS_SMS_IP_COUNT, DateUtils.getSeconds(), TimeUnit.SECONDS);
}
// 手机号上限检查
if (getPhoneVerificationCount > PHONE_MAX_PER_DAY) {
throw new BizException(BizExceptionEnum.PHONE_MAX_PER_DAY);
}
// 设备号上限检查
if (getDeviceVerificationCount > DEVICE_MAX_PER_DAY) {
throw new BizException(BizExceptionEnum.DEVICE_MAX_PER_DAY);
}
// IP上限检查
if (!IpUtil.whiteOf(clientIp) && getIPVerificationCount > IP_MAX_PER_DAY) {
throw new BizException(BizExceptionEnum.IP_MAX_PER_DAY);
}
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
if (!UserConstant.defaultTenantId.equals(sessionStruct.getTenantId())) {
key = Constants.REDIS_PREFIX_VERIFICATION_CODE + sessionStruct.getTenantId() + "_" + phoneNo;
}
long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES);
if (expire >= EXPIRE_MINUTES - 1) {
throw new BizException(BizExceptionEnum.DUPLICATE_MIN);
}
String uniqueId = phoneNo + UUID.randomUUID().toString().replaceAll("-", "");
MsgParams message = new MsgParams.Builder()
.typeList(Collections.singletonList(2))
.phoneNo(phoneNo)
.merchantId(SmsMerchant.of(smsMerchant).getMerchantId())
.contentId(SmsMerchant.of(smsMerchant).getContentId())
.uniqueId(uniqueId)
.contentArgs(Collections.singletonList(randomCode))
.channel("")
.build();
try {
smsService.getSmsSender().sendMsg(message);
redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES);
//删除用户重置密码,多次错误逻辑
deleteRetSendCode(phoneNo);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 删除用户重置密码时短信验证错误
*
* @param phoneNo 手机号码
*/
private void deleteRetSendCode(String phoneNo) {
String verificationCountKey = Constants.REDIS_VERIFICATION_COUNT + phoneNo;
redisTemplate.opsForHash().delete(verificationCountKey, Constants.REDIS_VERIFICATION_COUNT);
}
}
package cn.quantgroup.xyqb.service.v2;
import cn.quantgroup.sms.MsgParams;
import cn.quantgroup.tech.util.TechEnvironment;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.constant.UserConstant;
import cn.quantgroup.xyqb.controller.req.v2.SMSReq;
import cn.quantgroup.xyqb.exception.BizException;
import cn.quantgroup.xyqb.exception.BizExceptionEnum;
import cn.quantgroup.xyqb.model.SMSCodeBean;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.service.sms.ISmsService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import cn.quantgroup.xyqb.util.DateUtils;
import cn.quantgroup.xyqb.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class SMSVoiceVerificationCodeStrategy implements VerificationCodeStrategy {
@Autowired
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
@Autowired
private ISmsService smsService;
private static final long EXPIRE_MINUTES = 10;
/**
* 手机号语音上限
*/
private static final Long PHONE_VOICE_MAX_PER_DAY = 5L;
/**
* 设备每天上限
*/
private static final Long DEVICE_MAX_PER_DAY = 20L;
/**
* ip上限
*/
private static final Long IP_MAX_PER_DAY = 5000L;
@Override
public Integer getType() {
return 1;
}
@Override
public SMSCodeBean send(SMSReq smsReq) {
SMSCodeBean smsCodeBean = new SMSCodeBean();
String usage = null;
switch (smsReq.getSceneType()) {
case 0:
usage = "4";
break;
case 1:
usage = "6";
break;
case 2:
usage = "5";
break;
}
int codeLength = Constants.SMS_CODE_LEN_4;
if (smsReq.getCodeLength() != null) {
codeLength = smsReq.getCodeLength();
}
String randomCode = getRandomCode(codeLength);
if (usage == null) {
throw new BizException(BizExceptionEnum.ERROR_USAGE);
}
if (sendVerificationCode2VoiceNew(smsReq.getPhoneNo(), randomCode, usage)) {
smsCodeBean.setCode(randomCode);
return smsCodeBean;
} else {
throw new BizException(BizExceptionEnum.ERROR_SEND_SMS);
}
}
/**
* 新版本语音验证码
*/
private boolean sendVerificationCode2VoiceNew(String phoneNo, String randomCode, String usage) {
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSession();
String clientIp = sessionStruct.getIp();
// 手机号计数器
String verificationCountKey = Constants.REDIS_VOICE_CODE_COUNT + usage;
Long getPhoneVerificationCount = redisTemplate.opsForHash().increment(verificationCountKey, phoneNo, 1);
redisTemplate.expire(verificationCountKey, DateUtils.getSeconds(), TimeUnit.SECONDS);
// 设备号计数器
Long getDeviceVerificationCount = 0L;
if (StringUtils.isNotBlank(sessionStruct.getScDeviceId())) {
String verificationDeviceCountKey = Constants.REDIS_VOICE_DEVICE_COUNT + sessionStruct.getScDeviceId();
getDeviceVerificationCount = redisTemplate.opsForHash().increment(Constants.REDIS_VOICE_DEVICE_COUNT, verificationDeviceCountKey, 1);
redisTemplate.expire(Constants.REDIS_VOICE_DEVICE_COUNT, DateUtils.getSeconds(), TimeUnit.SECONDS);
}
// IP计数器
Long getIPVerificationCount = 0L;
if (StringUtils.isNotBlank(clientIp)) {
String verificationIPCountKey = Constants.REDIS_VOICE_IP_COUNT + clientIp;
getIPVerificationCount = redisTemplate.opsForHash().increment(Constants.REDIS_VOICE_IP_COUNT, verificationIPCountKey, 1);
redisTemplate.expire(Constants.REDIS_VOICE_IP_COUNT, DateUtils.getSeconds(), TimeUnit.SECONDS);
}
// 手机号上限检查
if (getPhoneVerificationCount > PHONE_VOICE_MAX_PER_DAY) {
throw new BizException(BizExceptionEnum.PHONE_VOICE_MAX_PER_DAY);
}
// 设备号上限检查
if (getDeviceVerificationCount > DEVICE_MAX_PER_DAY) {
throw new BizException(BizExceptionEnum.DEVICE_MAX_PER_DAY);
}
// IP上限检查
if (!IpUtil.whiteOf(clientIp) && getIPVerificationCount > IP_MAX_PER_DAY) {
throw new BizException(BizExceptionEnum.IP_MAX_PER_DAY);
}
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
if (!UserConstant.defaultTenantId.equals(sessionStruct.getTenantId())) {
key = Constants.REDIS_PREFIX_VERIFICATION_CODE + sessionStruct.getTenantId() + "_" + phoneNo;
}
long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES);
if (expire >= EXPIRE_MINUTES - 1) {
throw new BizException(BizExceptionEnum.DUPLICATE_MIN);
}
String uniqueId = phoneNo + UUID.randomUUID().toString().replaceAll("-", "");
MsgParams message = new MsgParams(Collections.singletonList(4), phoneNo, "1", "4", Collections.singletonList(randomCode), uniqueId);
try {
smsService.getSmsSender().sendMsg(message);
redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES);
//删除用户重置密码,多次错误逻辑
deleteRetSendCode(phoneNo);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 删除用户重置密码时短信验证错误
*
* @param phoneNo 手机号码
*/
private void deleteRetSendCode(String phoneNo) {
String verificationCountKey = Constants.REDIS_VERIFICATION_COUNT + phoneNo;
redisTemplate.opsForHash().delete(verificationCountKey, Constants.REDIS_VERIFICATION_COUNT);
}
private final static String RANDOM_CHARS = "0123456789";
private String getRandomCode(int count) {
if (TechEnvironment.isPro()) {
return RandomStringUtils.random(count, RANDOM_CHARS);
}
if (count == 6) {
return "000000";
}
return Constants.SUCCESS_CODE;
}
}
package cn.quantgroup.xyqb.service.v2;
import cn.quantgroup.xyqb.controller.req.v2.SMSReq;
import cn.quantgroup.xyqb.controller.req.v2.SMSVerifyReq;
import cn.quantgroup.xyqb.exception.BizException;
import cn.quantgroup.xyqb.exception.BizExceptionEnum;
import cn.quantgroup.xyqb.model.SMSCodeBean;
import cn.quantgroup.xyqb.service.sms.ISmsService;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class VerificationCodeContext {
private final ISmsService smsService;
private final Map<Integer, VerificationCodeStrategy> verificationCodeStrategyHashMap = new HashMap<>();
public VerificationCodeContext(List<VerificationCodeStrategy> verificationCodeStrategyList, ISmsService smsService) {
this.smsService = smsService;
verificationCodeStrategyList.forEach(i -> verificationCodeStrategyHashMap.put(i.getType(), i));
}
public SMSCodeBean send(SMSReq smsReq) {
VerificationCodeStrategy verificationCodeStrategy = verificationCodeStrategyHashMap.get(smsReq.getType());
if (verificationCodeStrategy == null) {
throw new BizException(BizExceptionEnum.UN_EXIT_VERIFY_TYPE);
}
return verificationCodeStrategy.send(smsReq);
}
public boolean verify(SMSVerifyReq smsVerifyReq) {
return smsService.verifyPhoneAndCode(smsVerifyReq);
}
}
package cn.quantgroup.xyqb.service.v2;
import cn.quantgroup.xyqb.controller.req.v2.SMSReq;
import cn.quantgroup.xyqb.model.SMSCodeBean;
public interface VerificationCodeStrategy {
Integer getType();
SMSCodeBean send(SMSReq smsReq);
}
...@@ -14,6 +14,11 @@ public interface IWechatService { ...@@ -14,6 +14,11 @@ public interface IWechatService {
WechatUserInfo getWechatUserInfoFromWechatServer(String token, String openId,String appId,Integer tenantId); WechatUserInfo getWechatUserInfoFromWechatServer(String token, String openId,String appId,Integer tenantId);
WechatUserInfo findWechatUserInfoFromDb(String openId,String appId,Integer tenantId); WechatUserInfo findWechatUserInfoFromDb(String openId,String appId,Integer tenantId);
List<WechatUserInfo> findWechatUserInfoFromDb(List<String> openId,String appId,Integer tenantId);
List<WechatUserInfo> findUnionIdsAndOpenIdAndTenantId(List<String> uuids,String appId,Integer tenantId);
WechatUserInfo findByUnionIdAndAppIdAndTenantId(String unionId,String appId,Integer tenantId);
WechatUserInfo findWechatUserInfoByPhoneNo(String phoneNo, Integer tenantId); WechatUserInfo findWechatUserInfoByPhoneNo(String phoneNo, Integer tenantId);
......
...@@ -116,6 +116,23 @@ public class WechatServiceImpl implements IWechatService { ...@@ -116,6 +116,23 @@ public class WechatServiceImpl implements IWechatService {
return weChatUserRepository.findByOpenIdAndAppNameAndAppIdAndTenantId(openId, "xyqb",appId,tenantId); return weChatUserRepository.findByOpenIdAndAppNameAndAppIdAndTenantId(openId, "xyqb",appId,tenantId);
} }
@Override
public List<WechatUserInfo> findWechatUserInfoFromDb(List<String> openId,String appId,Integer tenantId){
return weChatUserRepository.findByOpenIdInAndAppIdAndTenantId(openId,appId,tenantId);
}
public List<WechatUserInfo> findUnionIdsAndOpenIdAndTenantId(List<String> unionIds, String appId, Integer tenantId){
return weChatUserRepository.findByUnionIdInAndAppIdAndTenantId(unionIds,appId,tenantId);
}
@Override
public WechatUserInfo findByUnionIdAndAppIdAndTenantId(String openId,String appId,Integer tenantId) {
return weChatUserRepository.findByUnionIdAndAppIdAndTenantId(openId,appId,tenantId);
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public WechatUserInfo saveWechatUserInfo(WechatUserInfo userInfo) { public WechatUserInfo saveWechatUserInfo(WechatUserInfo userInfo) {
......
package cn.quantgroup.xyqb.session; package cn.quantgroup.xyqb.session;
import cn.quantgroup.xyqb.Constants; import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.model.LoginProperties;
import cn.quantgroup.xyqb.model.session.SessionStruct; import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.model.session.SessionValue; import cn.quantgroup.xyqb.model.session.SessionValue;
import cn.quantgroup.xyqb.util.IpUtil; import cn.quantgroup.xyqb.util.IpUtil;
...@@ -34,12 +35,17 @@ public class XyqbSessionContextHolder { ...@@ -34,12 +35,17 @@ public class XyqbSessionContextHolder {
sessionStruct = getXSessionFromRedis(); sessionStruct = getXSessionFromRedis();
threadSession.set(sessionStruct); threadSession.set(sessionStruct);
} }
if(sessionStruct!=null && sessionStruct.getValues()!=null && sessionStruct.getValues().getLoginProperties()!=null){
sessionStruct.setStmsUserId(sessionStruct.getValues().getLoginProperties().getStmsUserId());
sessionStruct.setStmsUserName(sessionStruct.getValues().getLoginProperties().getStmsUserName());
}
return sessionStruct; return sessionStruct;
} }
public static void initXSession() { public static SessionStruct initXSession() {
SessionStruct sessionStruct; SessionStruct sessionStruct = threadSession.get();
if (threadSession.get() == null) { if (sessionStruct == null) {
sessionStruct = new SessionStruct(); sessionStruct = new SessionStruct();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
...@@ -56,9 +62,53 @@ public class XyqbSessionContextHolder { ...@@ -56,9 +62,53 @@ public class XyqbSessionContextHolder {
sessionStruct.setGeetestId(geetestId); sessionStruct.setGeetestId(geetestId);
} }
sessionStruct.setScDeviceId(request.getHeader("scDeviceId")); sessionStruct.setScDeviceId(request.getHeader("scDeviceId"));
sessionStruct.setTerminal( request.getHeader("terminal")); sessionStruct.setTerminal(request.getHeader("terminal"));
threadSession.set(sessionStruct); threadSession.set(sessionStruct);
} else {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//如果是极验,那赋值
if (request.getHeader(Constants.X_BEHAVIOR_TYPE) != null && "1".equals(request.getHeader(Constants.X_BEHAVIOR_TYPE))) {
String geetestId = request.getHeader(Constants.X_BEHAVIOR_ID);
sessionStruct.setGeetestId(geetestId);
}
}
return sessionStruct;
} }
public static SessionStruct initSTMSSession(String token, String userId, String userName) {
SessionStruct sessionStruct = threadSession.get();
if (sessionStruct == null) {
sessionStruct = new SessionStruct();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String tenantId = request.getHeader(Constants.X_AUTH_TENANT);
String registered_from = request.getHeader(Constants.X_AUTH_FROM);
String ip = IpUtil.getRemoteIP(request);
sessionStruct.setTenantId(Integer.valueOf(tenantId));
sessionStruct.setRegisteredFrom(Long.valueOf(registered_from));
sessionStruct.setIp(ip);
sessionStruct.setScDeviceId(request.getHeader("scDeviceId"));
sessionStruct.setTerminal(request.getHeader("terminal"));
sessionStruct.setSid(token);
sessionStruct.setStmsUserId(userId);
sessionStruct.setStmsUserName(userName);
threadSession.set(sessionStruct);
} else {
sessionStruct.setStmsUserId(userId);
sessionStruct.setStmsUserName(userName);
}
SessionValue sessionValue = sessionStruct.getValues();
if (sessionValue == null) {
sessionValue = new SessionValue();
LoginProperties loginProperties = new LoginProperties();
loginProperties.setStmsUserId(userId);
loginProperties.setStmsUserName(userName);
sessionValue.setLoginProperties(loginProperties);
}
sessionStruct.setValues(sessionValue);
return sessionStruct;
} }
public static SessionStruct getXSessionFromRedis() { public static SessionStruct getXSessionFromRedis() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment