tsurumure 1 місяць тому
батько
коміт
9a59fcdea1

+ 17 - 10
src/main/java/com/backendsys/modules/app/controller/AppAuthController.java

@@ -1,19 +1,22 @@
 package com.backendsys.modules.app.controller;
 
 import com.backendsys.modules.app.entity.AppAuth;
+import com.backendsys.modules.app.entity.AppAuthWechat;
 import com.backendsys.modules.app.service.AppAuthService;
 import com.backendsys.modules.common.config.security.annotations.Anonymous;
 import com.backendsys.modules.common.utils.Result;
-import com.backendsys.modules.sdk.wechat.miniprogram.entity.WechatAuth;
-import com.backendsys.modules.sdk.wechat.miniprogram.service.WechatAuthService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.io.IOException;
+
 @Validated
 @RestController
 @Tag(name = "APP用户登录与注册")
@@ -22,21 +25,25 @@ public class AppAuthController {
     @Autowired
     private AppAuthService appAuthService;
 
-    @Autowired
-    private WechatAuthService wechatAuthService;
+    @Anonymous
+    @Operation(summary = "获取图形验证码")
+    @GetMapping("/api/app/auth/getCaptcha")
+    public void getCaptcha(HttpServletResponse response) throws IOException {
+        appAuthService.renderCaptcha(response);
+    }
 
     @Anonymous
-    @Operation(summary = "APP登录 (用户名登录)")
-    @PostMapping("/api/app/auth/login")
+    @Operation(summary = "前台用户登录 (手机账号)")
+    @PostMapping("/api/app/auth/loginWithPhone")
     public Result login(@Validated(AppAuth.Login.class) @RequestBody AppAuth appAuth) {
-        return Result.success().put("data", appAuthService.login(appAuth));
+        return Result.success().put("data", appAuthService.loginWithPhone(appAuth));
     }
 
     @Anonymous
-    @Operation(summary = "微信小程序用户登录")
+    @Operation(summary = "前台用户登录 (微信小程序用户)")
     @PostMapping("/api/app/auth/loginWithWechatMiniprogram")
-    public Result loginWithWechatMiniprogram(@Validated(WechatAuth.Login.class) @RequestBody WechatAuth wechatAuth) {
-        return Result.success().put("data", wechatAuthService.loginWithWechatMiniprogram(wechatAuth));
+    public Result loginWithWechatMiniprogram(@Validated(AppAuthWechat.Login.class) @RequestBody AppAuthWechat appAuthWechat) {
+        return Result.success().put("data", appAuthService.loginWithWechatMiniprogram(appAuthWechat));
     }
 
 }

+ 2 - 2
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/entity/WechatAuth.java → src/main/java/com/backendsys/modules/app/entity/AppAuthWechat.java

@@ -1,10 +1,10 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.entity;
+package com.backendsys.modules.app.entity;
 
 import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
 
 @Data
-public class WechatAuth {
+public class AppAuthWechat {
 
     public static interface Login{}
 

+ 18 - 2
src/main/java/com/backendsys/modules/app/service/AppAuthService.java

@@ -1,11 +1,27 @@
 package com.backendsys.modules.app.service;
 
 import com.backendsys.modules.app.entity.AppAuth;
+import com.backendsys.modules.app.entity.AppAuthWechat;
 import com.backendsys.modules.app.entity.AppUser;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
 
 public interface AppAuthService {
 
-    // APP登录 (用户名登录)
-    AppUser login(AppAuth appAuth);
+    // 获取图形验证码
+    void renderCaptcha(HttpServletResponse response) throws IOException;
+
+    // 前台用户登录 (手机账号)
+    AppUser loginWithPhone(AppAuth appAuth);
+
+    // 前台用户登录 (微信小程序用户)
+    AppUser loginWithWechatMiniprogram(AppAuthWechat appAuthWechat);
+
+
+    // [事件] 登录成功
+    AppUser loginSuccess(AppUser appUser);
+    // [事件] 登录失败
+    void loginFail(String errMsg, String phone, Boolean isIntercept);
 
 }

+ 109 - 7
src/main/java/com/backendsys/modules/app/service/impl/AppAuthServiceImpl.java

@@ -3,12 +3,16 @@ package com.backendsys.modules.app.service.impl;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.date.DateUnit;
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.backendsys.exception.CustException;
 import com.backendsys.modules.app.dao.AppUserDao;
 import com.backendsys.modules.app.entity.AppAuth;
+import com.backendsys.modules.app.entity.AppAuthWechat;
 import com.backendsys.modules.app.entity.AppUser;
 import com.backendsys.modules.app.service.AppAuthService;
+import com.backendsys.modules.app.utils.AuthWechat.WechatUtil;
 import com.backendsys.modules.common.config.redis.utils.RedisUtil;
 import com.backendsys.modules.common.config.security.entity.SecurityAppUserInfo;
 import com.backendsys.modules.common.config.security.utils.*;
@@ -16,11 +20,18 @@ import com.backendsys.modules.system.entity.TokenCatch;
 import com.backendsys.modules.system.service.SysCommonService;
 import com.backendsys.utils.response.ResultEnum;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.google.code.kaptcha.Producer;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.stereotype.Service;
 
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.util.Date;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
@@ -35,6 +46,8 @@ public class AppAuthServiceImpl implements AppAuthService {
     @Autowired
     private RedisUtil redisUtil;
     @Autowired
+    private WechatUtil wechatUtil;
+    @Autowired
     private CaptchaUtil captchaUtil;
     @Autowired
     private LockStatusUtil lockStatusUtil;
@@ -60,8 +73,46 @@ public class AppAuthServiceImpl implements AppAuthService {
     private String redisKeyOfRegisterFail = APPLICATION_NAME + "-app-register-error";
 
 
+    @Autowired
+    private Producer captchaProducer;
+
+    @Override
+    public void renderCaptcha(HttpServletResponse response) throws IOException {
+        try {
+
+            byte[] captchaChallengeAsJpeg;
+            ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
+
+            String createText = captchaProducer.createText();
+            // 获得当前 (UA + IP) 生成的 Key
+            String captchaRedisKey = httpRequestUtil.getKaptchaKey("APP");
+            // 保存 验证码字符串 到 redis 中
+            redisUtil.setCacheObject(captchaRedisKey, createText, this.CAPTCHA_DURATION, TimeUnit.MILLISECONDS);
+            // 返回 BufferedImage 对象并转为 byte 写入到 byte 数组中
+            BufferedImage challenge = captchaProducer.createImage(createText);
+            ImageIO.write(challenge, "jpg", jpegOutputStream);
+
+            // 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
+            captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
+            response.setHeader("Cache-Control", "no-store");
+            response.setHeader("Pragma", "no-cache");
+            response.setDateHeader("Expires", 0);
+            response.setContentType("image/jpeg");
+
+            ServletOutputStream responseOutputStream = response.getOutputStream();
+            responseOutputStream.write(captchaChallengeAsJpeg);
+            responseOutputStream.flush();
+            responseOutputStream.close();
+
+        } catch (IOException e) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+        }
+
+    }
+
+
     // [方法] 登录失败 (errMsg: 错误提示文本, phone: 手机号码, intercept: 是否拦截)
-    private void loginFail(String errMsg, String phone, Boolean isIntercept) {
+    public void loginFail(String errMsg, String phone, Boolean isIntercept) {
         // 删除图形验证码
         redisUtil.delete(httpRequestUtil.getKaptchaKey());
         // 添加登录错误的冻结标记
@@ -70,10 +121,10 @@ public class AppAuthServiceImpl implements AppAuthService {
     }
 
     // [方法] 登录成功
-    private AppUser loginSuccess(AppUser appUser) {
+    public AppUser loginSuccess(AppUser appUser) {
 
-        // 删除图形验证码缓存
-        redisUtil.delete(httpRequestUtil.getKaptchaKey());
+        // 删除图形验证码缓存 (如果有的话)
+        redisUtil.delete(httpRequestUtil.getKaptchaKey("APP"));
 
         // 删除旧的登录缓存
         tokenUtil.deleteRedisLoginToken(appUser.getLast_login_uuid());
@@ -119,10 +170,10 @@ public class AppAuthServiceImpl implements AppAuthService {
     }
 
     /**
-     * APP登录 (用户名登录)
+     * 前台用户登录 (手机账号)
      */
     @Override
-    public AppUser login(AppAuth appAuth) {
+    public AppUser loginWithPhone(AppAuth appAuth) {
 
         String phone = appAuth.getPhone();
         String password = appAuth.getPassword();
@@ -132,7 +183,7 @@ public class AppAuthServiceImpl implements AppAuthService {
         lockStatusUtil.checkLockStatus(redisKeyOfLoginFail, phone);
 
         // 判断图形验证码是否正确
-        if (!captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey())) {
+        if (!captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey("APP"))) {
             loginFail("验证码错误", phone, false);
             return null;
         }
@@ -155,4 +206,55 @@ public class AppAuthServiceImpl implements AppAuthService {
 
     }
 
+    /**
+     * 前台用户登录 (微信小程序用户)
+     */
+    @Override
+    public AppUser loginWithWechatMiniprogram(AppAuthWechat appAuthWechat) {
+
+        String code = appAuthWechat.getCode();
+        JSONObject response = wechatUtil.getCode2Session(code);
+        // [失败] 返回值:{"errcode":40164,"errmsg":"invalid ip 116.31.165.86 ipv6 ::ffff:116.31.165.86, not in whitelist, rid: 67d15951-5b3f2778-79d6f047"}
+        // [成功] 返回值:{"session_key":"uQAcry2PC1Lx/Krp+6rr0g==","openid":"oSB9r7T7kN1bC7PabJ7RmTUiaJmo"}
+
+        // - errcode: 错误码,请求失败时返回
+        if (!response.containsKey("errcode")) {
+
+            String openid = response.getStr("openid");
+            String session_key = response.getStr("session_key");
+
+            String uuid = Convert.toStr(UUID.randomUUID());
+            String last_login_ip = httpRequestUtil.getIpAddr();
+            String last_login_time = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+            // [Get] 判断用户是否存在 (openid)
+            AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getWechat_open_id, openid));
+            if (appUser == null) {
+                // [Insert] 不存在,则创建
+                appUser = new AppUser();
+                appUser.setWechat_open_id(openid);
+                appUser.setNickname("微信用户" + RandomUtil.randomStringUpper(4));
+                //
+                appUser.setLast_login_uuid(uuid);
+                appUser.setLast_login_ip(last_login_ip);
+                appUser.setLast_login_time(last_login_time);
+                appUserDao.insert(appUser);
+            } else {
+                // [Update] 更新时间
+                appUser.setLast_login_uuid(uuid);
+                appUser.setLast_login_ip(last_login_ip);
+                appUser.setLast_login_time(last_login_time);
+                appUserDao.updateById(appUser);
+            }
+            appUser.setUser_id(appUser.getId());
+            appUser.setSession_key(session_key);
+
+            // 登录成功
+            return loginSuccess(appUser);
+        } else {
+            throw new CustException(response.getStr("errmsg"));
+        }
+
+    }
+
 }

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/AES_Dec.java → src/main/java/com/backendsys/modules/app/utils/AuthWechat/AES_Dec.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.utils.AuthWechat;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.GCMParameterSpec;

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/AES_Enc.java → src/main/java/com/backendsys/modules/app/utils/AuthWechat/AES_Enc.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.utils.AuthWechat;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.GCMParameterSpec;

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/RSA_Sign.java → src/main/java/com/backendsys/modules/app/utils/AuthWechat/RSA_Sign.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.utils.AuthWechat;
 
 import com.google.gson.JsonObject;
 import java.nio.charset.StandardCharsets;

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/RSA_Verify.java → src/main/java/com/backendsys/modules/app/utils/AuthWechat/RSA_Verify.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.utils.AuthWechat;
 
 import com.google.gson.JsonObject;
 import java.io.ByteArrayInputStream;

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/utils/WechatUtil.java → src/main/java/com/backendsys/modules/app/utils/AuthWechat/WechatUtil.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.utils;
+package com.backendsys.modules.app.utils.AuthWechat;
 
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONObject;

+ 5 - 0
src/main/java/com/backendsys/modules/common/config/security/utils/HttpRequestUtil.java

@@ -141,6 +141,11 @@ public class HttpRequestUtil {
         String userAgent = getUa();
         return "captcha:ua:ip:" + CommonUtil.MD5(ip + userAgent);
     }
+    public String getKaptchaKey(String key) {
+        String ip = getIpAddr();
+        String userAgent = getUa();
+        return "captcha:ua:ip:" + CommonUtil.MD5(ip + userAgent + key);
+    }
 
     public String getUa() {
         HttpServletRequest request = getRequest();

+ 0 - 11
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/service/WechatAuthService.java

@@ -1,11 +0,0 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.service;
-
-import com.backendsys.modules.app.entity.AppUser;
-import com.backendsys.modules.sdk.wechat.miniprogram.entity.WechatAuth;
-
-public interface WechatAuthService {
-
-    // 微信小程序用户登录
-    AppUser loginWithWechatMiniprogram(WechatAuth wechatAuth);
-
-}

+ 0 - 143
src/main/java/com/backendsys/modules/sdk/wechat/miniprogram/service/impl/WechatAuthServiceImpl.java

@@ -1,143 +0,0 @@
-package com.backendsys.modules.sdk.wechat.miniprogram.service.impl;
-
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.date.DateUnit;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.util.RandomUtil;
-import cn.hutool.json.JSONObject;
-import cn.hutool.json.JSONUtil;
-import com.backendsys.exception.CustException;
-import com.backendsys.modules.app.dao.AppUserDao;
-import com.backendsys.modules.app.entity.AppUser;
-import com.backendsys.modules.common.config.redis.utils.RedisUtil;
-import com.backendsys.modules.common.config.security.entity.SecurityAppUserInfo;
-import com.backendsys.modules.common.config.security.utils.HttpRequestUtil;
-import com.backendsys.modules.common.config.security.utils.JwtUtil;
-import com.backendsys.modules.common.config.security.utils.TokenUtil;
-import com.backendsys.modules.sdk.wechat.miniprogram.entity.WechatAuth;
-import com.backendsys.modules.sdk.wechat.miniprogram.service.WechatAuthService;
-import com.backendsys.modules.sdk.wechat.miniprogram.utils.WechatUtil;
-import com.backendsys.modules.system.entity.TokenCatch;
-import com.backendsys.modules.system.service.SysCommonService;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import java.util.Date;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-@Service
-public class WechatAuthServiceImpl implements WechatAuthService {
-
-    @Autowired
-    private JwtUtil jwtUtil;
-    @Autowired
-    private TokenUtil tokenUtil;
-    @Autowired
-    private RedisUtil redisUtil;
-    @Autowired
-    private WechatUtil wechatUtil;
-    @Autowired
-    private HttpRequestUtil httpRequestUtil;
-
-    @Value("${REDIS_LOGIN_TOKEN_PREFIX}")
-    private String REDIS_LOGIN_TOKEN_PREFIX;
-
-    @Autowired
-    private SysCommonService sysCommonService;
-    @Autowired
-    private AppUserDao appUserDao;
-
-
-    // [方法] 登录成功
-    private AppUser loginSuccess(AppUser appUser) {
-
-        // 删除旧的登录缓存
-        tokenUtil.deleteRedisLoginToken(appUser.getLast_login_uuid());
-
-        // 判断用户是否启用
-        Integer status = appUser.getStatus();
-        if (status != null && status.equals(-1)) throw new CustException("该用户已被禁用,请与客服联系");
-
-        // 判断用户是否已删除
-        Integer del_flag = appUser.getDel_flag();
-        if (del_flag != null && del_flag.equals(1)) throw new CustException("当前用户不可用,请与客服联系");
-
-        // [系统配置] 微信用户默认登录过期时间(小时)
-        Integer APP_USER_LOGIN_DURATION_DEFAULT = Convert.toInt(sysCommonService.getCommonByTag("APP_USER_LOGIN_DURATION_DEFAULT"));
-        // 将小时转换为毫秒
-        Long DEFAULT_MILLISECONDS = APP_USER_LOGIN_DURATION_DEFAULT * DateUnit.HOUR.getMillis();
-        Integer token_duration_hours = Convert.toInt(DEFAULT_MILLISECONDS / 3600000L);
-
-        Date token_expiration = new Date((new Date()).getTime() + DEFAULT_MILLISECONDS);
-        appUser.setToken_expiration(DateUtil.format(token_expiration, "yyyy-MM-dd HH:mm:ss"));
-        appUser.setRole("APP_USER");
-
-        // 生成 Token
-        SecurityAppUserInfo securityUserInfo = JSONUtil.toBean(JSONUtil.parseObj(appUser), SecurityAppUserInfo.class);
-
-        String token = jwtUtil.createAppJwtToken(securityUserInfo);
-        String token_redis_key = REDIS_LOGIN_TOKEN_PREFIX + appUser.getLast_login_uuid();
-        appUser.setToken(token);
-
-        // [Redis] 将 Token 存入缓存
-        TokenCatch tokenCatch = new TokenCatch(token, null);
-        redisUtil.setCacheObject(token_redis_key, JSONUtil.toJsonStr(tokenCatch), token_duration_hours, TimeUnit.HOURS);
-
-        return appUser;
-    }
-
-    /**
-     * 微信小程序用户登录
-     */
-    @Override
-    public AppUser loginWithWechatMiniprogram(WechatAuth wechatAuth) {
-
-        String code = wechatAuth.getCode();
-        JSONObject response = wechatUtil.getCode2Session(code);
-        // [失败] 返回值:{"errcode":40164,"errmsg":"invalid ip 116.31.165.86 ipv6 ::ffff:116.31.165.86, not in whitelist, rid: 67d15951-5b3f2778-79d6f047"}
-        // [成功] 返回值:{"session_key":"uQAcry2PC1Lx/Krp+6rr0g==","openid":"oSB9r7T7kN1bC7PabJ7RmTUiaJmo"}
-
-        // - errcode: 错误码,请求失败时返回
-        if (!response.containsKey("errcode")) {
-
-            String openid = response.getStr("openid");
-            String session_key = response.getStr("session_key");
-
-            String uuid = Convert.toStr(UUID.randomUUID());
-            String last_login_ip = httpRequestUtil.getIpAddr();
-            String last_login_time = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
-
-            // [Get] 判断用户是否存在 (openid)
-            AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getWechat_open_id, openid));
-            if (appUser == null) {
-                // [Insert] 不存在,则创建
-                appUser = new AppUser();
-                appUser.setWechat_open_id(openid);
-                appUser.setNickname("微信用户" + RandomUtil.randomStringUpper(4));
-                //
-                appUser.setLast_login_uuid(uuid);
-                appUser.setLast_login_ip(last_login_ip);
-                appUser.setLast_login_time(last_login_time);
-                appUserDao.insert(appUser);
-            } else {
-                // [Update] 更新时间
-                appUser.setLast_login_uuid(uuid);
-                appUser.setLast_login_ip(last_login_ip);
-                appUser.setLast_login_time(last_login_time);
-                appUserDao.updateById(appUser);
-            }
-            appUser.setUser_id(appUser.getId());
-            appUser.setSession_key(session_key);
-
-            // 登录成功
-            return loginSuccess(appUser);
-        } else {
-            throw new CustException(response.getStr("errmsg"));
-        }
-
-    }
-
-}

+ 2 - 0
src/main/java/com/backendsys/modules/system/controller/SysAuthController.java

@@ -36,11 +36,13 @@ public class SysAuthController {
     public void getCaptcha(HttpServletResponse response) throws IOException {
         sysAuthService.renderCaptcha(response);
     }
+
     @Operation(summary = "判断是否需验证码登录状态")
     @GetMapping("/api/system/auth/checkCaptchaRequired")
     public Result checkCaptchaRequired(String username) {
         return Result.success().put("data", sysAuthService.checkCaptchaRequired(username));
     }
+
     @Operation(summary = "系统用户登录 (用户名)")
     @PostMapping(value = "/api/system/auth/login")
     public Result systemLogin(@Validated(SysAuth.Login.class) @RequestBody SysAuth sysAuth) {