|
@@ -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"));
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
}
|