tsurumure 5 сар өмнө
parent
commit
9dd4e5a677

+ 1 - 1
db/app_user.sql

@@ -28,7 +28,7 @@ CREATE TABLE `app_user` (
 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='会员表';
 
 INSERT INTO app_user(wechat_open_id, nickname, phone, email, password, gender) VALUES
-    ('o_FiE5k6Bx9svZ12ZpzegVcOaMfc', 'Mure', '13670511519', '405348097@qq.com', '$2a$10$0cFrGBWCP42iqolPYwH5c.ZfpuawXqMl3NiQI2WArzeNdwJyGwGAy', 1),
+    ('o_FiE5k6Bx9svZ12ZpzegVcOaMfc', 'Mure', '13670511519', '405348097@qq.com', '$2a$10$1ShLHm4rj9L8YvzsdwEm4OOGVA4ofudeUqXpK5gwRPnGtr6mKMFPm', 1),
     ('r_VqY5uWtbGiW7vPAQ8VZT6poiRs', '张小红', '13123456781', 'zhangxh@example.com', '$2a$10$0cFrGBWCP42iqolPYwH5c.ZfpuawXqMl3NiQI2WArzeNdwJyGwGAy', 1),
     ('x_LgT3mZfCvXoQ58AlrWE3r9BkeI', '李建国', '13123456782', 'lijg@example.com', '$2a$10$0cFrGBWCP42iqolPYwH5c.ZfpuawXqMl3NiQI2WArzeNdwJyGwGAy', 1),
     ('c_PoE7nF95XvAmT4JuReD4wBhsmL', '王丽华', '13123456783', 'wanglh@example.com', '$2a$10$0cFrGBWCP42iqolPYwH5c.ZfpuawXqMl3NiQI2WArzeNdwJyGwGAy', 1),

+ 3 - 1
db/sys_common.sql

@@ -26,5 +26,7 @@ INSERT INTO sys_common(name, description, tag, value, value_type, value_componen
     ('是否启用文件MD5查重', '已存在的文件不再重复上传,仅返回链接', 'UPLOAD_MD5_DUPLICATE', 1, 'Integer', 'Switch', null, 'UPLOAD', 1),
 
     ('系统用户登录过期时间(小时)', null, 'SYSTEM_USER_LOGIN_DURATION_DEFAULT', 24, 'Integer', 'Number', null, 'SYSTEM_USER', 1),
-    ('是否允许系统用户注册', null, 'SYSTEM_USER_ALLOW_REGISTER', 1, 'Integer', 'Switch', null, 'SYSTEM_USER', 1)
+    ('是否允许系统用户注册', null, 'SYSTEM_USER_ALLOW_REGISTER', 1, 'Integer', 'Switch', null, 'SYSTEM_USER', 1),
+
+    ('移动端用户登录过期时间(小时)', null, 'APP_USER_LOGIN_DURATION_DEFAULT', 168, 'Integer', 'Number', null, 'APP_USER', 1)
 ;

+ 3 - 2
src/main/java/com/backendsys/modules/app/controller/AppAuthController.java

@@ -1,5 +1,6 @@
 package com.backendsys.modules.app.controller;
 
+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.app.service.AppUserService;
@@ -24,7 +25,7 @@ public class AppAuthController {
     @Anonymous
     @Operation(summary = "APP登录 (用户名登录)")
     @PostMapping("/api/app/auth/login")
-    public Result login(@Validated(AppUser.Login.class) @RequestBody AppUser appUser) {
-        return Result.success().put("data", appAuthService.login(appUser));
+    public Result login(@Validated(AppAuth.Login.class) @RequestBody AppAuth appAuth) {
+        return Result.success().put("data", appAuthService.login(appAuth));
     }
 }

+ 20 - 0
src/main/java/com/backendsys/modules/app/entity/AppAuth.java

@@ -0,0 +1,20 @@
+package com.backendsys.modules.app.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+@Data
+public class AppAuth {
+
+    public static interface Login{}
+
+    @NotEmpty(message = "手机号码不能为空", groups = { Login.class })
+    private String phone;
+    @NotEmpty(message = "密码不能为空", groups = { Login.class })
+    private String password;
+    @TableField(exist = false)
+    @NotEmpty(message = "验证码不能为空", groups = { Login.class })
+    private String captcha;
+
+}

+ 7 - 7
src/main/java/com/backendsys/modules/app/entity/AppUser.java

@@ -11,7 +11,6 @@ import lombok.Data;
 @TableName("app_user")
 public class AppUser {
 
-    public static interface Login{}
     public static interface Detail{}
 
     @TableId(type = IdType.AUTO)
@@ -19,14 +18,10 @@ public class AppUser {
     private String wechat_open_id;
     private String nickname;
 
-    @NotEmpty(message = "手机号码不能为空", groups = { Login.class })
     private String phone;
     private String email;
-    @NotEmpty(message = "密码不能为空", groups = { Login.class })
     private String password;
-    @TableField(exist = false)
-    @NotEmpty(message = "验证码不能为空", groups = { Login.class })
-    private String captcha;
+
 
     private String gender;
     private String avatar;
@@ -36,6 +31,11 @@ public class AppUser {
     private Integer status;
     private String create_time;
     private String update_time;
-    private String del_flag;
+    private Integer del_flag;
+
+    @TableField(exist = false)
+    private String token_expiration;
+    @TableField(exist = false)
+    private String token;
 
 }

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

@@ -1,5 +1,6 @@
 package com.backendsys.modules.app.service;
 
+import com.backendsys.modules.app.entity.AppAuth;
 import com.backendsys.modules.app.entity.AppUser;
 
 import java.util.Map;
@@ -7,6 +8,6 @@ import java.util.Map;
 public interface AppAuthService {
 
     // APP登录 (用户名登录)
-    Map<String, Object> login(AppUser appUser);
+    AppUser login(AppAuth appAuth);
 
 }

+ 85 - 15
src/main/java/com/backendsys/modules/app/service/impl/AppAuthServiceImpl.java

@@ -1,20 +1,32 @@
 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.utils.CaptchaUtil;
-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.LockStatusUtil;
+import com.backendsys.modules.common.config.security.entity.SecurityAppUserInfo;
+import com.backendsys.modules.common.config.security.entity.SecurityUserInfo;
+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.List;
 import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 @Service
 public class AppAuthServiceImpl implements AppAuthService {
@@ -22,6 +34,8 @@ public class AppAuthServiceImpl implements AppAuthService {
     @Autowired
     private JwtUtil jwtUtil;
     @Autowired
+    private TokenUtil tokenUtil;
+    @Autowired
     private RedisUtil redisUtil;
     @Autowired
     private CaptchaUtil captchaUtil;
@@ -32,6 +46,8 @@ public class AppAuthServiceImpl implements AppAuthService {
 
     @Autowired
     private AppUserDao appUserDao;
+    @Autowired
+    private SysCommonService sysCommonService;
 
     @Value("${tencent.sms.debug}")
     private String SMS_DEBUG;
@@ -56,15 +72,62 @@ public class AppAuthServiceImpl implements AppAuthService {
         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.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"));
+
+        // 生成 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 Map<String, Object> login(AppUser appUser) {
+    public AppUser login(AppAuth appAuth) {
 
-        String phone = appUser.getPhone();
-        String password = appUser.getPassword();
-        String captcha = appUser.getCaptcha();
+        String phone = appAuth.getPhone();
+        String password = appAuth.getPassword();
+        String captcha = appAuth.getCaptcha();
 
         // 判断是否处于登录错误的冻结状态 (2分钟内错误5次,则出现冻结提示)
         lockStatusUtil.checkLockStatus(redisKeyOfLoginFail, phone);
@@ -75,15 +138,22 @@ public class AppAuthServiceImpl implements AppAuthService {
             return null;
         }
 
-        /*
-        // [Method] 判断 用户是否存在 && 密码是否正确
-        Map<String, Object> b2cMemberDTOSimple = b2cMemberMapper.queryMemberByIdOrName(null, phone);
-        if (!(b2cMemberDTOSimple != null && isUserPasswordValid(b2cMemberDTOSimple, password))) {
-            return Result.error(ResultEnum.INVALID_CREDENTIALS.getCode(), "用户名或密码错误");
+        // [Method] 判断 用户 是否存在 && 密码是否正确
+        AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper<AppUser>().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);
         }
-         */
 
-        return null;
     }
 
 

+ 15 - 0
src/main/java/com/backendsys/modules/common/config/security/entity/SecurityAppUserInfo.java

@@ -0,0 +1,15 @@
+package com.backendsys.modules.common.config.security.entity;
+
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class SecurityAppUserInfo {
+    private Long id;
+    private Long user_id;
+    private String phone;
+    private String last_login_uuid;
+    private String token_expiration;
+}

+ 17 - 1
src/main/java/com/backendsys/modules/common/config/security/utils/JwtUtil.java

@@ -2,6 +2,7 @@ package com.backendsys.modules.common.config.security.utils;
 
 import cn.hutool.core.convert.Convert;
 import cn.hutool.json.JSONUtil;
+import com.backendsys.modules.common.config.security.entity.SecurityAppUserInfo;
 import com.backendsys.modules.common.config.security.entity.SecurityUserInfo;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.Jwts;
@@ -43,7 +44,7 @@ public class JwtUtil {
 
 
     /**
-     * 创建 Token ()
+     * 创建 Token (系统用户)
      * @param securityUserInfo
      * @return
      */
@@ -58,6 +59,21 @@ public class JwtUtil {
             .compact();
     }
 
+    /**
+     * 创建 Token (APP用户)
+     * @param securityAppUserInfo
+     * @return
+     */
+    public String createAppJwtToken(SecurityAppUserInfo securityAppUserInfo) {
+        return Jwts.builder()
+            // subject 代表这个JWT的主体,即它的所有人
+            .subject(securityAppUserInfo.getLast_login_uuid())
+            .claim("userInfo", securityAppUserInfo)
+            .signWith(getSignInKey())
+            .expiration(Convert.toDate(securityAppUserInfo.getToken_expiration()))
+            .compact();
+    }
+
 
 //    /**
 //     * 创建Token (即将弃用)

+ 2 - 4
src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java

@@ -145,10 +145,10 @@ public class SysAuthServiceImpl implements SysAuthService {
 
         // 判断用户是否已删除
         Integer del_flag = sysUserInfo.getDel_flag();
-        if (del_flag != null && del_flag.equals(1)) throw new CustException("用户处于预删除状态,请与客服联系");
+        if (del_flag != null && del_flag.equals(1)) throw new CustException("当前用户不可用,请与客服联系");
 
         // 设置 最后一次的登录信息 (uuid, ip, 登录时间)
-        String uuid = String.valueOf(UUID.randomUUID());
+        String uuid = Convert.toStr(UUID.randomUUID());
         sysUserInfo.setLast_login_uuid(uuid);
         sysUserInfo.setLast_login_ip(httpRequestUtil.getIpAddr());
         sysUserInfo.setLast_login_time(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
@@ -210,13 +210,11 @@ public class SysAuthServiceImpl implements SysAuthService {
             loginFail("用户名或密码错误", username, true);
             return null;
         } else {
-
             // [登录失败] 密码不正确
             BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
             if (!encoder.matches(password, sysUser.getPassword())) {
                 loginFail("用户名或密码错误", username, true);
             }
-
             // [登录成功]
             return loginSuccess(sysUser.getId(), sysAuth.getIs_remember());
         }