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.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.AppUser; import com.backendsys.modules.app.service.AppAuthService; 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.*; 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 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 java.util.Date; import java.util.UUID; import java.util.concurrent.TimeUnit; @Service public class AppAuthServiceImpl implements AppAuthService { @Autowired private JwtUtil jwtUtil; @Autowired private TokenUtil tokenUtil; @Autowired private RedisUtil redisUtil; @Autowired private CaptchaUtil captchaUtil; @Autowired private LockStatusUtil lockStatusUtil; @Autowired private HttpRequestUtil httpRequestUtil; @Autowired private AppUserDao appUserDao; @Autowired private SysCommonService sysCommonService; @Value("${tencent.sms.debug}") private String SMS_DEBUG; @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"; // [方法] 登录失败 (errMsg: 错误提示文本, phone: 手机号码, intercept: 是否拦截) private void loginFail(String errMsg, String phone, Boolean isIntercept) { // 删除图形验证码 redisUtil.delete(httpRequestUtil.getKaptchaKey()); // 添加登录错误的冻结标记 if (isIntercept) lockStatusUtil.setLockStatus(redisKeyOfLoginFail, phone); throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode()); } // [方法] 登录成功 private AppUser loginSuccess(AppUser appUser) { // 删除图形验证码缓存 redisUtil.delete(httpRequestUtil.getKaptchaKey()); // 删除旧的登录缓存 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("当前用户不可用,请与客服联系"); // 设置 最后一次的登录信息 (uuid, ip, 登录时间) String uuid = Convert.toStr(UUID.randomUUID()); 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")); appUserDao.updateById(appUser); // [系统配置] 微信用户默认登录过期时间(小时) 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 + 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; } /** * APP登录 (用户名登录) */ @Override public AppUser login(AppAuth appAuth) { String phone = appAuth.getPhone(); String password = appAuth.getPassword(); String captcha = appAuth.getCaptcha(); // 判断是否处于登录错误的冻结状态 (2分钟内错误5次,则出现冻结提示) lockStatusUtil.checkLockStatus(redisKeyOfLoginFail, phone); // 判断图形验证码是否正确 if (!captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey())) { loginFail("验证码错误", phone, false); return null; } // [Method] 判断 用户 是否存在 && 密码是否正确 AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper().eq(AppUser::getPhone, phone)); if (appUser == null) { // [登录失败] 用户不存在 loginFail("手机号码或密码错误", phone, true); return null; } else { // [登录失败] 密码不正确 BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); if (!encoder.matches(password, appUser.getPassword())) { loginFail("手机号码或密码错误", phone, true); } // [登录成功] return loginSuccess(appUser); } } }