feat: email 비밀번호 찾기 이메일 인증 추가
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 8.4 KiB |
@ -37,7 +37,7 @@ dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-mail'
|
||||
implementation 'org.mindrot:jbcrypt:0.4'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
|
||||
|
||||
|
||||
|
||||
|
@ -2,6 +2,7 @@ package com.edufocus.edufocus.global.config;
|
||||
|
||||
|
||||
import com.edufocus.edufocus.global.properties.ImagePathProperties;
|
||||
import com.edufocus.edufocus.global.properties.MailProperties;
|
||||
import com.edufocus.edufocus.global.properties.RabbitMQProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -9,7 +10,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({
|
||||
RabbitMQProperties.class,
|
||||
ImagePathProperties.class
|
||||
ImagePathProperties.class,
|
||||
MailProperties.class
|
||||
})
|
||||
public class PropertiesConfig {
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package com.edufocus.edufocus.global.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
@Value("${spring.data.redis.host}")
|
||||
private String host;
|
||||
|
||||
@Value("${spring.data.redis.port}")
|
||||
private int port;
|
||||
|
||||
@Bean
|
||||
public RedisConnectionFactory redisConnectionFactory() {
|
||||
return new LettuceConnectionFactory(host, port);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.edufocus.edufocus.global.properties;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@ConfigurationProperties(prefix = "spring.mail")
|
||||
public class MailProperties {
|
||||
private final String host;
|
||||
private final Integer port;
|
||||
private final String name;
|
||||
private final String password;
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.edufocus.edufocus.mail.controller;
|
||||
|
||||
import com.edufocus.edufocus.mail.service.MailService;
|
||||
import com.edufocus.edufocus.user.model.service.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/mail")
|
||||
@RequiredArgsConstructor
|
||||
public class MailController {
|
||||
|
||||
private final MailService mailService;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
@PostMapping("/sendCode")
|
||||
public ResponseEntity<?> sendMail(@RequestParam String email) {
|
||||
if (!userService.isEmailExist(email)) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
mailService.sendMail(email);
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/verify")
|
||||
public ResponseEntity<?> verifyCode(@RequestParam String code, @RequestParam String email) {
|
||||
if (!mailService.verifyCode(code, email)) {
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.edufocus.edufocus.mail.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public interface MailService {
|
||||
|
||||
void sendMail(String to);
|
||||
|
||||
String createRandomCode();
|
||||
|
||||
boolean verifyCode(String code, String email);
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.edufocus.edufocus.mail.service;
|
||||
|
||||
import com.edufocus.edufocus.redis.util.RedisUtil;
|
||||
import com.edufocus.edufocus.user.model.entity.vo.User;
|
||||
import com.edufocus.edufocus.user.model.repository.UserRepository;
|
||||
import com.edufocus.edufocus.user.model.service.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Random;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public class MailServiceImpl implements MailService {
|
||||
|
||||
private final JavaMailSender mailSender;
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
@Override
|
||||
public void sendMail(String email) {
|
||||
String code = createRandomCode();
|
||||
redisUtil.setDataExpire(code, email, 60 * 5L);
|
||||
|
||||
SimpleMailMessage mail = createEmail(email, "[EDUFOCUS] 비밀번호 찾기 안내", code);
|
||||
mailSender.send(mail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyCode(String code, String email) {
|
||||
String registedEmail = redisUtil.getData(code);
|
||||
|
||||
return registedEmail != null && registedEmail.equals(email);
|
||||
}
|
||||
|
||||
private SimpleMailMessage createEmail(String to, String title, String code) {
|
||||
SimpleMailMessage message = new SimpleMailMessage();
|
||||
message.setTo(to);
|
||||
message.setSubject(title);
|
||||
message.setText("인증번호 6자리입니다 : " + code);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createRandomCode() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
sb.append((char) (random.nextInt(26) + 65));
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
sb.append(random.nextInt(10));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.edufocus.edufocus.redis.util;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class RedisUtil {
|
||||
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
public String getData(String key) {
|
||||
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
|
||||
return valueOperations.get(key);
|
||||
}
|
||||
|
||||
public void setData(String key, String value) {
|
||||
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
|
||||
valueOperations.set(key, value);
|
||||
}
|
||||
|
||||
public void setDataExpire(String key, String value, long duration) {
|
||||
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
|
||||
Duration expireDuration = Duration.ofSeconds(duration);
|
||||
valueOperations.set(key, value, expireDuration);
|
||||
}
|
||||
|
||||
public void deleteData(String key) {
|
||||
stringRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
}
|
@ -47,6 +47,6 @@ public class WebConfiguration implements WebMvcConfigurer {
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(jwtInterceptor)
|
||||
.addPathPatterns("/**") // 모든 경로에 대해 인터셉터 적용
|
||||
.excludePathPatterns("/v3/api-docs/**", "/swagger-resources/**", "/webjars/**", "/swagger-ui/**", "/auth/**", "/board/**", "/user/**", "/lecture/**", "/qna/**", "/quiz/**", "/video/**", "/registration/**", "/report/**"); // 인증 없이 접근 가능한 경로 설정
|
||||
.excludePathPatterns("/v3/api-docs/**", "/swagger-resources/**", "/webjars/**", "/swagger-ui/**", "/auth/**", "/board/**", "/user/**", "/lecture/**", "/qna/**", "/quiz/**", "/video/**", "/registration/**", "/report/**", "/mail/**"); // 인증 없이 접근 가능한 경로 설정
|
||||
}
|
||||
}
|
@ -66,6 +66,14 @@ public class UserController {
|
||||
}
|
||||
}
|
||||
|
||||
// 비밀번호 찾기를 통한 변경
|
||||
@PutMapping("/updateforgottenpassword")
|
||||
public ResponseEntity<String> updatePassword(@RequestParam long userId,
|
||||
@RequestParam String newPassword) {
|
||||
userService.changeForgottenPassword(userId, newPassword);
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Operation(summary = "로그인", description = "아이디와 비밀번호를 이용하여 로그인 처리.")
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<Map<String, Object>> login(
|
||||
|
@ -31,4 +31,6 @@ public interface UserRepository extends JpaRepository<User,Long> {
|
||||
|
||||
Optional<User> findByUserId(String userId);
|
||||
|
||||
Optional<User> findByEmail(String email);
|
||||
|
||||
}
|
||||
|
@ -25,4 +25,8 @@ public interface UserService {
|
||||
void changePassword(PasswordDto passwordDto, Long id);
|
||||
|
||||
boolean isUserIdExist(String userId);
|
||||
|
||||
boolean isEmailExist(String email);
|
||||
|
||||
void changeForgottenPassword(Long id, String newPassword);
|
||||
}
|
||||
|
@ -3,11 +3,11 @@ package com.edufocus.edufocus.user.model.service;
|
||||
|
||||
import com.edufocus.edufocus.user.model.entity.dto.InfoDto;
|
||||
import com.edufocus.edufocus.user.model.entity.dto.PasswordDto;
|
||||
import com.edufocus.edufocus.user.util.PasswordUtils;
|
||||
import com.edufocus.edufocus.user.model.entity.dto.RequestJoinDto;
|
||||
import com.edufocus.edufocus.user.model.entity.vo.User;
|
||||
import com.edufocus.edufocus.user.model.exception.UserException;
|
||||
import com.edufocus.edufocus.user.model.repository.UserRepository;
|
||||
import com.edufocus.edufocus.user.util.PasswordUtils;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -23,8 +23,7 @@ public class UserServiceImpl implements UserService {
|
||||
private final UserRepository userRepository;
|
||||
|
||||
|
||||
public void join(RequestJoinDto requestJoinDto)
|
||||
{
|
||||
public void join(RequestJoinDto requestJoinDto) {
|
||||
User user = User.builder()
|
||||
.userId(requestJoinDto.getUserId())
|
||||
.email(requestJoinDto.getEmail())
|
||||
@ -63,9 +62,6 @@ public class UserServiceImpl implements UserService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getUserName(Long id) {
|
||||
return userRepository.findById(id).get().getName();
|
||||
@ -125,6 +121,7 @@ public class UserServiceImpl implements UserService {
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveRefreshToken(Long id, String refreshToken) {
|
||||
userRepository.saveRefreshToken(id, refreshToken);
|
||||
@ -140,4 +137,25 @@ public class UserServiceImpl implements UserService {
|
||||
userRepository.deleteRefreshToken(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmailExist(String email) {
|
||||
Optional<User> user = userRepository.findByEmail(email);
|
||||
|
||||
return user.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeForgottenPassword(Long id, String newPassword) {
|
||||
User user = userRepository.findById(id).orElse(null);
|
||||
|
||||
if (user == null) {
|
||||
throw new UserException("User not found");
|
||||
}
|
||||
|
||||
// Hash the new password before saving
|
||||
user.setPassword(PasswordUtils.hashPassword(newPassword));
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -30,3 +30,12 @@ spring.rabbitmq.port=${RABBITMQ_PORT}
|
||||
spring.rabbitmq.username=${RABBITMQ_USERNAME}
|
||||
spring.rabbitmq.password=${RABBITMQ_PASSWORD}
|
||||
image.path=${IMAGE_PATH}
|
||||
spring.mail.host=smtp.gmail.com
|
||||
spring.mail.port=${MAIL_PORT}
|
||||
spring.mail.username=${MAIL_NAME}
|
||||
spring.mail.password=${MAIL_PASSWORD}
|
||||
spring.mail.properties.mail.smtp.auth=true
|
||||
spring.mail.properties.mail.smtp.timeout=5000
|
||||
spring.mail.properties.mail.smtp.starttls.enable=true
|
||||
spring.data.redis.host=${REDIS_HOST}
|
||||
spring.data.redis.port=${REDIS_PORT}
|