|
@@ -0,0 +1,443 @@
|
|
|
+package com.zanxiang.sdk.service.Impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.zanxiang.common.domain.ResultVo;
|
|
|
+import com.zanxiang.common.enums.AccountStatusEnum;
|
|
|
+import com.zanxiang.common.enums.HttpStatusEnum;
|
|
|
+import com.zanxiang.common.exception.BaseException;
|
|
|
+import com.zanxiang.common.text.UUID;
|
|
|
+import com.zanxiang.common.utils.IpUtils;
|
|
|
+import com.zanxiang.common.utils.JsonUtil;
|
|
|
+import com.zanxiang.common.utils.StringUtils;
|
|
|
+import com.zanxiang.common.utils.URIUtil;
|
|
|
+import com.zanxiang.mybatis.entity.User;
|
|
|
+import com.zanxiang.sdk.common.constant.ApiUrlConstant;
|
|
|
+import com.zanxiang.sdk.common.constant.RedisKeyConstant;
|
|
|
+import com.zanxiang.sdk.common.util.RedisUtils;
|
|
|
+import com.zanxiang.sdk.common.util.RegisterUtils;
|
|
|
+import com.zanxiang.sdk.domain.dto.UserOauthDTO;
|
|
|
+import com.zanxiang.sdk.domain.params.*;
|
|
|
+import com.zanxiang.sdk.domain.vo.UserLoginVO;
|
|
|
+import com.zanxiang.sdk.service.*;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author : lingfeng
|
|
|
+ * @time : 2022-06-22
|
|
|
+ * @description : 注册登录逻辑
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class RegisterLoginServiceImpl implements IRegisterLoginService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedisUtils<String> redisUtils;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IQqApiService qqApiService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IVxApiService vxApiService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IUserOauthService userOauthService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IUserTokenService userTokenService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IUserLoginLogService userLoginLogService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IUserService userService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ISmsService smsService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IWordCheckService wordCheckService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * QQ开发者应用id
|
|
|
+ */
|
|
|
+ @Value("${auth.qq-appId}")
|
|
|
+ private String qqAppId;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信开发者应用id
|
|
|
+ */
|
|
|
+ @Value("${auth.vx-appId}")
|
|
|
+ private String vxAppId;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * QQ授权注册登录
|
|
|
+ *
|
|
|
+ * @param response : 返回体
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void qqLoginAuth(HttpServletResponse response) {
|
|
|
+ //获取state
|
|
|
+ String state = this.getState();
|
|
|
+ //传递参数
|
|
|
+ Map<String, String> paramMap = new HashMap<>(4);
|
|
|
+ paramMap.put("response_type", "code");
|
|
|
+ paramMap.put("client_id", qqAppId);
|
|
|
+ paramMap.put("state", state);
|
|
|
+ paramMap.put("redirect_uri", ApiUrlConstant.QQ_REDIRECT_URL);
|
|
|
+ //重定向到QQ授权页面
|
|
|
+ try {
|
|
|
+ response.sendRedirect(URIUtil.fillUrlParams(ApiUrlConstant.QQ_AUTH_URL, paramMap, Boolean.TRUE));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("QQ授权登录重定向跳转异常");
|
|
|
+ throw new BaseException("QQ授权登录重定向跳转异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * QQ授权注册登录回调
|
|
|
+ *
|
|
|
+ * @param qqLoginCallbackParam : 回调参数
|
|
|
+ * @param request : request
|
|
|
+ * @return : 返回登录token
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public ResultVo<UserLoginVO> qqLoginCallback(QqLoginCallbackParam qqLoginCallbackParam, HttpServletRequest request) {
|
|
|
+ String code = qqLoginCallbackParam.getCode();
|
|
|
+ String state = qqLoginCallbackParam.getState();
|
|
|
+ String deviceType = qqLoginCallbackParam.getDeviceType();
|
|
|
+ //验证state,如果不一致,可能被CSRF攻击
|
|
|
+ this.checkState(state);
|
|
|
+ //获取用户信息
|
|
|
+ Map<String, String> userInfoMap = qqApiService.qqAuthUserInfo(code);
|
|
|
+ //查询用户授权信息是否存在
|
|
|
+ UserOauthDTO userOauthDTO = userOauthService.getUserOauthByOpenId(userInfoMap.get("openId"));
|
|
|
+ //获取用户信息
|
|
|
+ User user = this.getUserByUserOauth(deviceType, userInfoMap, userOauthDTO);
|
|
|
+ //用户登录成功
|
|
|
+ String userToken = userTokenService.getUserToken(userOauthDTO.getId(), deviceType);
|
|
|
+ //登录的ip
|
|
|
+ String realIp = IpUtils.getRealIp(request);
|
|
|
+ //插入用户登录记录
|
|
|
+ userLoginLogService.addUserLoginLog(realIp, user, qqLoginCallbackParam.getGameId());
|
|
|
+ //移出state
|
|
|
+ redisUtils.removeOfSet(RedisKeyConstant.AUTH_STATE_KEY, state);
|
|
|
+ //构造返回
|
|
|
+ return new ResultVo<>(new UserLoginVO(userToken));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信授权注册登录
|
|
|
+ *
|
|
|
+ * @param response : 返回体
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void vxLoginAuth(HttpServletResponse response) {
|
|
|
+ //获取state
|
|
|
+ String state = this.getState();
|
|
|
+ //传递参数
|
|
|
+ Map<String, String> paramMap = new HashMap<>(6);
|
|
|
+ paramMap.put("appid", qqAppId);
|
|
|
+ paramMap.put("redirect_uri", URIUtil.encodeURIComponent(ApiUrlConstant.VX_REDIRECT_URL));
|
|
|
+ paramMap.put("response_type", "code");
|
|
|
+ paramMap.put("scope", "snsapi_login");
|
|
|
+ paramMap.put("state", state);
|
|
|
+ paramMap.put("lang", "cn");
|
|
|
+ //重定向到QQ授权页面
|
|
|
+ try {
|
|
|
+ response.sendRedirect(URIUtil.fillUrlParams(ApiUrlConstant.VX_AUTH_URL, paramMap, Boolean.TRUE));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("微信授权登录重定向跳转异常");
|
|
|
+ throw new BaseException("微信授权登录重定向跳转异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信授权注册登录回调
|
|
|
+ *
|
|
|
+ * @param qqLoginCallbackParam : 回调参数
|
|
|
+ * @param request : request
|
|
|
+ * @return : 返回登录token
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public ResultVo<UserLoginVO> vxLoginCallback(QqLoginCallbackParam qqLoginCallbackParam, HttpServletRequest request) {
|
|
|
+ String deviceType = qqLoginCallbackParam.getDeviceType();
|
|
|
+ String code = qqLoginCallbackParam.getCode();
|
|
|
+ String state = qqLoginCallbackParam.getState();
|
|
|
+ //验证state,如果不一致,可能被CSRF攻击
|
|
|
+ this.checkState(state);
|
|
|
+ //获取用户信息
|
|
|
+ Map<String, String> userInfoMap = vxApiService.vxAuthUserInfo(code);
|
|
|
+ //查询用户授权信息是否存在
|
|
|
+ UserOauthDTO userOauthDTO = userOauthService.getUserOauthByOpenId(userInfoMap.get("openId"));
|
|
|
+ //获取用户信息
|
|
|
+ User user = this.getUserByUserOauth(deviceType, userInfoMap, userOauthDTO);
|
|
|
+ //登录的ip
|
|
|
+ String realIp = IpUtils.getRealIp(request);
|
|
|
+ //用户登录成功
|
|
|
+ String userToken = userTokenService.getUserToken(userOauthDTO.getId(), deviceType);
|
|
|
+ //插入用户登录记录
|
|
|
+ userLoginLogService.addUserLoginLog(realIp, user, qqLoginCallbackParam.getGameId());
|
|
|
+ //移出state
|
|
|
+ redisUtils.removeOfSet(RedisKeyConstant.AUTH_STATE_KEY, state);
|
|
|
+ //构造返回
|
|
|
+ return new ResultVo<>(new UserLoginVO(userToken));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户名密码注册
|
|
|
+ *
|
|
|
+ * @param registerPasswordParam : 用户名密码注册参数
|
|
|
+ * @param request : request
|
|
|
+ * @return : 返回注册结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ResultVo<UserLoginVO> registerPassword(RegisterPasswordParam registerPasswordParam, HttpServletRequest request) {
|
|
|
+ String username = registerPasswordParam.getUsername();
|
|
|
+ String password = registerPasswordParam.getPassword();
|
|
|
+ //用户名密码校验
|
|
|
+ HttpStatusEnum checkRegisterEnum = this.checkRegister(username, password);
|
|
|
+ if (!Objects.equals(checkRegisterEnum, HttpStatusEnum.SUCCESS)) {
|
|
|
+ return new ResultVo<>(checkRegisterEnum);
|
|
|
+ }
|
|
|
+ //创建用户信息
|
|
|
+ User user = User.builder()
|
|
|
+ .username(registerPasswordParam.getUsername())
|
|
|
+ .password(RegisterUtils.cmfPassword(password))
|
|
|
+ .deviceId(registerPasswordParam.getDeviceId())
|
|
|
+ .deviceType(registerPasswordParam.getDeviceType())
|
|
|
+ .status(AccountStatusEnum.NORMAL_STATUS.getStatus())
|
|
|
+ .createTime(LocalDateTime.now())
|
|
|
+ .updateTime(LocalDateTime.now())
|
|
|
+ .build();
|
|
|
+ userService.save(user);
|
|
|
+ //登录的ip
|
|
|
+ String realIp = IpUtils.getRealIp(request);
|
|
|
+ //插入用户登录记录
|
|
|
+ userLoginLogService.addUserLoginLog(realIp, user, registerPasswordParam.getGameId());
|
|
|
+ //获取token
|
|
|
+ String userToken = userTokenService.getUserToken(user.getId(), registerPasswordParam.getDeviceType());
|
|
|
+ //返回用户token
|
|
|
+ return new ResultVo<>(new UserLoginVO(userToken));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手机号注册
|
|
|
+ *
|
|
|
+ * @param registerMobileParam : 手机注册参数
|
|
|
+ * @param request : HttpServletRequest
|
|
|
+ * @return : 返回注册结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ResultVo<UserLoginVO> registerMobile(RegisterMobileParam registerMobileParam, HttpServletRequest request) {
|
|
|
+ Integer type = registerMobileParam.getType();
|
|
|
+ String mobile = registerMobileParam.getMobile();
|
|
|
+ String code = registerMobileParam.getCode();
|
|
|
+ String password = registerMobileParam.getPassword();
|
|
|
+ //校验手机验证码
|
|
|
+ HttpStatusEnum codeCheckEnum = smsService.smsCheck(type, mobile, code);
|
|
|
+ if (!Objects.equals(codeCheckEnum, HttpStatusEnum.SUCCESS)) {
|
|
|
+ return new ResultVo<>(codeCheckEnum);
|
|
|
+ }
|
|
|
+ //判断手机号是否已经注册
|
|
|
+ if (userService.count(new LambdaQueryWrapper<User>().eq(User::getMobile, mobile)) > 0) {
|
|
|
+ return new ResultVo<>(HttpStatusEnum.PHONE_IS_REG);
|
|
|
+ }
|
|
|
+ //验证密码是否合规
|
|
|
+ HttpStatusEnum passwordCheckEnum = RegisterUtils.checkPassword(password);
|
|
|
+ if (!Objects.equals(passwordCheckEnum, HttpStatusEnum.SUCCESS)) {
|
|
|
+ return new ResultVo<>(passwordCheckEnum);
|
|
|
+ }
|
|
|
+ //创建用户信息
|
|
|
+ User user = User.builder()
|
|
|
+ .username(mobile)
|
|
|
+ .regMobile(mobile)
|
|
|
+ .mobile(mobile)
|
|
|
+ .password(RegisterUtils.cmfPassword(password))
|
|
|
+ .deviceId(registerMobileParam.getDeviceId())
|
|
|
+ .deviceType(registerMobileParam.getDeviceType())
|
|
|
+ .status(AccountStatusEnum.NORMAL_STATUS.getStatus())
|
|
|
+ .createTime(LocalDateTime.now())
|
|
|
+ .updateTime(LocalDateTime.now())
|
|
|
+ .build();
|
|
|
+ userService.save(user);
|
|
|
+ //获取token
|
|
|
+ String userToken = userTokenService.getUserToken(user.getId(), registerMobileParam.getDeviceType());
|
|
|
+ //登录的ip
|
|
|
+ String realIp = IpUtils.getRealIp(request);
|
|
|
+ //插入用户登录记录
|
|
|
+ userLoginLogService.addUserLoginLog(realIp, user, registerMobileParam.getGameId());
|
|
|
+ //返回用户token
|
|
|
+ return new ResultVo<>(new UserLoginVO(userToken));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户名密码登录
|
|
|
+ *
|
|
|
+ * @param userLoginParam : 登录参数
|
|
|
+ * @param request : HttpServletRequest
|
|
|
+ * @return : 返回登录token
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public ResultVo<UserLoginVO> loginPassword(LoginPasswordParam userLoginParam, HttpServletRequest request) {
|
|
|
+ //用户名
|
|
|
+ String username = userLoginParam.getUsername();
|
|
|
+ //密码
|
|
|
+ String password = userLoginParam.getPassword();
|
|
|
+ //用户信息
|
|
|
+ User user;
|
|
|
+ //验证用户名是否为手机号
|
|
|
+ if (StringUtils.checkPhone(username)) {
|
|
|
+ user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getMobile, username));
|
|
|
+ } else {
|
|
|
+ user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
|
|
|
+ }
|
|
|
+ //用户信息不存在
|
|
|
+ if (user == null) {
|
|
|
+ return new ResultVo<>(HttpStatusEnum.USERNAME_OR_PASSWORD_ERR);
|
|
|
+ }
|
|
|
+ //判断账号是否停用
|
|
|
+ if (Objects.equals(AccountStatusEnum.FROZEN_STATUS.getStatus(), user.getStatus())) {
|
|
|
+ return new ResultVo<>(HttpStatusEnum.ACCOUNT_HALT);
|
|
|
+ }
|
|
|
+ //验证密码
|
|
|
+ if (!Objects.equals(RegisterUtils.cmfPassword(password), user.getPassword())) {
|
|
|
+ return new ResultVo<>(HttpStatusEnum.USERNAME_OR_PASSWORD_ERR);
|
|
|
+ }
|
|
|
+ //验证通过, 获取token
|
|
|
+ String userToken = userTokenService.getUserToken(user.getId(), userLoginParam.getDeviceType());
|
|
|
+ //登录的ip
|
|
|
+ String realIp = IpUtils.getRealIp(request);
|
|
|
+ //插入用户登录记录
|
|
|
+ userLoginLogService.addUserLoginLog(realIp, user, userLoginParam.getGameId());
|
|
|
+ //构造返回
|
|
|
+ return new ResultVo<>(new UserLoginVO(userToken));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手机号登录
|
|
|
+ *
|
|
|
+ * @param loginMobileParam : 手机号登录参数
|
|
|
+ * @param request : HttpServletRequest
|
|
|
+ * @return : 返回登录信息
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public ResultVo<UserLoginVO> loginMobile(LoginMobileParam loginMobileParam, HttpServletRequest request) {
|
|
|
+ Integer type = loginMobileParam.getType();
|
|
|
+ String mobile = loginMobileParam.getMobile();
|
|
|
+ String code = loginMobileParam.getCode();
|
|
|
+ //验证码校验
|
|
|
+ HttpStatusEnum httpStatusEnum = smsService.smsCheck(type, mobile, code);
|
|
|
+ //验证不通过, 返回
|
|
|
+ if (!Objects.equals(httpStatusEnum, HttpStatusEnum.SUCCESS)) {
|
|
|
+ return new ResultVo<>(httpStatusEnum);
|
|
|
+ }
|
|
|
+ //获取用户信息
|
|
|
+ User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getMobile, mobile));
|
|
|
+ //用户信息不存在
|
|
|
+ if (user == null) {
|
|
|
+ return new ResultVo<>(HttpStatusEnum.PHONE_NOT_REG);
|
|
|
+ }
|
|
|
+ //判断账号是否停用
|
|
|
+ if (Objects.equals(AccountStatusEnum.FROZEN_STATUS.getStatus(), user.getStatus())) {
|
|
|
+ return new ResultVo<>(HttpStatusEnum.ACCOUNT_HALT);
|
|
|
+ }
|
|
|
+ //验证通过, 获取token
|
|
|
+ String userToken = userTokenService.getUserToken(user.getId(), loginMobileParam.getDeviceType());
|
|
|
+ //登录的ip
|
|
|
+ String realIp = IpUtils.getRealIp(request);
|
|
|
+ //插入用户登录记录
|
|
|
+ userLoginLogService.addUserLoginLog(realIp, user, loginMobileParam.getGameId());
|
|
|
+ //构造返回
|
|
|
+ return new ResultVo<>(new UserLoginVO(userToken));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据用户授权信息获取用户
|
|
|
+ *
|
|
|
+ * @param deviceType : 设备类型
|
|
|
+ * @param userInfoMap : 第三方用户信息
|
|
|
+ * @param userOauthDTO : 用户授权信息
|
|
|
+ * @return : 返回应用用户信息
|
|
|
+ */
|
|
|
+ private User getUserByUserOauth(String deviceType, Map<String, String> userInfoMap, UserOauthDTO userOauthDTO) {
|
|
|
+ if (userOauthDTO != null) {
|
|
|
+ return userService.getById(userOauthDTO.getUserId());
|
|
|
+ }
|
|
|
+ //创建用户信息
|
|
|
+ User user = User.builder()
|
|
|
+ .nickname(userInfoMap.get("nickname"))
|
|
|
+ .avatar(userInfoMap.get("avatar"))
|
|
|
+ .deviceType(deviceType)
|
|
|
+ .createTime(LocalDateTime.now())
|
|
|
+ .updateTime(LocalDateTime.now())
|
|
|
+ .build();
|
|
|
+ userService.save(user);
|
|
|
+ //创建用户授权信息
|
|
|
+ userOauthDTO = userOauthService.createUserOauth(user, userInfoMap);
|
|
|
+ log.info("用户授权信息, userOauthDTO : {}", JsonUtil.toString(userOauthDTO));
|
|
|
+ //返回用户信息
|
|
|
+ return user;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 登录用户名密码合规检测
|
|
|
+ *
|
|
|
+ * @param username : 用户名验证
|
|
|
+ * @param password : 密码验证
|
|
|
+ * @return : 返回验证结果
|
|
|
+ */
|
|
|
+ private HttpStatusEnum checkRegister(String username, String password) {
|
|
|
+ //用户名合规检测
|
|
|
+ HttpStatusEnum checkUserNameEnum = RegisterUtils.checkUserName(username);
|
|
|
+ if (Objects.equals(checkUserNameEnum, HttpStatusEnum.SUCCESS)) {
|
|
|
+ return checkUserNameEnum;
|
|
|
+ }
|
|
|
+ //判断用户名是否存在敏感词
|
|
|
+ if (wordCheckService.hasWord(username)) {
|
|
|
+ return HttpStatusEnum.USERNAME_SENSITIVE;
|
|
|
+ }
|
|
|
+ //判断用户名是否已存在
|
|
|
+ if (userService.count(new LambdaQueryWrapper<User>().eq(User::getUsername, username)) > 0) {
|
|
|
+ return HttpStatusEnum.USERNAME_EXISTS;
|
|
|
+ }
|
|
|
+ //密码验证
|
|
|
+ return RegisterUtils.checkPassword(password);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成随机密钥并添加到redis中
|
|
|
+ *
|
|
|
+ * @return : 返回随机密钥
|
|
|
+ */
|
|
|
+ private String getState() {
|
|
|
+ String state = UUID.randomUUID().toString();
|
|
|
+ redisUtils.addToSet(RedisKeyConstant.AUTH_STATE_KEY, state);
|
|
|
+ return state;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * state参数检测
|
|
|
+ *
|
|
|
+ * @param state : 随机密钥
|
|
|
+ */
|
|
|
+ private void checkState(String state) {
|
|
|
+ if (!redisUtils.isMemberInSet(RedisKeyConstant.AUTH_STATE_KEY, state)) {
|
|
|
+ throw new BaseException("State验证失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|