레스토랑 예약 사이트 만들기 11 - 인증

AccessToken을 이용한 인증

@RestController
@RequiredArgsConstructor
public 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");
}
}
@ControllerAdvice
public 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
@RequiredArgsConstructor
public 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
@EnableWebSecurity
public 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();
}
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;

@NotEmpty
private String email;

@NotEmpty
private String name;

@NotNull
private Long level;

@NotEmpty
private String password;


public boolean isAdmin() {
return level > 2L;
}

public void deactivate(){
level = 0L;
}

public boolean isActive() {
return level > 0L;
}

@JsonIgnore
public String getAccessToken() {
if(password == null){
return "";
}

return password.substring(0, 10);
}
}
Share