Spring Security와 Attribute Converter

조갱·2022년 2월 26일
0

이슈 해결

목록 보기
2/14
post-thumbnail

(부제 : 비밀번호가 계속 바뀜)

0. 배경

비밀번호 초기화를 위한 인증 메일을 보내면, 비밀번호가 임의의 값으로 바뀌어버린다. (?) 원래는 잘됐었는데,,,,,,,

최근에는 DB에서 UserId를 암호화하여 저장하게 됐는데, 이때까지만 해도 Attribute Converter를 목적에 맞게 잘 사용하고 있었다. 그러나, Password까지 Attribute Converter를 사용하게 된 것이 문제의 화근이었다. (기존에는 Password는 회원가입/비밀번호 재설정 할때마다 따로 암호화하여 저장해서 사용하고 있었다.)

Spring Security 에 대한 간략한 정보는 (이곳을 클릭)하여 알 수 있다.
PasswordEncoder 에 대한 간략한 정보는 (이곳을 클릭)하여 알 수 있다.
Attribute Converter 에 대한 간략한 정보는 (이곳을 클릭)하여 알 수 있다.

1. 패스워드 초기화 방식

이 방식은 실무에서 쓰이는 방식은 아니고, 신입 프로젝트에서 진행한 서비스의 동작 방식이다.
빨간색으로 색칠된 부분에서 비밀번호가 재설정되었다.

2. 문제 상황

0. 배경에서도 소개했다 시피, 유저가 비밀번호 초기화를 시도만 해도 비밀번호가 임의의 값으로 바뀌는 현상이다. 자세한 설명을 위해 아래 코드와 사진을 함께 첨부한다.

package 패키지명

import 패키지명.PasswordConverter
import javax.persistence.*

@Entity
@Table(name = "USER")
// 회원관리 테이블
data class UserEntity(
    // 다른 속성들 더 있음...

    @Column(name = "user_password", length = 100)
    @Convert(converter = PasswordConverter::class)
    var userPassword: String = "",

    @Column(name = "user_token", length = 30)
    // 비밀번호 재설정 할때 사용할 토큰
    var userToken: String? = null    
) : BaseTimeEntity()

우선 기본적인 UserEntity의 구성은 위와 같다. Password에 Converter가 적용되어 있는 모습.

// 사용자에게 새로운 토큰 발급
@Throws(UsernameNotFoundException::class)
@Transactional
override fun updateToken(token: String, email: String) {
	val entity = repository.findByUserEmail(email)
		?: throw UsernameNotFoundException("invalid email.")
	entity.userToken = token
}

위 코드는 패스워드 초기화를 위한 Token을 할당하는 부분이다.
service.updateToken()이 실행되기 전 모습이다.
user_passworduser_token을 먼저 확인하자.
아래 사진은 updateToken()이 실행된 후의 모습이다.
user_passworduser_token이 변경된 것을 볼 수 있다.
위에 코드에서 봤듯, updateToken()에서는 Password를 수정하는 부분이 없는데.

3. 문제의 원인

문제의 원인은 AttributeConverterSpringSecurity 에 모두 있었다.

  1. AttributeConverter는 @Converter 어노테이션을 붙이지 않은 데이터'만' 수정할 때에도 동작한다.
  2. BCryptPasswordEncoder는 매번 다른 비밀번호가 생성된다.
    (동일한 비밀번호를 암호화 하더라도, 매번 다른 비밀번호가 만들어진다.)

이 두개가 맞물려 동작하면서, UserEntity에서 다른 정보를 수정할 때 Password가 변경되던 것이다.

4. 해결 방안

  1. UserEntity에서 Password에 @Converter를 제거했다.
  2. 비밀번호 암호화는 회원가입/비밀번호 재설정 할 때에만 개별적으로 적용했다.

5. 배운점

  1. Entity에 값을 쓸 때에는 Hibernate에서 row 자체를 update한다는 사실을 알게 됐다.
    (이 때문에 db에 값을 쓰기 위해 Password의 Converter가 작동하면서 PasswordEncoder가 작동했던 것.)
  2. Spring Security의 PasswordEncoder는 동일한 문자열일지라도, 암호화할 때마다 매번 다른 비밀번호를 생성한다는 것을 알게되었다.
  3. 이번 포스팅을 쓰며 부가적인 내용을 설명해야 했기 때문에, 자료조사를 하며 더 깊게 했고, 동작원리를 알게 되었다.
profile
A fast learner.

0개의 댓글