|
@@ -1,18 +1,18 @@
|
|
|
-package com.backendsys.modules.app.service.impl;
|
|
|
+package com.backendsys.modules.app.user.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.core.util.StrUtil;
|
|
|
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.app.user.dao.AppUserDao;
|
|
|
+import com.backendsys.modules.app.user.entity.AppAuth;
|
|
|
+import com.backendsys.modules.app.user.entity.AppUser;
|
|
|
+import com.backendsys.modules.app.user.service.AppAuthService;
|
|
|
+import com.backendsys.modules.app.user.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.*;
|
|
@@ -33,6 +33,8 @@ import java.awt.image.BufferedImage;
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
import java.io.IOException;
|
|
|
import java.util.Date;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.TimeZone;
|
|
|
import java.util.UUID;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
@@ -59,19 +61,12 @@ public class AppAuthServiceImpl implements AppAuthService {
|
|
|
@Autowired
|
|
|
private SysCommonService sysCommonService;
|
|
|
|
|
|
- @Value("${tencent.sms.debug}")
|
|
|
- private String SMS_DEBUG;
|
|
|
+ @Value("${spring.application.name}")
|
|
|
+ private String APPLICATION_NAME;
|
|
|
@Value("${CAPTCHA_DURATION}")
|
|
|
private Integer CAPTCHA_DURATION;
|
|
|
@Value("${REDIS_LOGIN_TOKEN_PREFIX}")
|
|
|
private String REDIS_LOGIN_TOKEN_PREFIX;
|
|
|
- @Value("${spring.application.name}")
|
|
|
- private String APPLICATION_NAME;
|
|
|
- private String redisKeyOfLogin = APPLICATION_NAME + "-app-sms-login";
|
|
|
- private String redisKeyOfRegister = APPLICATION_NAME + "-app-sms-register";
|
|
|
- private String redisKeyOfLoginFail = APPLICATION_NAME + "-app-login-error";
|
|
|
- private String redisKeyOfRegisterFail = APPLICATION_NAME + "-app-register-error";
|
|
|
-
|
|
|
|
|
|
@Autowired
|
|
|
private Producer captchaProducer;
|
|
@@ -107,17 +102,25 @@ public class AppAuthServiceImpl implements AppAuthService {
|
|
|
} catch (IOException e) {
|
|
|
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
|
|
|
// [方法] 登录失败 (errMsg: 错误提示文本, phone: 手机号码, intercept: 是否拦截)
|
|
|
public void loginFail(String errMsg, String phone, Boolean isIntercept) {
|
|
|
+ // 验证码是否必填
|
|
|
+ Boolean currentCaptchaRequired = captchaUtil.isCaptchaRequired(APPLICATION_NAME + "-app-login-required-captcha-" + phone, 3);
|
|
|
+ System.out.println("(loginFailByUsername) currentCaptchaRequired = " + currentCaptchaRequired);
|
|
|
+
|
|
|
// 删除图形验证码
|
|
|
- redisUtil.delete(httpRequestUtil.getKaptchaKey());
|
|
|
+ redisUtil.delete(httpRequestUtil.getKaptchaKey("APP"));
|
|
|
// 添加登录错误的冻结标记
|
|
|
- if (isIntercept) lockStatusUtil.setLockStatus(redisKeyOfLoginFail, phone);
|
|
|
- throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode());
|
|
|
+ if (isIntercept) lockStatusUtil.setLockStatus(APPLICATION_NAME + "-app-login-error", phone);
|
|
|
+
|
|
|
+ if (currentCaptchaRequired) {
|
|
|
+ throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode(), Map.of("is_captcha_required", true));
|
|
|
+ } else {
|
|
|
+ throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode());
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// [方法] 登录成功
|
|
@@ -142,7 +145,17 @@ public class AppAuthServiceImpl implements AppAuthService {
|
|
|
appUser.setUser_id(appUser.getId());
|
|
|
appUser.setLast_login_uuid(uuid);
|
|
|
appUser.setLast_login_ip(httpRequestUtil.getIpAddr());
|
|
|
- appUser.setLast_login_time(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
|
|
|
+
|
|
|
+ // 最后登录时间 (本地时间)
|
|
|
+ String localTimeStr = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
|
|
|
+ appUser.setLast_login_time(localTimeStr);
|
|
|
+
|
|
|
+// // UTC 时间
|
|
|
+// String utcTimeStr = DateUtil.date().setTimeZone(TimeZone.getTimeZone("UTC"))
|
|
|
+// .toString("yyyy-MM-dd HH:mm:ss");
|
|
|
+// appUser.setLast_login_time(utcTimeStr);
|
|
|
+
|
|
|
+ // [db] 更新前台用户信息
|
|
|
appUserDao.updateById(appUser);
|
|
|
|
|
|
|
|
@@ -152,6 +165,7 @@ public class AppAuthServiceImpl implements AppAuthService {
|
|
|
Long DEFAULT_MILLISECONDS = APP_USER_LOGIN_DURATION_DEFAULT * DateUnit.HOUR.getMillis();
|
|
|
Integer token_duration_hours = Convert.toInt(DEFAULT_MILLISECONDS / 3600000L);
|
|
|
|
|
|
+ // Token 记录的登录时间,是本地时间
|
|
|
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");
|
|
@@ -169,6 +183,18 @@ public class AppAuthServiceImpl implements AppAuthService {
|
|
|
return appUser;
|
|
|
}
|
|
|
|
|
|
+ private void setLoginRequired(String key) {
|
|
|
+ Object captchaValue = redisUtil.getCacheObject(APPLICATION_NAME + "-app-login-required-captcha-" + key);
|
|
|
+ Integer currentErrCount = (captchaValue == null) ? 1 : (Convert.toInt(captchaValue) + 1);
|
|
|
+ redisUtil.setCacheObject(APPLICATION_NAME + "-app-login-required-captcha-" + key, currentErrCount, 1, TimeUnit.MINUTES);
|
|
|
+ System.out.println("currentErrCount: " + currentErrCount);
|
|
|
+ }
|
|
|
+ private void cleanLoginRequired(String key) {
|
|
|
+ redisUtil.delete(APPLICATION_NAME + "-app-login-required-captcha-" + key);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 前台用户登录 (手机账号)
|
|
|
*/
|
|
@@ -180,27 +206,38 @@ public class AppAuthServiceImpl implements AppAuthService {
|
|
|
String captcha = appAuth.getCaptcha();
|
|
|
|
|
|
// 判断是否处于登录错误的冻结状态 (2分钟内错误5次,则出现冻结提示)
|
|
|
- lockStatusUtil.checkLockStatus(redisKeyOfLoginFail, phone);
|
|
|
-
|
|
|
- // 判断图形验证码是否正确
|
|
|
- if (!captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey("APP"))) {
|
|
|
- loginFail("验证码错误", phone, false);
|
|
|
- return null;
|
|
|
+ lockStatusUtil.checkLockStatus(APPLICATION_NAME + "-app-login-error", phone);
|
|
|
+
|
|
|
+ // -- 判断是否需要输入验证码 ----------------------------------------------------
|
|
|
+ // - 当输错 3 次密码时,需要输入验证码
|
|
|
+ // - 当输错后 1 分钟后重置
|
|
|
+ Boolean isCaptchaRequired = captchaUtil.isCaptchaRequired(APPLICATION_NAME + "-app-login-required-captcha-" + phone, 3);
|
|
|
+ if (isCaptchaRequired) {
|
|
|
+ Boolean isCaptchaEmpty = StrUtil.isEmpty(captcha);
|
|
|
+ Boolean isCpatchaValid = (captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey("APP")));
|
|
|
+ if (isCaptchaEmpty) { loginFail("验证码不能为空", phone, false); return null; }
|
|
|
+ if (!isCpatchaValid) { loginFail("验证码错误", phone, false); return null; }
|
|
|
}
|
|
|
+ // --------------------------------------------------------------------------
|
|
|
|
|
|
// [Method] 判断 用户 是否存在 && 密码是否正确
|
|
|
AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhone, phone));
|
|
|
if (appUser == null) {
|
|
|
+ // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
|
|
|
+ setLoginRequired(phone);
|
|
|
// [登录失败] 用户不存在
|
|
|
loginFail("手机号码或密码错误", phone, true);
|
|
|
return null;
|
|
|
} else {
|
|
|
- // [登录失败] 密码不正确
|
|
|
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
|
|
if (!encoder.matches(password, appUser.getPassword())) {
|
|
|
+ // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
|
|
|
+ setLoginRequired(phone);
|
|
|
+ // [登录失败] 密码不正确
|
|
|
loginFail("手机号码或密码错误", phone, true);
|
|
|
}
|
|
|
// [登录成功]
|
|
|
+ cleanLoginRequired(phone);
|
|
|
return loginSuccess(appUser);
|
|
|
}
|
|
|
|
|
@@ -210,9 +247,9 @@ public class AppAuthServiceImpl implements AppAuthService {
|
|
|
* 前台用户登录 (微信小程序用户)
|
|
|
*/
|
|
|
@Override
|
|
|
- public AppUser loginWithWechatMiniprogram(AppAuthWechat appAuthWechat) {
|
|
|
+ public AppUser loginWithWechatMiniprogram(AppAuth appAuth) {
|
|
|
|
|
|
- String code = appAuthWechat.getCode();
|
|
|
+ String code = appAuth.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"}
|