회원가입과 비밀번호 암호화

그동안 회원가입이 없었기 때문에 data.sql 로 회원 정보를 넣고 그걸 바탕으로 로그인 로직을 수행했었다.

이제 회원가입 기능을 만들기 때문에 data.sql 은 사용하지 않으니 삭제하거나

yml 파일에서 sql.init.mode : never 로 바꿔주자.

비밀번호는 개인정보이므로 암호화해서 처리해야 한다.

 


기본 설정

spring security 라이브러리를 이용하여 암호화를 해보자.

아래처럼 의존성을 추가해준다.

build.gradle

implementation 'org.springframework.security:spring-security-crypto'
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'

 

AuthController

Signup 객체에 회원가입 정보를 받아서 저장하는 컨트롤러를 작성한다.

@PostMapping("/auth/signup")
public void signup(@RequestBody Signup signup) {
    authService.signup(signup);
}

 

Signup

회원가입 정보를 담는 객체이다. 필요하다면 여기에 검증 기능도 넣으면 좋을것이다.

@Getter
@NoArgsConstructor
public class Signup {

    private String email;
    private String name;
    private String password;

    @Builder
    public Signup(String name, String password, String email) {
        this.email = email;
        this.name = name;
        this.password = password;
    }
}

 

AuthService

하는김에 중복 체크도 하는 회원가입 로직을 만든다.

중복 체크 후 비밀번호를 암호화해서 저장한다.

@Service
@RequiredArgsConstructor
public class AuthService {

    private final UserRepository userRepository;
    private final PasswordEncoder encoder;

    @Transactional
    public void signup(Signup signup) {
        Optional<User> userOptional = userRepository.findByEmail(signup.getEmail());
        if (userOptional.isPresent()) {
            throw new AlreadyExistEmailException();
        }

        String encryptedPassword = encoder.encrypt(signup.getPassword());

        User user = User.builder()
            .email(signup.getEmail())
            .name(signup.getName())
            .password(encryptedPassword)
            .build();
        userRepository.save(user);
    }
}

 

PasswordEncoder

재사용성을 위해 PasswordEncoder 를 만들어서 여기서 암호화 관련 메서드를 처리한다.

@Component
public class PasswordEncoder {

    private static final PasswordEncoder encoder = new PasswordEncoder(
        16,
        8,
        1,
        32,
        64);

    public String encrypt(String rawPassword) {
        return encoder.encode(rawPassword);
    }

    public boolean matches(String rawPassword, String encryptedPassword) {
        return encoder.matches(rawPassword, encryptedPassword);
    }
}

 

AlreadyExistEmailException

중복 회원일 시 발생하는 커스텀 예외

public class AlreadyExistEmailException extends DunpleException {

    private static String MESSAGE = "이미 가입된 이메일입니다.";

    public AlreadyExistEmailException() {
        super(MESSAGE);
    }

    @Override
    public int getStatusCode() {
        return 400;
    }
}

 

UserRepository

중복체크를 위해 Email 로 가입된 회원이 존재하는지 확인하는 메서드를 만든다.

Optional<User> findByEmail(String Email);

 


테스트

AuthSerivceTest

AuthService 의 회원가입 성공, 실패 테스트를 작성한다.

@DisplayName("회원가입 성공")
@Test
void signup() {
    // given
    Signup signup = Signup.builder()
        .email("hyukkind@naver.com")
        .name("midcon")
        .password("1234")
        .build();

    // when
    authService.signup(signup);
    User user = userRepository.findByEmail("hyukkind@naver.com")
        .orElseThrow(() -> new InvalidSigninInformationException());

    // then
    assertEquals(1, userRepository.count());
    assertEquals("hyukkind@naver.com", user.getEmail());
    assertEquals("midcon", user.getName());
    assertTrue(encoder.matches("1234", user.getPassword()));
}

@DisplayName("중복된 이메일로 회원가입 시 예외가 발생")
@Test
void signup2() {
    // given
    User user = User.builder()
        .email("hyukkind@naver.com")
        .name("mika")
        .password("1234")
        .build();
    userRepository.save(user);

    Signup signup = Signup.builder()
        .email("hyukkind@naver.com")
        .name("midcon")
        .password("1234")
        .build();

    // expected
    assertThrows(AlreadyExistEmailException.class,
        () -> authService.signup(signup));
}

 

테스트는 성공하고 의도한대로 동작함을 알 수 있다.

 


AuthControllerTest

AuthController 의 회원가입 성공, 실패 테스트를 작성한다.

@DisplayName("회원가입 성공")
@Test
void signup() throws Exception {
    // given
    Signup signup = Signup.builder()
        .email("hyukkind@naver.com")
        .name("midcon")
        .password("1234")
        .build();

    String json = objectMapper.writeValueAsString(signup);

    // expected
    mockMvc.perform(post("/auth/signup")
            .content(json)
            .contentType(APPLICATION_JSON))
        .andExpect(status().isOk())
        .andDo(print());
}

@DisplayName("회원가입 실패")
@Test
void signup2() throws Exception {
    // given
    User user = User.builder()
        .email("hyukkind@naver.com")
        .name("midcon")
        .password(encoder.encrypt("1234"))
        .build();
    userRepository.save(user);

    Signup signup = Signup.builder()
        .email("hyukkind@naver.com")
        .name("mika")
        .password("1234")
        .build();

    String json = objectMapper.writeValueAsString(signup);

    // expected
    mockMvc.perform(post("/auth/signup")
            .content(json)
            .contentType(APPLICATION_JSON))
        .andExpect(status().isBadRequest())
        .andDo(print());
}

 

테스트는 성공하고 원하는대로 동작함을 알 수 있다.

+ Recent posts