瀏覽代碼

更新二维码登陆 TODO 暂时解开效验手机号码

DESKTOP-VAEGFGM\zqc 2 月之前
父節點
當前提交
d74b3ab77d
共有 11 個文件被更改,包括 328 次插入2 次删除
  1. 3 1
      citu-framework/citu-common/src/main/java/com/citu/framework/common/validation/MobileValidator.java
  2. 1 0
      citu-module-system/citu-module-system-api/src/main/java/com/citu/module/system/enums/logger/LoginLogTypeEnum.java
  3. 10 0
      menduner/menduner-system-api/src/main/java/com/citu/module/menduner/system/enums/ErrorCodeConstants.java
  4. 32 1
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/AppMdeAuthController.java
  5. 39 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/vo/AppMdeWXAppAuthLoginRespVO.java
  6. 20 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/vo/AppMdeWeixinLoginAuthorize.java
  7. 27 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/vo/AppMdeWeixinLoginQrcodeClientIdReqVo.java
  8. 37 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/vo/AppMdeWeixinLoginQrcodeReqVo.java
  9. 38 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/redis/RedisKeyConstants.java
  10. 5 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/auth/MdeAuthService.java
  11. 116 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/auth/MdeAuthServiceImpl.java

+ 3 - 1
citu-framework/citu-common/src/main/java/com/citu/framework/common/validation/MobileValidator.java

@@ -18,8 +18,10 @@ public class MobileValidator implements ConstraintValidator<Mobile, String> {
         if (StrUtil.isEmpty(value)) {
             return true;
         }
+        // TODO 临时放开
+        return  true;
         // 校验手机
-        return ValidationUtils.isMobile(value);
+//        return ValidationUtils.isMobile(value);
     }
 
 }

+ 1 - 0
citu-module-system/citu-module-system-api/src/main/java/com/citu/module/system/enums/logger/LoginLogTypeEnum.java

@@ -17,6 +17,7 @@ public enum LoginLogTypeEnum {
     LOGIN_EMAIL(105), // 使用邮箱登录
 
     LOGIN_SWITCH(106), // 切换登录
+    LOGIN_WX_APP_AUTHORIZE(107), // 使用微信小程序授权登录
 
     LOGOUT_SELF(200),  // 自己主动登出
     LOGOUT_DELETE(202), // 强制退出

+ 10 - 0
menduner/menduner-system-api/src/main/java/com/citu/module/menduner/system/enums/ErrorCodeConstants.java

@@ -189,7 +189,17 @@ public interface ErrorCodeConstants {
     ErrorCode MDE_AUTH_PASSWORD_ERROR_TOO_MANY =
             new ErrorCode(1_100_017_024, "该账户密码已经错误多次,为了您的账户安全,超过5次将锁定您的账户。");
 
+    ErrorCode MDE_WX_APP_QRCODE_TOO_MANY =
+            new ErrorCode(1_100_017_025, "二维码获取太频繁。");
 
+    ErrorCode MDE_WX_APP_QRCODE_EX_TIME =
+            new ErrorCode(1_100_017_026, "二维码过期,请重新获取");
+
+    ErrorCode MDE_WX_APP_QRCODE_WAIT =
+            new ErrorCode(1_100_017_027, "等待扫码确认");
+
+    ErrorCode MDE_WX_APP_QRCODE_CHECK_STATE =
+            new ErrorCode(1_100_017_028, "等待扫码确认");
 
     // ========== 角色模块 1_100_018_000 ==========
     ErrorCode MDE_ROLE_NOT_EXISTS = new ErrorCode(1_100_018_001, "角色不存在");

+ 32 - 1
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/AppMdeAuthController.java

@@ -3,6 +3,7 @@ package com.citu.module.menduner.system.controller.app.common.auth;
 import cn.hutool.core.util.StrUtil;
 import com.citu.framework.common.enums.UserTypeEnum;
 import com.citu.framework.common.pojo.CommonResult;
+import com.citu.framework.common.util.servlet.ServletUtils;
 import com.citu.framework.idempotent.core.annotation.Idempotent;
 import com.citu.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver;
 import com.citu.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver;
@@ -24,12 +25,14 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
-import com.citu.framework.security.core.annotations.PreAuthenticated;
 
 import javax.annotation.security.PermitAll;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 
+import java.io.IOException;
+
 import static com.citu.framework.common.pojo.CommonResult.success;
 import static com.citu.module.menduner.system.enums.SocialApplicationConstants.SOCIAL_WX_PROGRAM_JOBHUNT_APPLICATION;
 
@@ -140,6 +143,34 @@ public class AppMdeAuthController {
         return success(authService.weixinMiniAppLogin(reqVO));
     }
 
+
+    @GetMapping("/weixin/login/qrcode")
+    @Operation(summary = "小程序登陆码")
+    public void weixinLoginQrcode(@Valid AppMdeWeixinLoginQrcodeReqVo reqVo, HttpServletRequest request, HttpServletResponse response) throws IOException {
+//        request.
+        String clientIP = ServletUtils.getClientIP(request);
+        byte [] data =  authService.weixinLoginQrcode(reqVo,clientIP);
+        response.setContentType("image/jpeg");
+        response.getOutputStream().write(data);
+    }
+
+    @PostMapping("/weixin/login/authorize")
+    @Operation(summary = "小程序登陆码授权")
+    public CommonResult weixinLoginAuthorize(@RequestBody  AppMdeWeixinLoginAuthorize request) throws IOException {
+        authService.weixinLoginAuthorize(request);
+        return success(true);
+    }
+
+
+    @PostMapping("/weixin/login/authorize/code")
+    @Operation(summary = "检查小程序是否授权登陆")
+    public CommonResult<AppMdeWXAppAuthLoginRespVO> weixinLoginAuthorizeCode(@RequestBody @Valid AppMdeWeixinLoginQrcodeClientIdReqVo reqVo, HttpServletRequest request) throws InterruptedException {
+        String clientIP = ServletUtils.getClientIP(request);
+        return success(authService.weixinLoginAuthorizeCode(reqVo,clientIP));
+    }
+
+
+
     @PostMapping("/create-weixin-jsapi-signature")
     @Operation(summary = "创建微信 JS SDK 初始化所需的签名",
             description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档")

+ 39 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/vo/AppMdeWXAppAuthLoginRespVO.java

@@ -0,0 +1,39 @@
+package com.citu.module.menduner.system.controller.app.common.auth.vo;
+
+public class AppMdeWXAppAuthLoginRespVO {
+
+    private AppMdeAuthLoginRespVO appMdeAuthLoginRespVO;
+
+
+    private String clientId;
+
+
+    /**
+     * 0.等待扫码确认  1.扫码通过
+     */
+    private Integer status;
+
+    public AppMdeAuthLoginRespVO getAppMdeAuthLoginRespVO() {
+        return appMdeAuthLoginRespVO;
+    }
+
+    public void setAppMdeAuthLoginRespVO(AppMdeAuthLoginRespVO appMdeAuthLoginRespVO) {
+        this.appMdeAuthLoginRespVO = appMdeAuthLoginRespVO;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}

+ 20 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/vo/AppMdeWeixinLoginAuthorize.java

@@ -0,0 +1,20 @@
+package com.citu.module.menduner.system.controller.app.common.auth.vo;
+
+import com.citu.framework.common.enums.UserTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import static com.citu.module.menduner.system.enums.SocialApplicationConstants.SOCIAL_WX_PROGRAM_JOBHUNT_APPLICATION;
+
+public class AppMdeWeixinLoginAuthorize {
+
+    private String uuid;
+
+
+    public String getUuid() {
+        return uuid;
+    }
+
+    public void setUuid(String uuid) {
+        this.uuid = uuid;
+    }
+}

+ 27 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/vo/AppMdeWeixinLoginQrcodeClientIdReqVo.java

@@ -0,0 +1,27 @@
+
+package com.citu.module.menduner.system.controller.app.common.auth.vo;
+
+import com.citu.framework.common.enums.UserTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import static com.citu.module.menduner.system.enums.SocialApplicationConstants.SOCIAL_WX_PROGRAM_JOBHUNT_APPLICATION;
+
+public class AppMdeWeixinLoginQrcodeClientIdReqVo {
+
+    @Schema(description = "客户端识别码", example = "sdsdsd")
+    private String clientId;
+
+
+
+
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+
+}

+ 37 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/auth/vo/AppMdeWeixinLoginQrcodeReqVo.java

@@ -0,0 +1,37 @@
+package com.citu.module.menduner.system.controller.app.common.auth.vo;
+
+import com.citu.framework.common.enums.UserTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import static com.citu.module.menduner.system.enums.SocialApplicationConstants.SOCIAL_WX_PROGRAM_JOBHUNT_APPLICATION;
+
+public class AppMdeWeixinLoginQrcodeReqVo extends AppMdeWeixinLoginQrcodeClientIdReqVo{
+
+//    @Schema(description = "是否刷新0刷新 1.第一次获取", example = "sdsdsd")
+//    private Integer refresh=0;
+
+    @Schema(description = "应用标识", example = "1")
+    private String application=SOCIAL_WX_PROGRAM_JOBHUNT_APPLICATION;
+
+    @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer userType = UserTypeEnum.MEMBER.getValue();
+
+
+    public String getApplication() {
+        return application;
+    }
+
+    public void setApplication(String application) {
+        this.application = application;
+    }
+
+    public Integer getUserType() {
+        return userType;
+    }
+
+    public void setUserType(Integer userType) {
+        this.userType = userType;
+    }
+
+
+}

+ 38 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/redis/RedisKeyConstants.java

@@ -204,6 +204,44 @@ public interface RedisKeyConstants {
      **/
     String MDE_AUTH_USER_PWD_LOCK = "mde_auth_user_pwd_lock:%s";
 
+
+    /**
+     * 门墩儿 微信登录二维码
+     * <p>
+     * KEY 格式:mde_wxapp_qrcoe:客户端ip:客户端id
+     * VALUE 数据类型:String
+     **/
+    String MDE_WXAPP_QRCOE = "mde_wxapp:qrcoe:%s:%s";
+
+    /**
+     * 门墩儿 微信登录二维码检查状态
+     * <p>
+     * KEY 格式:mde_wxapp_qrcoe_check:客户端ip:客户端id
+     * VALUE 数据类型:String
+     **/
+    String MDE_WXAPP_QRCOE_CHECK = "mde_wxapp:qrcoe_check:%s:%s";
+
+
+    /**
+     * 门墩儿 微信登录二维码授权码
+     * <p>
+     * KEY 格式:mde_wxapp_qrcoe:客户端ip:客户端id
+     * VALUE 数据类型:String
+     **/
+    String MDE_WXAPP_QRCOE_AUTHORIZE = "mde_wxapp:qrcoe:authorize:%s";
+
+    /**
+     * 门墩儿 微信登录二维码获取次数
+     * <p>
+     * KEY 格式:qrcoe_cache_time:客户端ip
+     * VALUE 数据类型:String
+     **/
+    String MDE_WXAPP_QRCOE_CACHE_NUM= "mde_wxapp:qrcoe_cache_num:%s";
+
+
+
+
+
     /**
      * 手机号枚举(一个ip一个手机号)
      * <p>

+ 5 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/auth/MdeAuthService.java

@@ -103,4 +103,9 @@ public interface MdeAuthService {
     void resetUserPassword(AppMdeUserResetPasswordReqVO reqVO);
 
 
+    byte[] weixinLoginQrcode(AppMdeWeixinLoginQrcodeReqVo reqVo, String clientIP);
+
+    void weixinLoginAuthorize(AppMdeWeixinLoginAuthorize request);
+
+    AppMdeWXAppAuthLoginRespVO weixinLoginAuthorizeCode(AppMdeWeixinLoginQrcodeClientIdReqVo reqVo, String clientIP) throws InterruptedException;
 }

+ 116 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/auth/MdeAuthServiceImpl.java

@@ -1,6 +1,7 @@
 package com.citu.module.menduner.system.service.auth;
 
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
 import com.citu.framework.common.enums.TerminalEnum;
@@ -9,9 +10,11 @@ import com.citu.framework.common.pojo.CommonResult;
 import com.citu.framework.common.util.monitor.TracerUtils;
 import com.citu.framework.common.util.servlet.ServletUtils;
 import com.citu.framework.common.util.validation.ValidationUtils;
+import com.citu.framework.security.core.LoginUser;
 import com.citu.module.menduner.common.enums.EventAsyncUrlEnum;
 import com.citu.module.menduner.common.enums.PointBizTypeEnum;
 import com.citu.module.menduner.common.message.EventAsyncConfirmMessage;
+import com.citu.module.menduner.common.util.LoginUserContext;
 import com.citu.module.menduner.system.controller.app.common.auth.vo.*;
 import com.citu.module.menduner.system.controller.app.jobhunt.user.vo.AppMdeUserResetPasswordReqVO;
 import com.citu.module.menduner.system.controller.base.logger.LoginLogCreateReqDTO;
@@ -30,6 +33,7 @@ import com.citu.module.system.api.social.SocialUserApi;
 import com.citu.module.system.api.social.dto.SocialUserBindReqDTO;
 import com.citu.module.system.api.social.dto.SocialUserRespDTO;
 import com.citu.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
+import com.citu.module.system.api.social.dto.SocialWxQrcodeReqDTO;
 import com.citu.module.system.enums.logger.LoginLogTypeEnum;
 import com.citu.module.system.enums.logger.LoginResultEnum;
 import com.citu.module.system.enums.oauth2.OAuth2ClientConstants;
@@ -40,8 +44,11 @@ import com.xingyuv.captcha.model.common.ResponseModel;
 import com.xingyuv.captcha.model.vo.CaptchaVO;
 import com.xingyuv.captcha.service.CaptchaService;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Lazy;
+import org.springframework.data.redis.connection.StringRedisConnection;
+import org.springframework.data.redis.core.RedisCallback;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
@@ -97,6 +104,9 @@ public class MdeAuthServiceImpl implements MdeAuthService {
     @Lazy
     private RedisTemplate<String, String> redisTemplate;
 
+
+
+
     @VisibleForTesting
     protected void validateCaptcha(AppMdeAuthLoginReqVO reqVO) {
         // 如果验证码关闭,则不进行校验
@@ -444,6 +454,112 @@ public class MdeAuthServiceImpl implements MdeAuthService {
         userService.enable(Collections.singletonList(user.getId()));
     }
 
+    @Override
+    public byte[] weixinLoginQrcode(AppMdeWeixinLoginQrcodeReqVo reqVo, String  clientIP) {
+        String uuid = IdUtil.fastSimpleUUID();
+
+        SocialWxQrcodeReqDTO reqDTO =new SocialWxQrcodeReqDTO();
+        reqDTO.setScene(uuid);
+        reqDTO.setApplication(reqVo.getApplication());
+        reqDTO.setUserType(reqVo.getUserType());
+        reqDTO.setPath("pagesB/websiteLoginVerification/index");
+        long exTime = 1000L*60L*5L;
+
+        // 使用 execute 方法确保原子性
+        Long increment = redisTemplate.execute((RedisCallback<Long>) connection -> {
+            // 增加计数器
+            Long incr = connection.incr(String.format(MDE_WXAPP_QRCOE_CACHE_NUM, clientIP).getBytes());
+            // 设置过期时间
+            connection.expire(String.format(MDE_WXAPP_QRCOE_CACHE_NUM, clientIP).getBytes(), exTime / 1000); // 过期时间单位为秒
+            return incr;
+        });
+
+        if(null!=increment && increment>20){
+            throw exception(MDE_WX_APP_QRCODE_TOO_MANY);
+        }
+        // 清理 上一次获取到的 验证码信息
+        String s = redisTemplate.opsForValue().get(String.format(MDE_WXAPP_QRCOE, clientIP, reqVo.getClientId()));
+        redisTemplate.delete(String.format(MDE_WXAPP_QRCOE,clientIP,reqVo.getClientId()));
+        if(StringUtils.isNotBlank(s)){
+            redisTemplate.delete(String.format(MDE_WXAPP_QRCOE_AUTHORIZE,s));
+        }
+
+        redisTemplate.opsForValue().set(String.format(MDE_WXAPP_QRCOE,clientIP,reqVo.getClientId()), uuid,exTime, TimeUnit.MILLISECONDS);
+        redisTemplate.opsForValue().set(String.format(MDE_WXAPP_QRCOE_AUTHORIZE,uuid), "wait",exTime, TimeUnit.MILLISECONDS);
+        return     socialClientApi.getWxaQrcode(reqDTO).getCheckedData();
+    }
+
+    @Override
+    public void weixinLoginAuthorize(AppMdeWeixinLoginAuthorize request) {
+        String s = redisTemplate.opsForValue().get(String.format(MDE_WXAPP_QRCOE_AUTHORIZE, request.getUuid()));
+        if(StringUtils.isBlank(s)){
+            throw exception(MDE_WX_APP_QRCODE_EX_TIME);
+        }
+        LoginUser loginUser = LoginUserContext.get();
+        redisTemplate.opsForValue().set(String.format(MDE_WXAPP_QRCOE_AUTHORIZE, request.getUuid()), loginUser.getId().toString(),1000L*60L*5L, TimeUnit.MILLISECONDS);
+    }
+
+    @Override
+    public AppMdeWXAppAuthLoginRespVO weixinLoginAuthorizeCode(AppMdeWeixinLoginQrcodeClientIdReqVo reqVo, String clientIP) throws InterruptedException {
+        String uuid = redisTemplate.opsForValue().get(String.format(MDE_WXAPP_QRCOE, clientIP, reqVo.getClientId()));
+        if (StringUtils.isBlank(uuid)) {
+            throw exception(MDE_WX_APP_QRCODE_EX_TIME);
+        }
+
+        int checkSleep = 200;
+        int checkCount =200;
+        int checkExtime = 100+(checkCount*checkSleep);
+        // 使用 execute 方法确保原子性
+        Long increment = redisTemplate.execute((RedisCallback<Long>) connection -> {
+            // 增加计数器
+            Long incr = connection.incr(String.format(MDE_WXAPP_QRCOE_CHECK, clientIP, reqVo.getClientId()).getBytes());
+            // 设置过期时间
+            connection.expire(String.format(MDE_WXAPP_QRCOE_CHECK, clientIP, reqVo.getClientId()).getBytes(), checkExtime / 1000); // 过期时间单位为秒
+            return incr;
+        });
+
+        redisTemplate.expire(String.format(MDE_WXAPP_QRCOE_CHECK, clientIP, reqVo.getClientId()), 1000L*60L, TimeUnit.MILLISECONDS);
+        if(!Long.valueOf(1).equals(increment)){
+            throw exception(MDE_WX_APP_QRCODE_CHECK_STATE);
+        }
+        AppMdeWXAppAuthLoginRespVO appMdeWXAppAuthLoginRespVO =new AppMdeWXAppAuthLoginRespVO();
+
+        String userId = null;
+        for (int i=0;i<=checkCount;i++){
+            userId = redisTemplate.opsForValue().get(String.format(MDE_WXAPP_QRCOE_AUTHORIZE, uuid));
+            if(StringUtils.isNotBlank(userId) && !"wait".equals(userId)){
+                break;
+            }
+            // 等待下次检查
+            Thread.sleep(checkSleep);
+        }
+        if(StringUtils.isBlank(userId) || "wait".equals(userId)){
+            // TODO 考虑返回新的clientId以保证安全性
+            // 使用 execute 方法确保原子性
+            long exTime = 1000L*60L*5L;
+            String newClientId = IdUtil.fastSimpleUUID();
+            redisTemplate.opsForValue().set(String.format(MDE_WXAPP_QRCOE,clientIP,newClientId), uuid, exTime, TimeUnit.MILLISECONDS);
+            redisTemplate.delete(String.format(MDE_WXAPP_QRCOE,clientIP,reqVo.getClientId()));
+            redisTemplate.delete(String.format(MDE_WXAPP_QRCOE_CHECK, clientIP, reqVo.getClientId()));
+            appMdeWXAppAuthLoginRespVO.setStatus(0);
+            appMdeWXAppAuthLoginRespVO.setClientId(newClientId);
+            return appMdeWXAppAuthLoginRespVO;
+        }
+        // 获得获得注册用户
+        MdeUserDO user = userService.getMdeUser(Long.valueOf(userId));
+        // 是否禁用
+        if (ObjectUtil.notEqual(user.getStatus(), MendunerStatusEnum.ENABLE.getStatus())) {
+            createLoginLog(user, user.getPhone(), LoginLogTypeEnum.LOGIN_WX_APP_AUTHORIZE, LoginResultEnum.USER_DISABLED);
+            throw exception(MDE_AUTH_LOGIN_USER_DISABLED);
+        }        // 创建 Token 令牌,记录登录日志
+
+        appMdeWXAppAuthLoginRespVO.setAppMdeAuthLoginRespVO(createTokenAfterLoginSuccess(user, user.getPhone(), LoginLogTypeEnum.LOGIN_WX_APP_AUTHORIZE, null));
+        appMdeWXAppAuthLoginRespVO.setStatus(1);
+
+        return appMdeWXAppAuthLoginRespVO;
+    }
+
+
     @Override
     public void validateSmsCode(AppMdeAuthSmsValidateReqVO reqVO) {
         smsCodeApi.validateSmsCode(MdeAuthConvert.INSTANCE.convert(reqVO).setMobile(reqVO.getPhone())).getCheckedData();