diff --git a/unilife-server/pom.xml b/unilife-server/pom.xml index 99ffc1d..254efa2 100644 --- a/unilife-server/pom.xml +++ b/unilife-server/pom.xml @@ -49,6 +49,12 @@ hutool-all 5.8.16 + + + + org.springframework.boot + spring-boot-starter-data-redis + org.springframework.boot diff --git a/unilife-server/src/main/java/com/unilife/common/constant/RedisConstant.java b/unilife-server/src/main/java/com/unilife/common/constant/RedisConstant.java new file mode 100644 index 0000000..6118e4c --- /dev/null +++ b/unilife-server/src/main/java/com/unilife/common/constant/RedisConstant.java @@ -0,0 +1,7 @@ +package com.unilife.common.constant; + + +public class RedisConstant { + public static final String LOGIN_EMAIL_KEY="login:email:"; + public static final String LOGIN_EMAIL_LIMIT_KEY = "login:email:limit:"; +} diff --git a/unilife-server/src/main/java/com/unilife/controller/UserController.java b/unilife-server/src/main/java/com/unilife/controller/UserController.java index 8ea9ad6..0a28a1e 100644 --- a/unilife-server/src/main/java/com/unilife/controller/UserController.java +++ b/unilife-server/src/main/java/com/unilife/controller/UserController.java @@ -4,6 +4,7 @@ import com.unilife.common.result.Result; import com.unilife.model.dto.EmailDTO; import com.unilife.model.dto.LogDTO; import com.unilife.model.dto.LoginDTO; +import com.unilife.model.dto.LoginEmailDTO; import com.unilife.service.UserService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -42,5 +43,10 @@ public class UserController { return userService.sendVerificationCode(email); } + @ApiOperation(value = "邮箱验证码登录") + @PostMapping("login/code") + public Result loginWithEmailCode(@RequestBody LoginEmailDTO loginEmailDTO){ + return userService.loginWithEmail(loginEmailDTO); + } } diff --git a/unilife-server/src/main/java/com/unilife/mapper/UserMapper.java b/unilife-server/src/main/java/com/unilife/mapper/UserMapper.java index 4e4d997..9520351 100644 --- a/unilife-server/src/main/java/com/unilife/mapper/UserMapper.java +++ b/unilife-server/src/main/java/com/unilife/mapper/UserMapper.java @@ -8,4 +8,5 @@ import org.apache.ibatis.annotations.Param; public interface UserMapper { void insert(User user); User FindByEmail(@Param("email") String email, @Param("password") String password); + User getUserByEmail(String email); } diff --git a/unilife-server/src/main/java/com/unilife/model/dto/LoginEmailDTO.java b/unilife-server/src/main/java/com/unilife/model/dto/LoginEmailDTO.java new file mode 100644 index 0000000..cd53e67 --- /dev/null +++ b/unilife-server/src/main/java/com/unilife/model/dto/LoginEmailDTO.java @@ -0,0 +1,13 @@ +package com.unilife.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class LoginEmailDTO { + private String email; + private String code; +} diff --git a/unilife-server/src/main/java/com/unilife/service/UserService.java b/unilife-server/src/main/java/com/unilife/service/UserService.java index 2e693d7..fece724 100644 --- a/unilife-server/src/main/java/com/unilife/service/UserService.java +++ b/unilife-server/src/main/java/com/unilife/service/UserService.java @@ -3,7 +3,7 @@ package com.unilife.service; import com.unilife.common.result.Result; import com.unilife.model.dto.LogDTO; import com.unilife.model.dto.LoginDTO; -import com.unilife.model.entity.User; +import com.unilife.model.dto.LoginEmailDTO; public interface UserService { @@ -11,4 +11,8 @@ public interface UserService { Result login(LogDTO logDTO); Result sendVerificationCode(String email); + + Result loginWithEmail(LoginEmailDTO loginEmailDTO); + + } diff --git a/unilife-server/src/main/java/com/unilife/service/impl/UserServiceImpl.java b/unilife-server/src/main/java/com/unilife/service/impl/UserServiceImpl.java index 2d40a17..29ca4ad 100644 --- a/unilife-server/src/main/java/com/unilife/service/impl/UserServiceImpl.java +++ b/unilife-server/src/main/java/com/unilife/service/impl/UserServiceImpl.java @@ -2,10 +2,13 @@ package com.unilife.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import com.unilife.common.constant.RedisConstant; import com.unilife.common.result.Result; import com.unilife.mapper.UserMapper; import com.unilife.model.dto.LogDTO; import com.unilife.model.dto.LoginDTO; +import com.unilife.model.dto.LoginEmailDTO; import com.unilife.model.entity.User; import com.unilife.model.vo.LogVO; import com.unilife.model.vo.LoginVO; @@ -14,6 +17,7 @@ import com.unilife.utils.RegexUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Component; @@ -22,6 +26,13 @@ import org.springframework.stereotype.Service; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +import static com.unilife.common.constant.RedisConstant.LOGIN_EMAIL_KEY; + @Slf4j @Component @Service @@ -33,9 +44,15 @@ public class UserServiceImpl implements UserService { @Autowired private JavaMailSender mailSender; + @Autowired + private StringRedisTemplate stringRedisTemplate; + @Value("${spring.mail.username}") private String from; + final int CODE_EXPIRE_MINUTES = 10; + final int LIMIT_SECONDS=60; + @Override @@ -76,14 +93,25 @@ public class UserServiceImpl implements UserService { return Result.error(400,"邮箱格式不正确"); } - //2.生成随机验证码 - String code = RandomUtil.randomNumbers(6); - log.debug("成功生成验证码,邮箱{},验证码{}", email, code); + //2.防止频繁发送验证码 + String countKey = RedisConstant.LOGIN_EMAIL_LIMIT_KEY + email; + Boolean setSuccess = stringRedisTemplate.opsForValue().setIfAbsent( + countKey, + "1", + Duration.ofSeconds(LIMIT_SECONDS) + ); + + if (Boolean.FALSE.equals(setSuccess)) { + return Result.error(null, "请求过于频繁,请稍后再试"); + } + //3.生成随机验证码 + String code = RandomUtil.randomNumbers(6); + log.debug("成功生成验证码,邮箱{},验证码{}", email, code); - //3.发送验证码到邮箱 + //4.发送验证码到邮箱 try { //构建邮件 MimeMessage message=mailSender.createMimeMessage(); @@ -115,12 +143,78 @@ public class UserServiceImpl implements UserService { } - //4.存储随机产生的验证码 + //5.存储随机产生的验证码,设置有效期为十分钟 + stringRedisTemplate.opsForValue().set(LOGIN_EMAIL_KEY + email, code, Duration.ofMinutes(CODE_EXPIRE_MINUTES)); + + return Result.success(200,"验证码已发送"); + } + + @Override + public Result loginWithEmail(LoginEmailDTO loginEmailDTO) { + String email=loginEmailDTO.getEmail(); + + if(RegexUtils.isEmailInvalid(email)){ + return Result.error(null,"请输入正确的邮箱"); + } + + String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstant.LOGIN_EMAIL_KEY + email); + if (cacheCode == null) { + return Result.error(null, "验证码已过期或未发送,请重新获取"); + } + + // 3. 校验验证码是否正确 + String code = loginEmailDTO.getCode(); + if (!cacheCode.equals(code)) { + return Result.error(null, "验证码错误"); + } + + // 4. 验证通过,删除验证码 + stringRedisTemplate.delete(RedisConstant.LOGIN_EMAIL_KEY + email); + + // 5. 查询用户是否存在 + User user=userMapper.getUserByEmail(email); + if(user == null){ + user=createUserWithEmail(email); + } + + //6.生成登录凭证 //TODO + // 8. 返回用户信息和登录凭证 + Map userInfo = new HashMap<>(); + //HashMap userInfo.put("token", token); + userInfo.put("user", user); - return Result.success(200,"验证码已发送"); + return Result.success(userInfo); + } + + /** + * 使用邮箱信息创建新用户 + */ + private User createUserWithEmail(String email) { + User user = new User(); + user.setEmail(email); + user.setNickname("用户" + RandomUtil.randomString(6)); // 生成随机昵称 + String username = email.split("@")[0]+"_"+ RandomUtil.randomString(4); // 使用@前面的部分作为用户名 + user.setUsername(username); + + String password=RandomUtil.randomString(6); + user.setPassword(password); + user.setRole((byte)0); // 普通用户角色 + user.setStatus((byte)1); // 正常状态 + user.setIsVerified((byte)0); // 未验证 + user.setPoints(0); // 初始积分 + user.setGender((byte)0); + // 保存用户 + try { + userMapper.insert(user); + }catch (Exception e){ + log.error("用户创建失败"); + } + + return user; } + } diff --git a/unilife-server/src/main/resources/application.yml b/unilife-server/src/main/resources/application.yml index 17b36cb..3986424 100644 --- a/unilife-server/src/main/resources/application.yml +++ b/unilife-server/src/main/resources/application.yml @@ -20,6 +20,9 @@ spring: socketFactory: port: 465 class: javax.net.ssl.SSLSocketFactory + redis: + host: 127.0.0.1 + port: 6379 knife4j: enable: true openapi: @@ -44,4 +47,4 @@ mybatis: map-underscore-to-camel-case: true logging: level: - com.unilife: debug + com.unilife: debug \ No newline at end of file diff --git a/unilife-server/src/main/resources/mappers/UserMapper.xml b/unilife-server/src/main/resources/mappers/UserMapper.xml index 20a789b..e422367 100644 --- a/unilife-server/src/main/resources/mappers/UserMapper.xml +++ b/unilife-server/src/main/resources/mappers/UserMapper.xml @@ -14,23 +14,7 @@ INSERT INTO users ( - username, - email, - password, - nickname, - avatar, - bio, - gender, - student_id, - department, - major, - grade, - points, - role, - status, - is_verified, - login_ip, - login_time + username, email, password,nickname,avatar,bio, gender,student_id,department, major,grade,points,role,status,is_verified,login_ip,login_time ) VALUES ( #{username}, #{email}, @@ -57,4 +41,25 @@ FROM users WHERE email = #{email} AND password = #{password} + + \ No newline at end of file