2023.08.08.화.TIL

heeh·2023년 8월 8일

TIL

목록 보기
60/82
post-thumbnail

2023.08.08.화.TIL

  • 한 이메일로 인증 번호를 2번 이상 받으면 오류가 나는 상황
    • 새로 인증 번호를 받으면 이전 인증 번호가 삭제되는 것 구현하기
  • 로그인 url 질문이 나옴! → JwtAuthenticationFilter에 로그인 구현이 있다
    public JwtAuthenticationFilter(JwtUtil jwtUtil) {
            this.jwtUtil = jwtUtil;
            setFilterProcessesUrl("/api/login");
        }
    • 내일 URL 이야기! → api를 빼거나 다 바꾸거나…?!
  • 콘솔에서는 isApproved()가 true로 잘 바뀌어 찍히는데 DB에서 false로 찍혀서 인증된 이메일이 되지 않아 회원가입을 하지 못하는 상태….!
    • 왜 DB에 적용되지 않는지 확인하기

Email Redis

  • 원격자료구조 유용함!
  • https://keeeeeepgoing.tistory.com/15
  • 레디스 설치
  • https://dmaolon00.tistory.com/entry/Spring-이메일-인증-번호-전송-유효-시간-Gmail-SMTP-Server-Redis
  • https://velog.io/@ayoung0073/SpringBoot-이메일-인증-과정-Redis
  • application.properties
    spring.data.redis.host=localhost
    spring.data.redis.port=6379
  • MimeMessage message = mailSender.createMimeMessage(); 는 왜 Message로만 받아야 할까?
    public String joinEmail(String email) {
            makeRandomNumber(); // 이메일 인증 코드 만들기
            String toMail = email; // 누구에게 보낼지?
            String title = "회원 가입 인증 이메일 입니다."; // 이메일 제목, postman : ?email=thddltkr10@naver.com
            String content =
                    "<hybrid>를 방문해주셔서 감사합니다." +    //html 형식으로 작성
                            "<br><br>" +
                            "인증 번호는 " + verificationCode + "입니다." +
                            "<br>" +
                            " "; //이메일 내용 삽입
            sendEmail(toMail, title, content);
    
            LocalDateTime expirationDateTime = LocalDateTime.now().plusMinutes(10);
            // 만료시간 설정, now(): 지금으로부터, .plusMinures(10): 10분
    
            EmailVerification emailVerification = new EmailVerification(email, verificationCode, expirationDateTime);
            // EmailVerification는 new EmailVerification(userEmail, verificationCode, expirationDateTime); 으로 인스턴스화
    
            emailVerificationRepository.save(emailVerification);
            // 데이터베이스에 이메일 인증 정보를 저장
    
            return Integer.toString(verificationCode);
        }
    • 이걸 가져오고 싶은데 어떻게 해야할까?
    • 바로, public String joinEmail(String email)를 Message 타입으로 바꿔주고 return도 Message로 해줘야 한다!
    • 그래야 MimeMessage message = joinEmail(email);을 가져올 수 있을거야!
  • rcv : 메일 받는 사람, 내 코드로 바꾸기
    @Override
        public String sendEmail(String toMail, String title, String content) {
            MimeMessage message = emailSender.createMimeMessage();
    
            // true 매개 값을 전달하면 multipart 형식의 메세지 전달이 가능.문자 인코딩 설정도 가능
            try {
                MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
                helper.setTo(toMail);
                helper.setSubject(title);
                // true 전달 > html 형식으로 전송 , 작성하지 않으면 단순 텍스트로 전달.
                helper.setText(content, true);
                mailSender.send(message);
            } catch (MessagingException e) {
                e.printStackTrace();
            }
            return redisUtil.setDataExpire(rcv, verificationCode, 60 * 5L);
        }
  • public void sendEmail(String email)의 redisUtil.setDataExpire(email, verificationCode, 60 * 5L); 에서 verificationCode가 오류!
    • private int verificationCode; → private String verificationCode; 변경
    • public void makeRandomNumber()의 verificationCode = checkNum; 가 오류난다!
    • alt+enter로 verificationCode = String.valueOf(checkNum); 가 됐는데 오류 해결!
      → 다만 String.valueOf의 의미, 이렇게 해도 되는지! 물어보기
      - String.valueOf(checkNum)
      → String이 아닌 (checkNum)를 String으로 만들어주는(그 값으로 만들어주는 것)
  • controller postmapping : 이메일 보내기
    @PostMapping("/email")
        public String sendEmail(@RequestParam String email) throws Exception {
            String confirm = emailService.joinEmail(email);
            return confirm;
        }
  • redis 사용 방법 2가지
  • redis 사용으로 인해 이전 코드 삭제
    public void verificationCodePass(EmailVerificationRequestDto emailVerificationRequestDto) {
            // 이메일 인증을 확인
            if (!verificationCodeCheck(emailVerificationRequestDto)) {
                throw new IllegalArgumentException("이메일 인증이 필요합니다.");
            }
        }
    
        @Transactional // isApproved 값을 업데이트하고 있기 때문에 사용
        // 이메일 인증 코드를 확인
        public boolean verificationCodeCheck(EmailVerificationRequestDto emailVerificationRequestDto) {
            String userEmail = emailVerificationRequestDto.getEmail();
            int userEnteredVerificationCode = emailVerificationRequestDto.getVerificationCode();
    
            EmailVerification verification = emailVerificationRepository.findByEmail(userEmail).orElseThrow(
                    () -> new IllegalArgumentException("인증 코드를 전송하고 메일을 확인해주세요.")
            );
            // 인증 코드를 보낸 메일이 있냐, 없냐 그 데이터를 찾는 것
    
            if (verification.getVerificationCode() != userEnteredVerificationCode) {
                // 저장된 인증 코드(verification.getVerificationCode())와 사용자가 입력한 인증 코드(userEnteredVerificationCode)를 비교하여 일치하지 않으면 throw
                throw new IllegalArgumentException("올바른 인증 번호가 아닙니다.");
            }
            if (verification.getExpirationDateTime().isBefore(LocalDateTime.now())) {
                // verification.getExpirationDateTime()를 사용하여 이메일 인증 정보의 인증 코드 유효 기간(expirationDateTime)을 가져오기
                // 현재 시간(LocalDateTime.now())과 .isBefore를 사용하여 비교하고 throw
                // (verification.getExpirationDateTime()이 LocalDateTime.now()보다 .isBefore(지났다면)?
                throw new IllegalArgumentException("인증 시간이 만료되었습니다.");
            }
            verification.approve();
            System.out.println(verification.isApproved());
            // 올바른 인증 코드를 입력했을 경우 허가를 해줌 (=인증된 이메일이 됨)
    
            return true;
        }
    • signup 안에
      // 인증된 email 찾아서 확인
              EmailVerification checkUserEmailApprove = emailVerificationRepository.findByEmail(email).orElseThrow(
                      () -> new IllegalArgumentException("Email을 인증해주세요.")
              );
              if (!checkUserEmailApprove.isApproved()) {
                  throw new IllegalArgumentException("인증되지 않은 Email입니다.");
              } // throw new Exception -> 오류 반환 메세지
  • 전 controller - @GetMapping
    @GetMapping("/email") //URL 대문자가 들어가면 안됩니다
        public ResponseEntity<MsgResponseDto> emailCheck(@RequestBody EmailVerificationRequestDto emailVerificationRequestDto) {
            userService.verificationCodePass(emailVerificationRequestDto);
            return ResponseEntity.ok().body(new MsgResponseDto("이메일 인증 성공", HttpStatus.OK.value()));
        }
  • 전 controller - @PostMapping
    @PostMapping("/email-send")
        public String sendEmail(@RequestParam String email) throws Exception {
            String confirm = emailService.joinEmail(email);
            return confirm;
        }
  • 전 EmailServiceImpl - makeRandomNumber
    public static String makeRandomCode() {
            // 난수의 범위 111111 ~ 999999 (6자리 난수)
            Random r = new Random();
            int checkNum = r.nextInt(888888) + 111111;
            System.out.println("인증번호 : " + checkNum);
            verificationCode = String.valueOf(checkNum); // checkNum이 authNumber에 담김
            return r.toString();
        }
  • redis cli 명령어
  • key(인증 코드) 값이 redis에 왜 저장이 안돼 ㅠㅠ
    • 참고
    • PostMan에 email이…… userEmail이었어…………………………... Request 하려고 하는 통로가 없어서 key 값을 받아오지 못하고 있었던 거야!
    • key : 이메일, value : 인증 코드
      • 순서 어디서 정해진 것인지 찾아보기
  • public class EmailServiceImpl implements EmailService 의 public void sendEmail(String email) throws Exception 의 if문에서 왜 삭제를?!
    if (redisUtil.existData(email)) {
                redisUtil.deleteData(email);
            }
    • 일시적으로 저장을 위한 인메모리 사용! → redis
    • MySQL은 table을 삭제하면 다시 초기화 되는데, table이 없는 redis는 어떻게 초기화 시켜서 다시 실행할 수 있나??
      • 일시적 저장하는 인메모리이기 때문에 런을 다시 돌리면 초기화 되는 듯!
  • @ResponseBody
    • 서버에서 클라이언트로 응답 데이터를 전송해야 함
      • User → 클라이언트 반환
      • User (java)
      • User (http)
    • 이 어노테이션을 사용하면 반환하고자 하는 자바 객체를 http 본문의 객체로 변환해서 클라이언트에 보내준다
    • 프론트엔드와 관련

Username???

  • public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter - protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
    • String username = ((UserDetailsImpl) authResult.getPrincipal()).getUsername();

      String username = ((UserDetailsImpl) authResult.getPrincipal()).getUsername();
      • 난 Username 라는 것을 써 준 적이 없는데???
    • ctrl + 클릭해서 들어가 보니 public class UserDetailsImpl implements UserDetails - public String getUsername()

      @Override
          public String getUsername() {
              return user.getUserName();
          }
      • public String getUsername() 이름이 맘에 안 들어서 바꿔줌 → UserNames()
    • 빨간 줄이 떠 있어야 하는 public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter - protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) 의 String username = ((UserDetailsImpl) authResult.getPrincipal()).getUsername(); 의 getUsername(); 가 정상 작동?!

    • ctrl + 클릭해서 들어가 보니 public interface UserDetails extends Serializable

      /**
      	 * Returns the username used to authenticate the user. Cannot return
      	 * <code>null</code>.
      	 * @return the username (never <code>null</code>)
      	 */
      	String getUsername();
    • public class UserDetailsImpl implements UserDetails 에 빨간 줄이 떠 있고 public String UserNames()은 꺼져 있고 @Override에 빨간 줄, no usages!

    • public String UserNames()가 없어도

      public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter - protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) 의 String username = ((UserDetailsImpl) authResult.getPrincipal()).getUsername(); 의

      getUsername(); 가 정상 작동되니까 그럼 필요 없는 거 아니야?

    • 삭제하면 public class UserDetailsImpl implements UserDetails 에 빨간 줄

    • 결론

      • UserDetails 를 implements 해주기 때문에 public interface UserDetails extends Serializable 에서 필요하고 요구하는 것들은 똑같이 써주어야 함
profile
공부하자개발하자으쌰으쌰

0개의 댓글