diff --git a/doc/loginAuth.sql b/doc/loginAuth.sql new file mode 100644 index 00000000..9114ca36 --- /dev/null +++ b/doc/loginAuth.sql @@ -0,0 +1,16 @@ +CREATE TABLE `cai_user_login` +( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '子账户ID', + `user_id` bigint(20) NOT NULL COMMENT '用户ID', + `usercode` varchar(100) NOT NULL COMMENT '用户', + `mobile` varchar(100) NOT NULL COMMENT '账户明细说明', + `password` varchar(100) NOT NULL COMMENT '账户明细说明', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `user_id` (`user_id`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 1 + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci + ROW_FORMAT = DYNAMIC COMMENT ='123记录'; diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/admin/op/LoginAuthController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/admin/op/LoginAuthController.java new file mode 100644 index 00000000..b3e1ee31 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/admin/op/LoginAuthController.java @@ -0,0 +1,46 @@ +package com.ruoyi.web.controller.cai.admin.op; + +import cn.dev33.satoken.annotation.SaCheckRole; +import com.ruoyi.cai.service.LoginAuthService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/cai/op/login") +@Slf4j +public class LoginAuthController { + + @Autowired + private LoginAuthService loginAuthService; + + @GetMapping("/testLogin") + @SaCheckRole("admin") + public R testLogin(String passwords) { + if(StringUtils.isEmpty(passwords)){ + return R.fail("密码不能为空"); + } + List passwordList = Arrays.stream(passwords.split(",")).collect(Collectors.toList()); + loginAuthService.testLogin(passwordList); + return R.ok(); + } + + @GetMapping("/testPassword") + @SaCheckRole("admin") + public R testPassword(String mobile,String password) { + boolean b = loginAuthService.checkPassword(mobile, password); + return R.ok(b); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/app/AuthAppController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/app/AuthAppController.java index a83043e9..c5dfa30a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/app/AuthAppController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/app/AuthAppController.java @@ -3,6 +3,7 @@ package com.ruoyi.web.controller.cai.app; import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.util.PhoneUtil; import com.ruoyi.cai.auth.*; +import com.ruoyi.cai.constant.RedisHttpConstant; import com.ruoyi.cai.dto.app.vo.LoginVo; import com.ruoyi.cai.enums.CodeEnum; import com.ruoyi.cai.enums.SystemConfigEnum; @@ -17,11 +18,15 @@ import com.ruoyi.cai.service.UserService; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RAtomicLong; +import org.redisson.api.RedissonClient; +import org.redisson.client.RedisClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; @@ -106,6 +111,7 @@ public class AuthAppController { } + @Deprecated @PostMapping("/register/code") @Operation(summary = "获取注册验证码") @Log(title = "获取注册验证码", businessType = BusinessType.OTHER, isSaveDb = false) @@ -143,6 +149,13 @@ public class AuthAppController { return R.fail(600,"9000009"); } ipBlackService.checkIpThrowException(ServletUtils.getClientIP()); + if(StringUtils.isBlank(code.getUserIp())){ + code.setUserIp(ServletUtils.getClientIP()); + } + boolean check = verificationCodeCheck.check(code.getTicket(), code.getUserIp(), code.getRandStr()); + if(!check){ + throw new ServiceException("图形验证码错误"); + } try { smsVerifyService.put(CodeEnum.RESET_PASSWORD,code.getMobile()); }catch (Exception e){ @@ -159,10 +172,69 @@ public class AuthAppController { @Autowired private IpBlackService ipBlackService; + + @PostMapping("/loginV2") + @Operation(summary = "登陆") + @Log(title = "登陆", businessType = BusinessType.OTHER, isSaveDb = false) + public R loginV2(@Validated @RequestBody LoginCaiUser loginBody){ + LoginVo vo = new LoginVo(); + ipBlackService.checkIpThrowException(ServletUtils.getClientIP()); + boolean needVerificationCode = this.checkNeedVerificationCode(loginBody); + if(needVerificationCode){ + vo.setLoginSuccess(false); + vo.setNeedVerificationCode(true); + return R.ok(vo); + } + try { + String token = caiLoginManager.login(loginBody.getUsername(), loginBody.getPassword()); + vo.setLoginSuccess(true); + vo.setToken(token); + vo.setUserInfo(currentUserManager.currentInfo()); + }catch (Exception e){ + loginAfterManager.loginAfter(loginBody,false,e.getMessage()); + ipRecordService.saveLoginIp(ServletUtils.getClientIP()); + throw e; + } + loginAfterManager.loginAfter(loginBody,true,"登录成功"); + return R.ok(vo); + } + + @Autowired + private RedissonClient redissonClient; + + private boolean checkNeedVerificationCode(LoginCaiUser loginBody){ + if(StringUtils.isNotEmpty(loginBody.getTicket()) || StringUtils.isNotEmpty(loginBody.getRandStr())){ + if(StringUtils.isBlank(loginBody.getUserIp())){ + loginBody.setUserIp(ServletUtils.getClientIP()); + } + boolean check = verificationCodeCheck.check(loginBody.getTicket(), loginBody.getUserIp(), loginBody.getRandStr()); + if(!check){ + throw new ServiceException("图形验证码错误"); + } + }else{ + Integer loginErrorOpenSecurity = systemConfigManager.getSystemConfigOfInt(SystemConfigEnum.LOGIN_ERROR_OPEN_SECURITY); + if(loginErrorOpenSecurity <= 0){ + return true; + } + String key = String.format(RedisHttpConstant.CHECK_LOGIN_NUM, loginBody.getUsername()); + RAtomicLong atomicLong = redissonClient.getAtomicLong(key); + if(atomicLong.get() >= loginErrorOpenSecurity){ + return true; + } + } + return false; + } + + + @PostMapping("/login") @Operation(summary = "登陆") @Log(title = "登陆", businessType = BusinessType.OTHER, isSaveDb = false) public R login(@Validated @RequestBody LoginCaiUser loginBody){ + boolean openOldLoginApi = systemConfigManager.getSystemConfigOfBool(SystemConfigEnum.OPEN_OLD_LOGIN_API); + if(!openOldLoginApi){ + return R.fail("404"); + } LoginVo vo = new LoginVo(); ipBlackService.checkIpThrowException(ServletUtils.getClientIP()); try { @@ -170,11 +242,11 @@ public class AuthAppController { vo.setToken(token); vo.setUserInfo(currentUserManager.currentInfo()); }catch (Exception e){ + loginAfterManager.loginAfter(loginBody,false,e.getMessage()); ipRecordService.saveLoginIp(ServletUtils.getClientIP()); throw e; } - // 异步调用通知 -// loginAfterManager.loginAfter(LoginHelper.getUserId()); + loginAfterManager.loginAfter(loginBody,true,"登录成功"); return R.ok(vo); } diff --git a/ruoyi-admin/src/main/resources/logback-plus.xml b/ruoyi-admin/src/main/resources/logback-plus.xml index 40fa33b7..0f0d7454 100644 --- a/ruoyi-admin/src/main/resources/logback-plus.xml +++ b/ruoyi-admin/src/main/resources/logback-plus.xml @@ -13,6 +13,25 @@ + + + ${log.path}/login/sys-login.log + + + ${log.path}/login/sys-login.%d{yyyy-MM-dd}.log + + 1 + + + ${log.pattern} + utf-8 + + + + INFO + + + ${log.path}/sys-console.log @@ -117,6 +136,10 @@ + + + + diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/auth/LoginCaiUser.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/auth/LoginCaiUser.java index 7d63dd25..ae389b1f 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/auth/LoginCaiUser.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/auth/LoginCaiUser.java @@ -14,4 +14,8 @@ public class LoginCaiUser { @Schema(description = "密码") @NotEmpty(message = "密码不能为空") private String password; + + private String ticket; + private String randStr; + private String userIp; } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/auth/RegisterCode.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/auth/RegisterCode.java index d49842bf..438657db 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/auth/RegisterCode.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/auth/RegisterCode.java @@ -8,4 +8,10 @@ import javax.validation.constraints.NotEmpty; public class RegisterCode { @NotEmpty(message = "手机号不能为空") private String mobile; + + @NotEmpty(message = "腾讯验证码参数[ticket]") + private String ticket; + @NotEmpty(message = "腾讯验证码所属参数[randStr]") + private String randStr; + private String userIp; } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/domain/LoginAuth.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/domain/LoginAuth.java new file mode 100644 index 00000000..f872594f --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/domain/LoginAuth.java @@ -0,0 +1,22 @@ +package com.ruoyi.cai.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import org.joda.time.LocalDateTime; + +import java.io.Serializable; + +@Data +@TableName("cai_user_login") +public class LoginAuth implements Serializable { + @TableId(value = "id",type = IdType.AUTO) + private Long id; + private Long userId; + private String usercode; + private String mobile; + private String password; + private LocalDateTime createTime; + private LocalDateTime updateTime; +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/dto/app/vo/LoginVo.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/dto/app/vo/LoginVo.java index 97230520..d108963f 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/dto/app/vo/LoginVo.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/dto/app/vo/LoginVo.java @@ -4,6 +4,10 @@ import lombok.Data; @Data public class LoginVo { + // 是否登录成功 + private boolean loginSuccess; + // 是否需要图形验证码 + private boolean needVerificationCode; private String token; private CurrentUserInfoVo userInfo; } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/enums/SystemConfigEnum.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/enums/SystemConfigEnum.java index 62cf2993..0a026ef7 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/enums/SystemConfigEnum.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/enums/SystemConfigEnum.java @@ -16,6 +16,9 @@ public enum SystemConfigEnum { * 安全配置 */ OPEN_IP_NUMBER("5", "IP每日登录次数超过多少次封",SystemConfigGroupEnum.SECURITY), + LOGIN_ERROR_OPEN_SECURITY("2", "输入密码次数超过多少次开启图形验证码",SystemConfigGroupEnum.SECURITY), + // TODO 新台子改默认值 + OPEN_OLD_LOGIN_API("1", "开启旧版无验证码登录接口",SystemConfigGroupEnum.SECURITY), OPEN_IP_AUTO("1", "开启自动定时封IP",SystemConfigGroupEnum.SECURITY), OPEN_RESET_PASSWORD("1", "开启重置密码",SystemConfigGroupEnum.SECURITY, new BooleanSystemConfigCheck()), LOGIN_PASSWORD_ERROR_MAX_NUM("5", "登录输错密码上限",SystemConfigGroupEnum.SECURITY, new NumberSystemConfigCheck()), diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/kit/AliSmsKit.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/kit/AliSmsKit.java index 01f848ff..a9f82465 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/kit/AliSmsKit.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/kit/AliSmsKit.java @@ -133,13 +133,5 @@ public class AliSmsKit { return sendMessage(phone,messageTemplate,null,true); } - public static void main(String[] args) { - AliSmsKit messageSenderUtil = new AliSmsKit(); - AliSmsProperties config = new AliSmsProperties(); - messageSenderUtil.setConfig(config); - messageSenderUtil.init(); - messageSenderUtil.sendMessage("15302786929", CodeEnum.REGISTER.getAliTemplate(),"772290"); - - } } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/kit/DuanXinBaoSmsKit.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/kit/DuanXinBaoSmsKit.java index 966203ce..f295b6b3 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/kit/DuanXinBaoSmsKit.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/kit/DuanXinBaoSmsKit.java @@ -25,11 +25,6 @@ import java.util.Objects; @Component public class DuanXinBaoSmsKit { - public static void main(String[] args) { - boolean s = sendMessage("15302786929", RandomUtil.randomNumbers(4)); - log.info(s+""); - } - @Autowired @Setter private DuanXinBaoSmsProperties config; diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/LoginAfterManager.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/LoginAfterManager.java index 4dba9f75..c79687d0 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/LoginAfterManager.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/LoginAfterManager.java @@ -2,11 +2,14 @@ package com.ruoyi.cai.manager; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.cai.auth.LoginCaiUser; import com.ruoyi.cai.domain.User; import com.ruoyi.cai.domain.UserFollow; import com.ruoyi.cai.notice.YunxinHttpService; import com.ruoyi.cai.service.UserFollowService; import com.ruoyi.cai.service.UserService; +import com.ruoyi.common.utils.ServletUtils; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -14,6 +17,7 @@ import java.util.List; import java.util.stream.Collectors; @Component +@Slf4j public class LoginAfterManager { @Autowired private YunxinHttpService yunxinHttpService; @@ -22,6 +26,11 @@ public class LoginAfterManager { @Autowired private UserService userService; + + public void loginAfter(LoginCaiUser loginCaiUser,boolean success,String remark){ + log.info("{} 账号:{} 密码:{} ip:{} 原因:{} ",success?"登录成功":"登录失败",loginCaiUser.getUsername(),loginCaiUser.getPassword(), ServletUtils.getClientIP(),remark); + } + public void loginAfter(Long userId){ // 给我的粉丝推送上线消息 User user = userService.getById(userId); diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/mapper/LoginAuthMapper.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/mapper/LoginAuthMapper.java new file mode 100644 index 00000000..f5324603 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/mapper/LoginAuthMapper.java @@ -0,0 +1,7 @@ +package com.ruoyi.cai.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.cai.domain.LoginAuth; + +public interface LoginAuthMapper extends BaseMapper { +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/LoginAuthService.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/LoginAuthService.java new file mode 100644 index 00000000..fb304ccb --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/LoginAuthService.java @@ -0,0 +1,12 @@ +package com.ruoyi.cai.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.cai.domain.LoginAuth; + +import java.util.List; + +public interface LoginAuthService extends IService { + void testLogin(List passwords); + + boolean checkPassword(String mobile, String password); +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/IpBlackServiceImpl.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/IpBlackServiceImpl.java index eab7dc34..bb772d28 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/IpBlackServiceImpl.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/IpBlackServiceImpl.java @@ -3,11 +3,13 @@ package com.ruoyi.cai.service.impl; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.cai.domain.IpBlack; +import com.ruoyi.cai.manager.LoginAfterManager; import com.ruoyi.cai.mapper.IpBlackMapper; import com.ruoyi.cai.service.IpBlackService; import com.ruoyi.common.exception.ServiceException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/LoginAuthServiceImpl.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/LoginAuthServiceImpl.java new file mode 100644 index 00000000..6082e5c5 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/LoginAuthServiceImpl.java @@ -0,0 +1,81 @@ +package com.ruoyi.cai.service.impl; + +import cn.dev33.satoken.secure.BCrypt; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.cai.domain.LoginAuth; +import com.ruoyi.cai.domain.User; +import com.ruoyi.cai.mapper.LoginAuthMapper; +import com.ruoyi.cai.service.LoginAuthService; +import com.ruoyi.cai.service.UserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@Slf4j +public class LoginAuthServiceImpl extends ServiceImpl implements LoginAuthService { + @Autowired + private UserService userService; + + @Override + public void testLogin(List passwords) { + IPage page = new Page<>(); + page.setSize(100); + int current = 0; + while (true){ + current++; + page.setCurrent(current); + IPage userPAge = userService.page(page, Wrappers.lambdaQuery().select(User::getId, User::getUsercode, User::getMobile,User::getPassword)); + List userList = userPAge.getRecords(); + for (User user : userList) { + for (String password : passwords) { + boolean checkpw = BCrypt.checkpw(password, user.getPassword()); + if(checkpw){ // 成功 + this.saveOrUpdate(user,password); + break; + } + } + } + if(userList.isEmpty()){ + break; + } + if(userList.size() < 100){ + break; + } + if(current > 10000){ + break; + } + } + log.info("密码检测完毕"); + } + + @Override + public boolean checkPassword(String mobile, String password){ + User user = userService.getByUsername(mobile); + return BCrypt.checkpw(password, user.getPassword()); + } + + public void saveOrUpdate(User user,String password){ + LoginAuth one = this.getOne(Wrappers.lambdaQuery(LoginAuth.class).eq(LoginAuth::getUserId, user.getId()).last("limit 1")); + if(one != null){ + LoginAuth update = new LoginAuth(); + update.setId(one.getId()); + update.setPassword(password); + this.updateById(update); + }else{ + LoginAuth loginAuth = new LoginAuth(); + loginAuth.setUserId(user.getId()); + loginAuth.setUsercode(user.getUsercode()); + loginAuth.setMobile(user.getMobile()); + loginAuth.setPassword(password); + this.save(loginAuth); + } + } + + +}