|
|
@@ -0,0 +1,131 @@
|
|
|
+package com.njuzr.eaibackend.service.impl;
|
|
|
+
|
|
|
+import com.njuzr.eaibackend.controller.MyResponse;
|
|
|
+import com.njuzr.eaibackend.dto.auth.EmailLoginDTO;
|
|
|
+import com.njuzr.eaibackend.dto.auth.UserLoginDTO;
|
|
|
+import com.njuzr.eaibackend.exception.MyException;
|
|
|
+import com.njuzr.eaibackend.mapper.UserMapper;
|
|
|
+import com.njuzr.eaibackend.po.MyUserDetails;
|
|
|
+import com.njuzr.eaibackend.po.User;
|
|
|
+import com.njuzr.eaibackend.service.AuthenticationService;
|
|
|
+import com.njuzr.eaibackend.service.EmailService;
|
|
|
+import com.njuzr.eaibackend.utils.JWTTokenUtil;
|
|
|
+import com.njuzr.eaibackend.utils.ModelMapperUtil;
|
|
|
+import com.njuzr.eaibackend.vo.auth.UserLoginVO;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
+import org.springframework.data.redis.core.ValueOperations;
|
|
|
+import org.springframework.http.HttpStatus;
|
|
|
+import org.springframework.security.authentication.AuthenticationManager;
|
|
|
+import org.springframework.security.authentication.BadCredentialsException;
|
|
|
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
|
+import org.springframework.security.core.Authentication;
|
|
|
+import org.springframework.security.core.context.SecurityContextHolder;
|
|
|
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.util.UUID;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class AuthenticationServiceImpl implements AuthenticationService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AuthenticationManager authenticationManager;
|
|
|
+ @Autowired
|
|
|
+ private JWTTokenUtil jwtTokenUtil;
|
|
|
+ @Autowired
|
|
|
+ private EmailService emailService;
|
|
|
+ @Autowired
|
|
|
+ private UserMapper userMapper;
|
|
|
+ @Autowired
|
|
|
+ private RedisTemplate<String, Object> redisTemplate;
|
|
|
+
|
|
|
+ private static final long SEND_INTERVAL = 60; // 允许再次发送的时间间隔,单位秒
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MyResponse login(UserLoginDTO userLoginDTO) {
|
|
|
+ try {
|
|
|
+ Authentication authentication = authenticationManager.authenticate(
|
|
|
+ new UsernamePasswordAuthenticationToken(
|
|
|
+ userLoginDTO.getOfficialNumber(),
|
|
|
+ userLoginDTO.getPassword()
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+ SecurityContextHolder.getContext().setAuthentication(authentication);
|
|
|
+ MyUserDetails userDetails = (MyUserDetails) authentication.getPrincipal();
|
|
|
+
|
|
|
+ log.info("用户认证通过,用户认证信息如下:" + userDetails.toString());
|
|
|
+
|
|
|
+ String jwt = jwtTokenUtil.generateToken(userDetails.getOfficialNumber());
|
|
|
+ log.info("生成jwt成功,token信息:" + jwt);
|
|
|
+
|
|
|
+ UserLoginVO userLoginVO = ModelMapperUtil.map(userDetails, UserLoginVO.class);
|
|
|
+ userLoginVO.setToken(jwt);
|
|
|
+ return MyResponse.success(userLoginVO);
|
|
|
+
|
|
|
+ } catch (BadCredentialsException e) {
|
|
|
+ log.error("登陆失败,密码错误");
|
|
|
+ return MyResponse.error(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase()+":"+"用户名或密码错误");
|
|
|
+ } catch (UsernameNotFoundException e) {
|
|
|
+ log.error("登陆失败,找不到该用户");
|
|
|
+ return MyResponse.error(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase()+":"+"用户名或密码错误");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MyResponse loginByEmail(EmailLoginDTO emailLoginDTO) {
|
|
|
+ // 从Redis中获取验证码
|
|
|
+ final String key = "loginVerifyCode:" + emailLoginDTO.getOfficialEmail();
|
|
|
+ String storedCode = (String) redisTemplate.opsForValue().get(key);
|
|
|
+ if (storedCode == null) {
|
|
|
+ throw new MyException(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase() + ":" + "验证码已过期");
|
|
|
+ }
|
|
|
+ // 验证码校验
|
|
|
+ if(!storedCode.equals(emailLoginDTO.getVerifyCode())) {
|
|
|
+ throw new MyException(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase() + ":" + "验证码错误");
|
|
|
+ }
|
|
|
+
|
|
|
+ User user = userMapper.selectByOfficialEmail(emailLoginDTO.getOfficialEmail());
|
|
|
+ if(user == null) {
|
|
|
+ log.error("登陆失败,找不到该用户");
|
|
|
+ return MyResponse.error(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase()+":"+"邮箱未注册");
|
|
|
+ }
|
|
|
+ String jwt = jwtTokenUtil.generateToken(user.getOfficialNumber());
|
|
|
+ log.info("生成jwt成功,token信息:" + jwt);
|
|
|
+ UserLoginVO userLoginVO = ModelMapperUtil.map(user, UserLoginVO.class);
|
|
|
+ userLoginVO.setToken(jwt);
|
|
|
+ return MyResponse.success(userLoginVO);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void sendLoginVerifyCode(String officialEmail) {
|
|
|
+ // 检验邮箱合法性,只允许南京大学邮箱
|
|
|
+ if (!officialEmail.endsWith("@smail.nju.edu.cn") && !officialEmail.endsWith("@nju.edu.cn")) {
|
|
|
+ throw new MyException(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase() + ":" + "只允许南京大学邮箱,请检查邮箱格式");
|
|
|
+ }
|
|
|
+
|
|
|
+ ValueOperations<String, Object> ops = redisTemplate.opsForValue();
|
|
|
+ String intervalKey = "requestInterval:" + officialEmail;
|
|
|
+
|
|
|
+ // 检查是否已经发送过验证码并且时间间隔未过
|
|
|
+ if (ops.get(intervalKey) != null) {
|
|
|
+ throw new MyException(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase() + ":" + "需等待" + SEND_INTERVAL + "秒才能再次发送验证码");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成验证码
|
|
|
+ String code = UUID.randomUUID().toString().substring(0, 6);
|
|
|
+ // 存储验证码到Redis,设置5分钟过期
|
|
|
+ int timeout = 5;
|
|
|
+ final String key = "loginVerifyCode:" + officialEmail;
|
|
|
+ // 存储验证码到Redis并设置过期时间
|
|
|
+ redisTemplate.opsForValue().set(key, code, timeout, TimeUnit.MINUTES);
|
|
|
+ // 设置请求间隔标记,防止频繁请求
|
|
|
+ redisTemplate.opsForValue().set(intervalKey, "requested", SEND_INTERVAL, TimeUnit.SECONDS);
|
|
|
+ emailService.sendVerifyCodeEmail(officialEmail, code, timeout);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|