AccessToken을 이용한 인증@RestController@RequiredArgsConstructorpublic class SessionController { private final UserService userService; @PostMapping("/session") public ResponseEntity<SessionResponseDto> create(@RequestBody SessionRequestDto resource){ String email = resource.getEmail(); String password = resource.getPassword(); User user = userService.authenticate(email, password); String url = "/session"; String accessToken = user.getAccessToken(); SessionResponseDto sessionResponseDto = SessionResponseDto.builder() .accessToken(accessToken) .build(); return ResponseEntity.created(URI.create(url)).body(sessionResponseDto); }}
@WebMvcTest(SessionController.class)class SessionControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test @DisplayName("AccessToken을 반환하는지 확인한다.") public void create() throws Exception { String email = "tester@example.com"; String password = "test"; User mockUser = User.builder().password("ACCESSTOKEN").build(); given(userService.authenticate(email, password)).willReturn(mockUser); ResultActions resultActions = mockMvc.perform(post("/session") .contentType(MediaType.APPLICATION_JSON) .content("{\"email\":\"tester@example.com\",\"password\":\"test\"}")); resultActions .andExpect(status().isCreated()) .andExpect(header().string("location", "/session")) // AccessToken을 사용하는지 확인한다. .andExpect(content().string("{\"accessToken\":\"ACCESSTOKE\"}")); verify(userService).authenticate(eq(email), eq(password)); } @Test @DisplayName("올바르지 않은 이메일을 이용한 요청을 시도") public void createWithNotExistedEmail() throws Exception { String email = "x@example.com"; String password = "test"; given(userService.authenticate(email, password)).willThrow(EmailNotExistedException.class); ResultActions resultActions = mockMvc.perform(post("/session") .contentType(MediaType.APPLICATION_JSON) .content("{\"email\":\"x@example.com\",\"password\":\"test\"}")); resultActions .andExpect(status().isBadRequest()); verify(userService).authenticate(eq(email), eq(password)); } @Test @DisplayName("올바르지 않은 패스워드를 이용한 요청을 시도") public void createWithInvalidAttributes() throws Exception { String email = "tester@example.com"; String password = "x"; given(userService.authenticate(email, password)).willThrow(PasswordWrongException.class); ResultActions resultActions = mockMvc.perform(post("/session") .contentType(MediaType.APPLICATION_JSON) .content("{\"email\":\"tester@example.com\",\"password\":\"x\"}")); resultActions .andExpect(status().isBadRequest()); verify(userService).authenticate(eq(email), eq(password)); }}
public class EmailNotExistedException extends RuntimeException{ public EmailNotExistedException(){ super("Email is Not registered"); }}
public class PasswordWrongException extends RuntimeException{ public PasswordWrongException(){ super("Password is Wrong"); }}
@ControllerAdvicepublic class SessionErrorAdvice { @ResponseBody @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(PasswordWrongException.class) public String handlePasswordWrong(){ return "{}"; } @ResponseBody @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(EmailNotExistedException.class) public String handleEmailNotExisted(){ return "{}"; }}
@Service@RequiredArgsConstructorpublic class UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; public User registerUser(String email, String name, String password) { // 회원이 이미 등록되어 있는지 Email을 통해 유효성 검사 Optional<User> optional = userRepository.findByEmail(email); // 회원이 이미 존재하는 경우 예외처리를 한다. if(optional.isPresent()){ throw new EmailExistedException(email); }// // 패스워드를 암호화해서 저장한다.// // 암호화 방식은 BCrypt방식을 이용해 암호화를 진행// BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String encodedPassword = passwordEncoder.encode(password); User user = User.builder() .email(email) .name(name) .password(encodedPassword) .level(1L) .build(); userRepository.save(user); return user; } public User authenticate(String email, String password) { User user = userRepository.findByEmail(email) .orElseThrow(() -> new EmailNotExistedException()); String encodedPassword = passwordEncoder.encode(password); if(!passwordEncoder.matches(password, user.getPassword())){ throw new PasswordWrongException(); } return user; }}
@Test@DisplayName("올바른 파라미터를 이용해 사용자 유효성 검사를 한다.")public void authenticateWithValidAttributes(){ String email = "test@example.com"; String name = "Tester"; String password = "test"; User mockUser = User.builder() .Id(1004L) .email(email) .name(name) .password(password) .build(); given(userRepository.findByEmail(email)).willReturn(Optional.of(mockUser)); given(passwordEncoder.matches(any(), any())).willReturn(true); User user = userService.authenticate(email, password); assertThat(user.getEmail()).isEqualTo(email);}@Test@DisplayName("올바르지 않은 이메일을 이용해 사용자 유효성 검사를 한다.")public void authenticateNotValidEmail(){ String email = "x@example.com"; String password = "test"; given(userRepository.findByEmail(email)).willThrow(EmailNotExistedException.class); assertThatThrownBy(()->{ User user = userService.authenticate(email, password); }).isInstanceOf(EmailNotExistedException.class);}@Test@DisplayName("올바르지 않은 이메일을 이용해 사용자 유효성 검사를 한다.")public void authenticateNotValidPassword(){ String email = "test@example.com"; String name = "Tester"; String password = "x"; User mockUser = User.builder() .Id(1004L) .email(email) .name(name) .password(password) .build(); given(userRepository.findByEmail(email)).willReturn(Optional.of(mockUser)); given(passwordEncoder.matches(password, mockUser.getPassword())).willReturn(false); assertThatThrownBy(()->{ User user = userService.authenticate(email, password); }).isInstanceOf(PasswordWrongException.class);}
@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .cors().disable() .csrf().disable() .formLogin().disable() .headers().frameOptions().disable(); } @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }}