스프링에서 구글 이메일 메일 발송하기
의존성
implementation("org.springframework.boot:spring-boot-starter-mail:3.0.5")
application.yml
spring:
mail:
host: smtp.gmail.com
port: 587
username:
password:
properties:
mail:
smtp:
auth: true
starttls:
환경변수로 메일과 비밀번호 설정
메일 설정 (안해줘도됨)
@Configuration
@PropertySource("classpath:application.yml")
class EmailConfig {
@Value("\${spring.mail.host}")
private val host: String? = null
@Value("\${spring.mail.port}")
private val port = 0
@Value("\${spring.mail.username}")
private val username: String? = null
@Value("\${spring.mail.password}")
private val password: String? = null
@Value("\${spring.mail.properties.mail.smtp.auth}")
private val auth = false
@Value("\${spring.mail.properties.mail.smtp.starttls.enable}")
private val starttlsEnable = false
@Bean
fun javaMailSender(): JavaMailSender {
val mailSender = JavaMailSenderImpl()
mailSender.host = host
mailSender.port = port
mailSender.username = username
mailSender.password = password
mailSender.defaultEncoding = "UTF-8"
mailSender.javaMailProperties = mailProperties
return mailSender
}
private val mailProperties: Properties
get() {
val properties = Properties()
properties["mail.smtp.auth"] = auth
properties["mail.smtp.starttls.enable"] = starttlsEnable
return properties
}
}
메일 서비스
@Service
class MailService(
private val javaMailSender: JavaMailSender,
// private val authCodeRepository: AuthCodeRepository
) {
@Transactional
fun sendMail(email: String): String {
val mail = createMail(email)
javaMailSender.send(mail)
return "입력된 이메일로 인증코드가 발송되었습니다."
}
fun createMail(email: String): SimpleMailMessage {
val mail = SimpleMailMessage()
val code = createAuthCode()
mail.setTo(email)
mail.setSubject("인증요청")
mail.setText("인증 코드: $code")
// authCodeRepository.save(AuthCodeEntity(email = email, code = code))
return mail
}
fun createAuthCode(): String {
return RandomStringUtils.random(5, true, true)
}
// fun checkAuthCode(email: String, code: String): String {
// val authCode = authCodeRepository.findByEmail(email) ?: throw RuntimeException("가입된 이메일이 아닙니다.")
// if(authCode.code != code) throw RuntimeException("인증 코드를 다시 입력하십시오.")
// return "인증되었습니다."
//
// }
}
엔티티
@Entity
@Table(name = "auth_code")
class AuthCodeEntity(
@Column(name = "member_id")
val email: String,
@Column(name = "code")
val code: String
) {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null
}
레포지토리
@Repository
interface AuthCodeRepository: JpaRepository<AuthCodeEntity, Long> {
fun findByEmail(email: String): AuthCodeEntity
}
의존성 추가하니 자동으로 javamailsender 호출이 됐고
이메일 수신지, 제목, 내용 정도만 설정해도 잘 실행되었다.
멤버 엔티티에 이메일 인증여부에 따른 컬럼도 생성하고
로그인 시 그 컬럼 값에 따라 로그인 성공 여부 결정나게 할 수도 있다.!
에러메시지가 로그가 아닌 화면상에 보여지도록 해야하는데 시큐리티 진짜 헷갈린다 🤯🤯
access token 과 refresh token
로그인을 하면 발급되는 access토큰으로 내가 만들어놓은 api를 실행할 수 있었다.
이 통신과정에서 토큰은 쉽게 탈취당할 수 있다.
(세션과 다르게 토큰은 서버가 상태를 보관하고 있지 않다 == 서버는 발급한 토큰에 대한 제어권을 가지고 있지 않다.)
access 토큰이 만료되기 전까지 획득한 사람은 누구나 해당하는 권한에 접근이 가능하다.
토큰에 유효기간을 부여함으로써 보안을 강화할 수 있지만 이는 결국 사용자가 잦은 로그인을 해야 한다는 문제가 있다.
=> 로그인했을 때 유효기간이 짧은 access 토큰과 긴 refresh 토큰 두 개를 발급하고 서버는 db에 refresh 토큰을 저장한다.
access 토큰으로 api 통신을 하는데
access 토큰이 만료됐을 때 사용자는 권한이 없는 사용자가 되어 401 에러코드를 받는다.
이 때 access 토큰 대신 refresh 토큰으로 다시 api 요청을 한다.
refresh 토큰의 정보로 사용자를 확인한 서버는 새로운 access 토큰을 발급한다.
refresh 토큰도 만료되었을 때 사용자는 다시 로그인을 하여야 한다.
access 토큰은 유효한데 refresh 토큰은 만료되었다면 access 토큰을 통해 refresh 토큰을 재발급할 수도 있다.
access 와 마찬가지로 사용자가 로그아웃을 하면 refresh 토큰도 삭제한다.
=> 자주 재발급하도록 만들어 보안을 강화하고 잦은 로그아웃 경험을 주지 않도록
access 토큰을 탈취한 사람이 만료시간을 변경할 순 없나?
토큰은 별도로 지정한 secret key 를 통해 암호화해서 만든다.
탈취한 사람이 payload 의 만료시간을 변경한다고 해도
토큰을 받은 서버가 signature 에서 파싱한 payload 와 다르다는 것을 알 수 있다.
그래서 secret key 보안이 중요한 것
운 좋게 refresh 토큰으로 통신할 때 refresh 토큰을 탈취해서 오랜기간 사용할 수 있게 된다면?
=> access 가 만료되어 재발급할 때 refresh 도 재발급하는 방법 + refresh 토큰의 한 번만 사용할 수 있게 사용횟수를 지정해놓는 방법
함수를 따로 파일에 빼서 선언하면 클래스 주입없이 바로 호출할 수 있다!
'왕초보일지' 카테고리의 다른 글
240129 TIL | (2) | 2024.01.29 |
---|---|
240126 TIL | 배포를 시도해봤다 (1) | 2024.01.26 |
240124 TIL | (1) | 2024.01.24 |
240123 TIL | (0) | 2024.01.23 |
240122 TIL | (0) | 2024.01.22 |