Commit 18f93b0a authored by 技术部-任文超's avatar 技术部-任文超

重构微信关联:

1、关闭独立的关联请求接口;
2、创建微信关联、解绑、冲突用户下线事务
3、捆绑该关联动作到用户账密登录接口和快速登录接口
parent 75ca7b4c
...@@ -28,6 +28,8 @@ public interface Constants { ...@@ -28,6 +28,8 @@ public interface Constants {
String X_AUTH_TOKEN = "x-auth-token"; String X_AUTH_TOKEN = "x-auth-token";
/** 登录账号/手机号参数名 */ /** 登录账号/手机号参数名 */
String PHONE_NO = "phoneNo"; String PHONE_NO = "phoneNo";
/** 微信标识参数名 */
String OPEN_ID = "openId";
// -- Start -- IPV4安全策略常量组 // -- Start -- IPV4安全策略常量组
/** 账密不匹配错误 - 按账号计数 */ /** 账密不匹配错误 - 按账号计数 */
......
...@@ -3,6 +3,7 @@ package cn.quantgroup.xyqb.controller; ...@@ -3,6 +3,7 @@ package cn.quantgroup.xyqb.controller;
import cn.quantgroup.xyqb.exception.PasswordErrorLimitException; import cn.quantgroup.xyqb.exception.PasswordErrorLimitException;
import cn.quantgroup.xyqb.exception.UserNotExistException; import cn.quantgroup.xyqb.exception.UserNotExistException;
import cn.quantgroup.xyqb.exception.VerificationCodeErrorException; import cn.quantgroup.xyqb.exception.VerificationCodeErrorException;
import cn.quantgroup.xyqb.exception.WechatRelateUserException;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.util.IPUtil; import cn.quantgroup.xyqb.util.IPUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -63,6 +64,18 @@ public class ExceptionHandlingController implements IBaseController { ...@@ -63,6 +64,18 @@ public class ExceptionHandlingController implements IBaseController {
return new JsonResult(unee.getMessage(), 401L, null); return new JsonResult(unee.getMessage(), 401L, null);
} }
/**
* 微信关联异常
* @param wrue
* @return
*/
@ExceptionHandler(WechatRelateUserException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public JsonResult wechatRelateUserException(WechatRelateUserException wrue) {
LOGGER.info("throw WechatRelateUserException,msg={},businessCode={},code={}", wrue.getMessage(), 1L, 401L);
return new JsonResult(wrue.getMessage(), 401L, null);
}
/** /**
* 其他全局异常 * 其他全局异常
* @param e * @param e
......
...@@ -27,6 +27,8 @@ import org.springframework.beans.factory.annotation.Value; ...@@ -27,6 +27,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
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 org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
...@@ -206,6 +208,9 @@ public class WeChatController implements IBaseController { ...@@ -206,6 +208,9 @@ public class WeChatController implements IBaseController {
* @param response * @param response
*/ */
private void receiveCodeWithDefault(String code, String systemKey, String schema, Long registerFrom, String redirect, HttpServletResponse response){ private void receiveCodeWithDefault(String code, String systemKey, String schema, Long registerFrom, String redirect, HttpServletResponse response){
// 微信跳转请求入参监控
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
LOGGER.info("[WeChatController][receiveCodeWithDefault]微信授权及跳转:code:{},systemKey:{},schema:{},registerFrom:{},redirect:{},request:{},response:{}", code, systemKey, schema, registerFrom, redirect, request, response);
/* /*
* 预处理(容错) * 预处理(容错)
*/ */
...@@ -220,6 +225,7 @@ public class WeChatController implements IBaseController { ...@@ -220,6 +225,7 @@ public class WeChatController implements IBaseController {
// 从code获取token // 从code获取token
Merchant merchant = merchantService.findMerchantByName(systemKey); Merchant merchant = merchantService.findMerchantByName(systemKey);
AccessTokenResponse token = wechatService.getToken(code); AccessTokenResponse token = wechatService.getToken(code);
LOGGER.info("[WeChatController][receiveCodeWithDefault]微信授权及跳转:merchant:{},token:{}", merchant, token);
if (token == null) { if (token == null) {
// 让用户登录,不关联微信, 构造不关联微信的url // 让用户登录,不关联微信, 构造不关联微信的url
redirectNormalUrl(response, merchant, registerFrom,domain); redirectNormalUrl(response, merchant, registerFrom,domain);
...@@ -228,12 +234,14 @@ public class WeChatController implements IBaseController { ...@@ -228,12 +234,14 @@ public class WeChatController implements IBaseController {
WechatUserInfo userInfo = WechatUserInfo userInfo =
wechatService.getWechatUserInfoFromWechatServer(token.getAccessToken(), wechatService.getWechatUserInfoFromWechatServer(token.getAccessToken(),
token.getOpenId()); token.getOpenId());
LOGGER.info("[WeChatController][receiveCodeWithDefault]微信授权及跳转:WechatUserInfo - from wechat api:{}", userInfo);
if (userInfo == null || StringUtils.isEmpty(userInfo.getOpenId())) { if (userInfo == null || StringUtils.isEmpty(userInfo.getOpenId())) {
// 让用户登录,不关联微信, 构造不关联微信的url // 让用户登录,不关联微信, 构造不关联微信的url
redirectNormalUrl(response, merchant, registerFrom, domain); redirectNormalUrl(response, merchant, registerFrom, domain);
return; return;
} }
WechatUserInfo userInfoInDb = wechatService.findWechatUserInfoFromDb(userInfo.getOpenId()); WechatUserInfo userInfoInDb = wechatService.findWechatUserInfoFromDb(userInfo.getOpenId());
LOGGER.info("[WeChatController][receiveCodeWithDefault]微信授权及跳转:WechatUserInfo - from DB:{}", userInfo);
// welcome 首次登录 // welcome 首次登录
if (userInfoInDb == null) { if (userInfoInDb == null) {
// 微信用户首次登录界面, 首先保存userInfo, 跳入到微信注册登录界面 // 微信用户首次登录界面, 首先保存userInfo, 跳入到微信注册登录界面
...@@ -287,6 +295,7 @@ public class WeChatController implements IBaseController { ...@@ -287,6 +295,7 @@ public class WeChatController implements IBaseController {
} }
private String createUserSession(User user, Merchant merchant, String redirect, String domain, Long registerFrom) { private String createUserSession(User user, Merchant merchant, String redirect, String domain, Long registerFrom) {
LOGGER.info("[WeChatController][createUserSession]微信授权及跳转:user:{},merchant:{},redirect:{},domain:{},registerFrom:{}",user, merchant, redirect, domain, registerFrom);
if (StringUtils.isEmpty(redirect) || "redirect".equals(redirect)) { if (StringUtils.isEmpty(redirect) || "redirect".equals(redirect)) {
LOGGER.info("微信登录:redirect为null,走正常流程."); LOGGER.info("微信登录:redirect为null,走正常流程.");
if ("baitiao".equals(merchant.getName())) { if ("baitiao".equals(merchant.getName())) {
...@@ -311,19 +320,20 @@ public class WeChatController implements IBaseController { ...@@ -311,19 +320,20 @@ public class WeChatController implements IBaseController {
private String loginInWechatWithSessionCreated(User user, Merchant merchant, String target, Long channelId, String domain, Long registerFrom) { private String loginInWechatWithSessionCreated(User user, Merchant merchant, String target, Long channelId, String domain, Long registerFrom) {
AuthBean authBean = sessionService.createSession(channelId, registerFrom, "", user, merchant); AuthBean authBean = sessionService.createSession(channelId, registerFrom, "", user, merchant);
LOGGER.info("[WeChatController][loginInWechatWithSessionCreated]微信授权及跳转:user:{},merchant:{},target:{},channelId:{},domain:{},registerFrom:{}",user, merchant, target, channelId, domain, registerFrom);
return domain + "/landing?token=" + authBean.getToken() + "&registerFrom=" + registerFrom + "&channelId=" + channelId + "&key=" + merchant.getName() + "&target=" + target; return domain + "/landing?token=" + authBean.getToken() + "&registerFrom=" + registerFrom + "&channelId=" + channelId + "&key=" + merchant.getName() + "&target=" + target;
} }
private void redirectWechatLoginUrlWithoutLogin(HttpServletResponse response, Merchant merchant, WechatUserInfo userInfo, Long registerFrom,String domain) { private void redirectWechatLoginUrlWithoutLogin(HttpServletResponse response, Merchant merchant, WechatUserInfo userInfo, Long registerFrom,String domain) {
String redirectUrl = assembleWechatRedirectUrl(merchant, userInfo, registerFrom,domain); String redirectUrl = assembleWechatRedirectUrl(merchant, userInfo, registerFrom,domain);
LOGGER.info("redirectWechatLoginUrlWithoutLogin redirectUrl:[{}]",redirectUrl); LOGGER.info("[WeChatController][redirectWechatLoginUrlWithoutLogin]微信授权及跳转:redirectUrl:[{}]",redirectUrl);
response.setHeader("Location", redirectUrl); response.setHeader("Location", redirectUrl);
response.setStatus(301); response.setStatus(301);
} }
private void redirectNormalUrl(HttpServletResponse response, Merchant merchant, Long registerFrom,String domain) { private void redirectNormalUrl(HttpServletResponse response, Merchant merchant, Long registerFrom,String domain) {
String redirectUrl = assembleNormalRedirectUrl(merchant, registerFrom,domain); String redirectUrl = assembleNormalRedirectUrl(merchant, registerFrom,domain);
LOGGER.info("redirectNormalUrl redirectUrl:[{}]",redirectUrl); LOGGER.info("[WeChatController][redirectNormalUrl]微信授权及跳转: redirectUrl:[{}]",redirectUrl);
response.setHeader("Location", redirectUrl); response.setHeader("Location", redirectUrl);
response.setStatus(301); response.setStatus(301);
} }
......
...@@ -12,6 +12,7 @@ import cn.quantgroup.xyqb.entity.UserDetail; ...@@ -12,6 +12,7 @@ import cn.quantgroup.xyqb.entity.UserDetail;
import cn.quantgroup.xyqb.entity.WechatUserInfo; import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.exception.UserNotExistException; import cn.quantgroup.xyqb.exception.UserNotExistException;
import cn.quantgroup.xyqb.exception.VerificationCodeErrorException; import cn.quantgroup.xyqb.exception.VerificationCodeErrorException;
import cn.quantgroup.xyqb.exception.WechatRelateUserException;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.UserModel; import cn.quantgroup.xyqb.model.UserModel;
import cn.quantgroup.xyqb.model.UserStatistics; import cn.quantgroup.xyqb.model.UserStatistics;
...@@ -45,6 +46,7 @@ import java.nio.charset.Charset; ...@@ -45,6 +46,7 @@ import java.nio.charset.Charset;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
/** /**
* Http服务接口:用户注册、登录、重置密码 * Http服务接口:用户注册、登录、重置密码
...@@ -99,7 +101,6 @@ public class UserController implements IBaseController { ...@@ -99,7 +101,6 @@ public class UserController implements IBaseController {
* @param userId * @param userId
* @param key * @param key
* @param request * @param request
* @param openId
* @param dimension * @param dimension
* @return * @return
*/ */
...@@ -110,10 +111,10 @@ public class UserController implements IBaseController { ...@@ -110,10 +111,10 @@ public class UserController implements IBaseController {
@RequestParam(required = false, defaultValue = "1") Long createdFrom, @RequestParam(required = false, defaultValue = "1") Long createdFrom,
@RequestParam(required = false, defaultValue = "") String userId, @RequestParam(required = false, defaultValue = "") String userId,
@RequestParam(required = false,defaultValue = "xyqb") String key, @RequestParam(required = false,defaultValue = "xyqb") String key,
HttpServletRequest request, String openId, @RequestParam(required = false) String dimension,
@RequestParam(required = false) String dimension) { HttpServletRequest request) {
LOGGER.info("loginV1 -> channelId:{},appChennel:{},createdFrom:{},userId:{},key:{},openId:{},dimension:{}",channelId, appChannel, createdFrom, userId, key, openId, dimension); LOGGER.info("loginV1 -> channelId:{},appChennel:{},createdFrom:{},userId:{},key:{},dimension:{}",channelId, appChannel, createdFrom, userId, key, dimension);
return login(channelId, appChannel, createdFrom, userId, key, request, openId, dimension); return login(channelId, appChannel, createdFrom, userId, key, dimension, request);
} }
@LogHttpCaller @LogHttpCaller
...@@ -124,18 +125,17 @@ public class UserController implements IBaseController { ...@@ -124,18 +125,17 @@ public class UserController implements IBaseController {
@RequestParam(required = false, defaultValue = "1") Long createdFrom, @RequestParam(required = false, defaultValue = "1") Long createdFrom,
@RequestParam(required = false, defaultValue = "") String userId, @RequestParam(required = false, defaultValue = "") String userId,
@RequestParam(required = false,defaultValue = "xyqb") String key, @RequestParam(required = false,defaultValue = "xyqb") String key,
HttpServletRequest request, String openId, @RequestParam(required = false) String dimension,
@RequestParam(required = false) String dimension) { HttpServletRequest request) {
LOGGER.info("login -> channelId:{},appChannel:{},createdFrom:{},userId:{},key:{},openId:{},dimension:{}",channelId,appChannel,createdFrom,userId,key,openId,dimension); LOGGER.info("login -> channelId:{},appChannel:{},createdFrom:{},userId:{},key:{},dimension:{}",channelId,appChannel,createdFrom,userId,key,dimension);
Merchant merchant = merchantService.findMerchantByName(key); Merchant merchant = merchantService.findMerchantByName(key);
if (merchant == null) { if (merchant == null) {
return JsonResult.buildErrorStateResult("未知的连接", null); return JsonResult.buildErrorStateResult("未知的连接", null);
} }
if (!StringUtils.isEmpty(userId) && userId.length() > 10) { if (!StringUtils.isEmpty(userId) && userId.length() > 10) {
return loginWithUserId(channelId, appChannel, createdFrom, userId, merchant,dimension, request);
return loginWithUserId(channelId, appChannel, createdFrom, userId, merchant,dimension);
} else { } else {
return loginWithHttpBasic(channelId, appChannel, createdFrom, merchant, request, openId,dimension); return loginWithHttpBasic(channelId, appChannel, createdFrom, merchant,dimension, request);
} }
} }
...@@ -207,11 +207,13 @@ public class UserController implements IBaseController { ...@@ -207,11 +207,13 @@ public class UserController implements IBaseController {
//广点通转化注册 - 发送消息 - 方法内过滤 //广点通转化注册 - 发送消息 - 方法内过滤
MqUtils.sendRegisterMessageForGdt(phoneNo, clickId); MqUtils.sendRegisterMessageForGdt(phoneNo, clickId);
} }
if(!wechatRelateUserIfNecessary(user, request)){
return JsonResult.buildErrorStateResult("登录时微信关联失败", null);
}
//增加登陆统计发送 //增加登陆统计发送
UserStatistics statistics = new UserStatistics(user,dimension,3,channelId); UserStatistics statistics = new UserStatistics(user,dimension,3,channelId);
MqUtils.sendLoanVest(statistics); MqUtils.sendLoanVest(statistics);
return new JsonResult(sessionService.createSession(channelId, createdFrom, appChannel, user, merchant)); return new JsonResult(sessionService.createSession(channelId, createdFrom, appChannel, user, merchant));
// return createSession(channelId, createdFrom, appChannel, user);
} }
/** /**
...@@ -386,15 +388,16 @@ public class UserController implements IBaseController { ...@@ -386,15 +388,16 @@ public class UserController implements IBaseController {
return JsonResult.buildErrorStateResult(null, null); return JsonResult.buildErrorStateResult(null, null);
} }
UserDetail detail = userDetailService.findByUserId(user.getId()); UserDetail detail = userDetailService.findByUserId(user.getId());
//UserDetail detail = userDetailRepository.findByUserId(user.getId());
UserModel userModel = new UserModel(user, detail); UserModel userModel = new UserModel(user, detail);
return JsonResult.buildSuccessResult("token校验成功", userModel); return JsonResult.buildSuccessResult("token校验成功", userModel);
} }
private JsonResult loginWithHttpBasic(Long channelId, String appChannel, Long createdFrom, Merchant merchant, HttpServletRequest request, String openId,String dimension) { private JsonResult loginWithHttpBasic(Long channelId, String appChannel, Long createdFrom, Merchant merchant,String dimension, HttpServletRequest request) {
User user = verificateUserNameAndPassword(request, openId); User user = verificateUserNameAndPassword(request);
if (user == null) { if (user == null) {
return JsonResult.buildErrorStateResult("用户名或密码不正确", null); return JsonResult.buildErrorStateResult("用户名或密码不正确", null);
}else if(!wechatRelateUserIfNecessary(user, request)){
return JsonResult.buildErrorStateResult("登录时微信关联失败", null);
} }
//增加登陆统计发送 //增加登陆统计发送
UserStatistics statistics=new UserStatistics(user,dimension,1,channelId); UserStatistics statistics=new UserStatistics(user,dimension,1,channelId);
...@@ -403,7 +406,7 @@ public class UserController implements IBaseController { ...@@ -403,7 +406,7 @@ public class UserController implements IBaseController {
return new JsonResult(sessionService.createSession(channelId, createdFrom, appChannel, user, merchant)); return new JsonResult(sessionService.createSession(channelId, createdFrom, appChannel, user, merchant));
} }
private User verificateUserNameAndPassword(HttpServletRequest request, String openId) { private User verificateUserNameAndPassword(HttpServletRequest request) {
String credential = request.getHeader("authorization"); String credential = request.getHeader("authorization");
if (StringUtils.isBlank(credential) || !credential.startsWith("Basic ")) { if (StringUtils.isBlank(credential) || !credential.startsWith("Basic ")) {
return null; return null;
...@@ -451,19 +454,48 @@ public class UserController implements IBaseController { ...@@ -451,19 +454,48 @@ public class UserController implements IBaseController {
return StringUtils.defaultString(targetPassword, "").equals(PasswordUtil.MD5(paramPass.toLowerCase() + Constants.PASSWORD_SALT)); return StringUtils.defaultString(targetPassword, "").equals(PasswordUtil.MD5(paramPass.toLowerCase() + Constants.PASSWORD_SALT));
} }
private JsonResult loginWithUserId(Long channelId, String appChannel, Long createdFrom, String userId, Merchant merchant,String dimension) { private JsonResult loginWithUserId(Long channelId, String appChannel, Long createdFrom, String userId, Merchant merchant,String dimension,HttpServletRequest request) {
//查询用户,存在则保存用户session信息,userId为uuid //查询用户
User user = userService.findByUuidInDb(userId); User user = userService.findByUuidInDb(userId);
//用户信息存在,更新session中的最后访问时间,重新写入缓存. if (Objects.isNull(user) || !user.getEnable()) {
if (null != user || !user.getEnable()) {
//增加登陆统计发送
UserStatistics statistics=new UserStatistics(user,dimension,4,channelId);
MqUtils.sendLoanVest(statistics);
return new JsonResult(sessionService.createSession(channelId, createdFrom, appChannel, user, merchant));
} else {
LOGGER.error("用户不存在,或者已经注销,userId:{}",userId); LOGGER.error("用户不存在,或者已经注销,userId:{}",userId);
return JsonResult.buildErrorStateResult("登录失败", null); return JsonResult.buildErrorStateResult("登录失败", null);
} else if(!wechatRelateUserIfNecessary(user, request)){
return JsonResult.buildErrorStateResult("登录时微信关联失败", null);
}
//增加登陆统计发送
UserStatistics statistics=new UserStatistics(user,dimension,4,channelId);
MqUtils.sendLoanVest(statistics);
//更新session
return new JsonResult(sessionService.createSession(channelId, createdFrom, appChannel, user, merchant));
}
/**
* 如果必要的话,关联用户和微信
* @param user - 用户标识
* @param request - 当前请求
* @return true - 继续登录,false - 微信关联失败,重新登录
*/
private boolean wechatRelateUserIfNecessary(User user,HttpServletRequest request){
Objects.requireNonNull(request, "无效请求");
String clientIp = IPUtil.getRemoteIP(request);
Set<String> paramKeys = request.getParameterMap().keySet();
boolean ready = paramKeys.contains(Constants.OPEN_ID);
if(!ready){
return true;
}else if(Objects.isNull(user) || Objects.isNull(user.getId()) || StringUtils.isBlank(request.getParameter(Constants.OPEN_ID))){
LOGGER.warn("微信关联失败,user:{}, request:{}",user, request);
return false;
} }
Long userId = user.getId();
String phoneNo = user.getPhoneNo();
try{
int rows = wechatService.relateUser(userId, phoneNo, request.getParameter(Constants.OPEN_ID));
return rows > 0;
}catch (Exception e){
LOGGER.error("微信关联失败,user:{}, request:{}",user, request, e);
}
return false;
} }
/** /**
...@@ -471,15 +503,18 @@ public class UserController implements IBaseController { ...@@ -471,15 +503,18 @@ public class UserController implements IBaseController {
* @param openId - 微信openId * @param openId - 微信openId
* @return * @return
*/ */
@LogHttpCaller
@RequestMapping("/associate_wechat") @RequestMapping("/associate_wechat")
public JsonResult associateWithWechat(String openId) { public JsonResult associateWithWechat(String openId, HttpServletRequest request) {
User user = getCurrentUserFromRedis(); User user = getCurrentUserFromRedis();
if(Objects.isNull(user) || Objects.isNull(user.getId())){ LOGGER.error("[UserController][associateWithWechat][非法请求]微信关联,user:{},openId:{},request:{}",user, openId, request);
return JsonResult.buildSuccessResult("[非法请求]微信关联", null, 1L);
/*if(Objects.isNull(user) || Objects.isNull(user.getId())){
LOGGER.warn("微信关联失败,用户未登录:user:[{}],openId:[{}]",user,openId); LOGGER.warn("微信关联失败,用户未登录:user:[{}],openId:[{}]",user,openId);
return JsonResult.buildErrorStateResult("请登陆后重新操作", null); return JsonResult.buildErrorStateResult("请登陆后重新操作", null);
} }
Long userId = user.getId(); Long userId = user.getId();
/* 判断用户是否已绑定其他微信 */ *//* 判断用户是否已绑定其他微信 *//*
WechatUserInfo userInfoInDb = wechatService.queryByUserId(userId); WechatUserInfo userInfoInDb = wechatService.queryByUserId(userId);
if (Objects.nonNull(userInfoInDb)) { if (Objects.nonNull(userInfoInDb)) {
if(Objects.equals(openId, userInfoInDb.getOpenId())){ if(Objects.equals(openId, userInfoInDb.getOpenId())){
...@@ -494,7 +529,7 @@ public class UserController implements IBaseController { ...@@ -494,7 +529,7 @@ public class UserController implements IBaseController {
wechatService.forbiddenUserWeChat(userId); wechatService.forbiddenUserWeChat(userId);
} }
} }
/* 执行绑定 */ *//* 执行绑定 *//*
WechatUserInfo userInfo = wechatService.findWechatUserInfoFromDb(openId); WechatUserInfo userInfo = wechatService.findWechatUserInfoFromDb(openId);
LOGGER.info("微信关联:user:[{}],openId:[{}],wechatUserInfo:[{}]",user,openId,userInfo); LOGGER.info("微信关联:user:[{}],openId:[{}],wechatUserInfo:[{}]",user,openId,userInfo);
// 无微信信息 // 无微信信息
...@@ -507,11 +542,11 @@ public class UserController implements IBaseController { ...@@ -507,11 +542,11 @@ public class UserController implements IBaseController {
LOGGER.warn("微信关联失败,该微信已经被其他用户绑定:user:[{}],openId:[{}],wechatUserInfo:[{}]",user,openId,userInfo); LOGGER.warn("微信关联失败,该微信已经被其他用户绑定:user:[{}],openId:[{}],wechatUserInfo:[{}]",user,openId,userInfo);
return JsonResult.buildErrorStateResult("该微信已经被其他用户绑定", null); return JsonResult.buildErrorStateResult("该微信已经被其他用户绑定", null);
} }
/* *//*
* 未绑定信用钱包用户 * 未绑定信用钱包用户
*/ *//*
try { try {
int rows = wechatService.bindingUser(userId, user.getPhoneNo(), openId); int rows = wechatService.relateUser(userId, user.getPhoneNo(), openId);
if(rows > 0){ if(rows > 0){
LOGGER.info("微信关联成功:rows:{}, user:[{}],openId:[{}],wechatUserInfo:[{}]",rows,user,openId,userInfo); LOGGER.info("微信关联成功:rows:{}, user:[{}],openId:[{}],wechatUserInfo:[{}]",rows,user,openId,userInfo);
return JsonResult.buildSuccessResult("微信关联成功", null); return JsonResult.buildSuccessResult("微信关联成功", null);
...@@ -523,7 +558,7 @@ public class UserController implements IBaseController { ...@@ -523,7 +558,7 @@ public class UserController implements IBaseController {
// 不做绑定 // 不做绑定
LOGGER.warn("微信关联失败,保存遇到问题:user:[{}],openId:[{}],wechatUserInfo:[{}]",user,openId,userInfo, ex); LOGGER.warn("微信关联失败,保存遇到问题:user:[{}],openId:[{}],wechatUserInfo:[{}]",user,openId,userInfo, ex);
return JsonResult.buildErrorStateResult("微信关联失败", null); return JsonResult.buildErrorStateResult("微信关联失败", null);
} }*/
} }
/** /**
......
package cn.quantgroup.xyqb.exception;
/**
* 微信关联用户异常
* @author 任文超
* @time 2018-04-03
*/
public class WechatRelateUserException extends RuntimeException {
private static final long serialVersionUID = -1L;
public WechatRelateUserException(String msg, Throwable t) {
super(msg, t);
}
public WechatRelateUserException(String msg) {
super(msg);
}
}
...@@ -7,6 +7,8 @@ import org.springframework.data.jpa.repository.Modifying; ...@@ -7,6 +7,8 @@ import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import static org.springframework.transaction.annotation.Propagation.MANDATORY;
/** /**
* Created by 11 on 2017/1/18. * Created by 11 on 2017/1/18.
*/ */
...@@ -23,8 +25,27 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon ...@@ -23,8 +25,27 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon
@Modifying @Modifying
int removeByUserId(Long userId); int removeByUserId(Long userId);
/**
* 关联用户
* @param userId - 用户标识
* @param phoneNo
* @param openId - 微信标识
* @return 记录更新行数
*/
@Transactional @Transactional
@Modifying @Modifying
@Query(value = "update wechat_userinfo set user_id=?1,phone_no=?2 where open_id=?3 and user_id is null", nativeQuery = true) @Query(value = "update wechat_userinfo set user_id=?1,phone_no=?2 where open_id=?3 and user_id is null", nativeQuery = true)
int bindingUser(Long userId, String phoneNo, String openId); int relateUser(Long userId, String phoneNo, String openId);
/**
* 解除关联关系 -- 包括:1、当前微信旧的关联用户;2、当前用户旧的关联微信
* 强制事务
* @param openId - 微信标识
* @param userId - 用户标识
* @return 记录更新行数
*/
@Transactional(propagation = MANDATORY)
@Modifying
@Query(value = "update wechat_userinfo set user_id=null,phone_no='*' where open_id=?1 or user_id=?2", nativeQuery = true)
int dissociateUser(String openId, Long userId);
} }
...@@ -12,7 +12,18 @@ import cn.quantgroup.xyqb.model.session.SessionValue; ...@@ -12,7 +12,18 @@ import cn.quantgroup.xyqb.model.session.SessionValue;
*/ */
public interface ISessionService { public interface ISessionService {
/**
* 更新session
* 用户信息存在,更新session中的最后访问时间,重新写入缓存.
* 存在则保存用户session信息,userId为uuid
*
* @param channelId
* @param createdFrom
* @param appChannel
* @param user
* @param merchant
* @return
*/
AuthBean createSession(Long channelId, Long createdFrom, String appChannel, User user, Merchant merchant); AuthBean createSession(Long channelId, Long createdFrom, String appChannel, User user, Merchant merchant);
SessionStruct createSessionAndPersist(User user, LoginProperties loginProperties); SessionStruct createSessionAndPersist(User user, LoginProperties loginProperties);
......
...@@ -44,6 +44,18 @@ public class SessionServiceImpl implements ISessionService { ...@@ -44,6 +44,18 @@ public class SessionServiceImpl implements ISessionService {
@Autowired @Autowired
private IUserService userService; private IUserService userService;
/**
* 更新session
* 用户信息存在,更新session中的最后访问时间,重新写入缓存.
* 存在则保存用户session信息,userId为uuid
*
* @param channelId
* @param createdFrom
* @param appChannel
* @param user
* @param merchant
* @return
*/
@Override @Override
public AuthBean createSession(Long channelId, Long createdFrom, String appChannel, User user, Merchant merchant) { public AuthBean createSession(Long channelId, Long createdFrom, String appChannel, User user, Merchant merchant) {
AuthBean authBean = new AuthBean(); AuthBean authBean = new AuthBean();
......
...@@ -17,8 +17,21 @@ public interface IWechatService { ...@@ -17,8 +17,21 @@ public interface IWechatService {
WechatUserInfo saveWechatUserInfo(WechatUserInfo userInfo); WechatUserInfo saveWechatUserInfo(WechatUserInfo userInfo);
int bindingUser(Long userId, String phoneNo, String openId); /**
* 关联用户和微信
* @param userId - 用户标识
* @param phoneNo
* @param openId - 微信标识
* @return
*/
int relateUser(Long userId, String phoneNo, String openId);
/**
* 按userId查微信信息
* 此接口实现处不应加缓存,否则解绑时(在关联用户的事务中)不好清除
* @param userId - 用户标识
* @return
*/
WechatUserInfo queryByUserId(Long userId); WechatUserInfo queryByUserId(Long userId);
int forbiddenUserWeChat(Long userId); int forbiddenUserWeChat(Long userId);
......
package cn.quantgroup.xyqb.service.wechat.impl; package cn.quantgroup.xyqb.service.wechat.impl;
import cn.quantgroup.xyqb.entity.WechatUserInfo; import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.exception.WechatRelateUserException;
import cn.quantgroup.xyqb.model.webchat.AccessTokenResponse; import cn.quantgroup.xyqb.model.webchat.AccessTokenResponse;
import cn.quantgroup.xyqb.repository.IWeChatUserRepository; import cn.quantgroup.xyqb.repository.IWeChatUserRepository;
import cn.quantgroup.xyqb.service.http.IHttpService; import cn.quantgroup.xyqb.service.http.IHttpService;
import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.service.wechat.IWechatService; import cn.quantgroup.xyqb.service.wechat.IWechatService;
import cn.quantgroup.xyqb.util.ValidationUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
...@@ -14,12 +17,14 @@ import org.springframework.beans.factory.annotation.Qualifier; ...@@ -14,12 +17,14 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.Example; import org.springframework.data.domain.Example;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
...@@ -32,19 +37,21 @@ import java.util.concurrent.TimeUnit; ...@@ -32,19 +37,21 @@ import java.util.concurrent.TimeUnit;
public class WechatServiceImpl implements IWechatService { public class WechatServiceImpl implements IWechatService {
private static final String WECHAT_TOKEN_KEY_PREFIX = "wechat:token:"; private static final String WECHAT_TOKEN_KEY_PREFIX = "wechat:token:";
private static final String WECHAT_USERINFO_KEY_PREFIX = "wechat:userinfo:"; private static final String WECHAT_USERINFO_KEY_PREFIX = "wechat:userinfo:";
@Autowired
private IHttpService httpService;
@Autowired
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
@Value("${wechat.appid}") @Value("${wechat.appid}")
private String appId; private String appId;
@Value("${wechat.secret}") @Value("${wechat.secret}")
private String secret; private String secret;
@Autowired
private IWeChatUserRepository weChatUserRepository;
private String accessTokenUrl; private String accessTokenUrl;
private String refreshTokenUrl = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s"; private String refreshTokenUrl = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s";
@Resource
private IHttpService httpService;
@Resource
private ISessionService sessionService;
@Resource
private IWeChatUserRepository weChatUserRepository;
@Autowired
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
@PostConstruct @PostConstruct
private void init() { private void init() {
...@@ -136,15 +143,36 @@ public class WechatServiceImpl implements IWechatService { ...@@ -136,15 +143,36 @@ public class WechatServiceImpl implements IWechatService {
@Override @Override
@CacheEvict(value = "WechatUserInfo", key = "'openId:' + #openId", cacheManager = "cacheManager") @CacheEvict(value = "WechatUserInfo", key = "'openId:' + #openId", cacheManager = "cacheManager")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public int bindingUser(Long userId, String phoneNo, String openId) { public int relateUser(Long userId, String phoneNo, String openId) {
if(Objects.isNull(userId) || Objects.isNull(openId)){ if(Objects.isNull(userId) || Objects.isNull(openId) || StringUtils.isBlank(openId)){
log.error("微信关联失败:非法入参:[service]:userId:{},phoneNo:{},openId:{}", userId, phoneNo, openId);
return 0; return 0;
} }
return weChatUserRepository.bindingUser(userId, Optional.ofNullable(phoneNo).orElse(""), openId); /* 如果当前openId已关联其他用户,则解绑成功后要注销其登录session */
WechatUserInfo wechatUserInfo = weChatUserRepository.findByUserId(userId);
// 强制解除关联
int dissociate = weChatUserRepository.dissociateUser(openId, userId);
if(dissociate < 1){
log.error("微信关联失败:解绑条数<1:[service]:userId:{},phoneNo:{},openId:{}", userId, phoneNo, openId);
throw new WechatRelateUserException("微信关联失败");
}
int relate = weChatUserRepository.relateUser(userId, Optional.ofNullable(phoneNo).orElse(""), openId);
if(relate < 1){
log.error("微信关联失败:绑定条数<1:[service]:userId:{},phoneNo:{},openId:{}", userId, phoneNo, openId);
throw new WechatRelateUserException("微信关联失败");
}else if(Objects.nonNull(wechatUserInfo) && Objects.nonNull(wechatUserInfo.getUserId())){
// 如果当前openId已关联其他用户,则解绑成功后要注销其登录session
sessionService.deleteByUserId(wechatUserInfo.getUserId());
}
log.info("微信关联成功:[service]:userId:{},phoneNo:{},openId:{},dissociate:{},relate:{}", userId, phoneNo, openId,dissociate,relate);
return relate;
} }
@Override @Override
public WechatUserInfo findWechatUserInfoByPhoneNo(String phoneNo) { public WechatUserInfo findWechatUserInfoByPhoneNo(String phoneNo) {
if(!ValidationUtil.validatePhoneNo(phoneNo)){
return null;
}
return weChatUserRepository.findByPhoneNo(phoneNo); return weChatUserRepository.findByPhoneNo(phoneNo);
} }
...@@ -163,15 +191,19 @@ public class WechatServiceImpl implements IWechatService { ...@@ -163,15 +191,19 @@ public class WechatServiceImpl implements IWechatService {
return httpService.get(String.format(refreshTokenUrl, refreshToken)); return httpService.get(String.format(refreshTokenUrl, refreshToken));
} }
/**
* 按userId查微信信息
* 此处不应加缓存,否则解绑时不好清除
* @param userId - 用户标识
* @return
*/
@Override @Override
@Cacheable(value = "WechatUserInfo", key = "'userId:' + #userId", unless = "#result == null", cacheManager = "cacheManager")
public WechatUserInfo queryByUserId(Long userId) { public WechatUserInfo queryByUserId(Long userId) {
return weChatUserRepository.findByUserId(userId); return weChatUserRepository.findByUserId(userId);
} }
@Override @Override
@CacheEvict(value = "WechatUserInfo", key = "'userId:' + #userId", cacheManager = "cacheManager")
public int forbiddenUserWeChat(Long userId) { public int forbiddenUserWeChat(Long userId) {
return weChatUserRepository.removeByUserId(userId); return weChatUserRepository.removeByUserId(userId);
} }
......
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