From e098be0146822b18ea5840047c6092a7baab5c95 Mon Sep 17 00:00:00 2001 From: Heejeong01110 <123rkdrkd@naver.com> Date: Sun, 23 Jan 2022 01:36:00 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20api?= =?UTF-8?q?=20=EC=8B=9C=ED=81=90=EB=A6=AC=ED=8B=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configures/JwtAuthenticationFilter.java | 95 +++++++++++++++++++ .../configures/WebSecurityConfig.java | 49 +++++++--- .../everevent/controller/UserController.java | 19 ---- .../everevent/service/UserService.java | 28 ------ .../everevent/service/UserServiceTest.java | 69 -------------- 5 files changed, 130 insertions(+), 130 deletions(-) create mode 100644 src/main/java/kdt/prgrms/kazedon/everevent/configures/JwtAuthenticationFilter.java diff --git a/src/main/java/kdt/prgrms/kazedon/everevent/configures/JwtAuthenticationFilter.java b/src/main/java/kdt/prgrms/kazedon/everevent/configures/JwtAuthenticationFilter.java new file mode 100644 index 0000000..cda92ca --- /dev/null +++ b/src/main/java/kdt/prgrms/kazedon/everevent/configures/JwtAuthenticationFilter.java @@ -0,0 +1,95 @@ +package kdt.prgrms.kazedon.everevent.configures; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.text.MessageFormat; +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import kdt.prgrms.kazedon.everevent.configures.auth.CustomUserDetails; +import kdt.prgrms.kazedon.everevent.domain.user.dto.request.LoginRequest; +import kdt.prgrms.kazedon.everevent.domain.user.dto.response.UserInfoResponse; +import kdt.prgrms.kazedon.everevent.exception.ErrorMessage; +import kdt.prgrms.kazedon.everevent.service.converter.UserConverter; +import lombok.SneakyThrows; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.server.ServletServerHttpResponse; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + + private final AuthenticationManager authenticationManager; + + private final JwtAuthenticationProvider jwtAuthenticationProvider; + + private final UserConverter userConverter; + + public JwtAuthenticationFilter( + AuthenticationManager authenticationManager, + JwtAuthenticationProvider jwtAuthenticationProvider, + UserConverter userConverter) { + this.authenticationManager = authenticationManager; + this.jwtAuthenticationProvider = jwtAuthenticationProvider; + this.userConverter = userConverter; + } + + @SneakyThrows + @Override + public Authentication attemptAuthentication(HttpServletRequest request, + HttpServletResponse response) + throws AuthenticationException { + + LoginRequest loginRequest = new ObjectMapper().readValue(request.getInputStream(), + LoginRequest.class); + + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken( + (loginRequest != null) ? loginRequest.getEmail() : null, + (loginRequest != null) ? loginRequest.getPassword() : null); + + return authenticationManager.authenticate(authenticationToken); + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, + FilterChain chain, + Authentication authResult) throws IOException { + + CustomUserDetails userDetails = (CustomUserDetails) authResult.getPrincipal(); + String token = jwtAuthenticationProvider.createToken( + userDetails.getUsername(), + userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList() + ); + + response.setHeader("X-AUTH-TOKEN", token); + response.setStatus(HttpStatus.OK.value()); + + UserInfoResponse userInfoResponse = userConverter.convertToUserInfoResponse( + userDetails.getUser()); + MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); + MediaType jsonMimeType = MediaType.APPLICATION_JSON; + + if (jsonConverter.canWrite(userInfoResponse.getClass(), jsonMimeType)) { + jsonConverter.write(userInfoResponse, jsonMimeType, new ServletServerHttpResponse(response)); + } + } + + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, + HttpServletResponse response, AuthenticationException exception) throws IOException { + LoginRequest loginRequest = new ObjectMapper().readValue(request.getInputStream(), + LoginRequest.class); + + String errorMessage = MessageFormat.format(ErrorMessage.LOGIN_FAILED.getMessage(), + loginRequest.getEmail()); + response.sendError(HttpStatus.UNAUTHORIZED.value(), errorMessage); + } + +} diff --git a/src/main/java/kdt/prgrms/kazedon/everevent/configures/WebSecurityConfig.java b/src/main/java/kdt/prgrms/kazedon/everevent/configures/WebSecurityConfig.java index 09553a6..e9cff5e 100644 --- a/src/main/java/kdt/prgrms/kazedon/everevent/configures/WebSecurityConfig.java +++ b/src/main/java/kdt/prgrms/kazedon/everevent/configures/WebSecurityConfig.java @@ -2,17 +2,21 @@ import kdt.prgrms.kazedon.everevent.configures.property.JwtProperty; import kdt.prgrms.kazedon.everevent.service.CustomUserDetailService; +import kdt.prgrms.kazedon.everevent.service.converter.UserConverter; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.BeanIds; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.filter.CorsFilter; @Configuration @@ -23,16 +27,46 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final JwtProperty jwtProperty; private final CustomUserDetailService customUserDetailService; private final CorsFilter corsFilter; + private final UserConverter userConverter; public JwtAuthenticationProvider jwtAuthenticationProvider() { return new JwtAuthenticationProvider(jwtProperty, customUserDetailService); } + @Bean(BeanIds.AUTHENTICATION_MANAGER) + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + private JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception { + JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter( + authenticationManagerBean(), jwtAuthenticationProvider(), userConverter); + jwtAuthenticationFilter.setFilterProcessesUrl("/api/v1/login"); + return jwtAuthenticationFilter; + } + @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } + @Override + public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) + throws Exception { + authenticationManagerBuilder + .userDetailsService(customUserDetailService) + .passwordEncoder(passwordEncoder()); + } + + @Override + public void configure(WebSecurity web) { + web.ignoring().antMatchers( + "/api-docs", + "/webjars/**", + "/h2-console/**"); + } + @Override protected void configure(HttpSecurity http) throws Exception { http @@ -42,6 +76,7 @@ protected void configure(HttpSecurity http) throws Exception { .formLogin().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtAuthenticationProvider())) + .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) .authorizeRequests() .antMatchers("/api/v1/signup") .anonymous() @@ -77,18 +112,4 @@ protected void configure(HttpSecurity http) throws Exception { .permitAll(); } - @Override - public void configure(WebSecurity web) { - web.ignoring().antMatchers( - "/api-docs", - "/webjars/**", - "/h2-console/**"); - } - - @Bean - @Override - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - } diff --git a/src/main/java/kdt/prgrms/kazedon/everevent/controller/UserController.java b/src/main/java/kdt/prgrms/kazedon/everevent/controller/UserController.java index 31690ce..815c6ef 100644 --- a/src/main/java/kdt/prgrms/kazedon/everevent/controller/UserController.java +++ b/src/main/java/kdt/prgrms/kazedon/everevent/controller/UserController.java @@ -4,16 +4,13 @@ import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; -import kdt.prgrms.kazedon.everevent.configures.JwtAuthenticationProvider; import kdt.prgrms.kazedon.everevent.configures.auth.AuthUser; -import kdt.prgrms.kazedon.everevent.configures.auth.CustomUserDetails; import kdt.prgrms.kazedon.everevent.domain.event.dto.response.UserParticipateEventsResponse; import kdt.prgrms.kazedon.everevent.domain.favorite.dto.response.SimpleMarketFavoriteReadResponse; import kdt.prgrms.kazedon.everevent.domain.like.dto.response.SimpleEventLikeReadResponse; import kdt.prgrms.kazedon.everevent.domain.review.dto.response.UserReviewReadResponse; import kdt.prgrms.kazedon.everevent.domain.user.User; import kdt.prgrms.kazedon.everevent.domain.user.dto.request.CheckPasswordRequest; -import kdt.prgrms.kazedon.everevent.domain.user.dto.request.LoginRequest; import kdt.prgrms.kazedon.everevent.domain.user.dto.request.SignUpRequest; import kdt.prgrms.kazedon.everevent.domain.user.dto.request.UserUpdateRequest; import kdt.prgrms.kazedon.everevent.domain.user.dto.response.UserInfoResponse; @@ -34,7 +31,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -59,21 +55,6 @@ public class UserController { private final FavoriteService favoriteService; private final ReviewService reviewService; private final LikeService likeService; - private final JwtAuthenticationProvider jwtAuthenticationProvider; - - @PostMapping("/login") - public ResponseEntity login(@RequestBody LoginRequest request) { - UserInfoResponse response = userService.login(request); - - CustomUserDetails userDetails = (CustomUserDetails) SecurityContextHolder.getContext() - .getAuthentication().getPrincipal(); - String token = jwtAuthenticationProvider.createToken( - userDetails.getUsername(), - userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList() - ); - - return ResponseEntity.ok().header(TOKEN_HEADER, token).body(response); - } @PostMapping("/signup") public ResponseEntity signUp(@RequestBody @Valid SignUpRequest request) { diff --git a/src/main/java/kdt/prgrms/kazedon/everevent/service/UserService.java b/src/main/java/kdt/prgrms/kazedon/everevent/service/UserService.java index fd347f4..e063f57 100644 --- a/src/main/java/kdt/prgrms/kazedon/everevent/service/UserService.java +++ b/src/main/java/kdt/prgrms/kazedon/everevent/service/UserService.java @@ -2,7 +2,6 @@ import kdt.prgrms.kazedon.everevent.domain.user.User; import kdt.prgrms.kazedon.everevent.domain.user.UserType; -import kdt.prgrms.kazedon.everevent.domain.user.dto.request.LoginRequest; import kdt.prgrms.kazedon.everevent.domain.user.dto.request.SignUpRequest; import kdt.prgrms.kazedon.everevent.domain.user.dto.request.UserUpdateRequest; import kdt.prgrms.kazedon.everevent.domain.user.dto.response.UserInfoResponse; @@ -12,14 +11,9 @@ import kdt.prgrms.kazedon.everevent.exception.ErrorMessage; import kdt.prgrms.kazedon.everevent.exception.InvalidPasswordException; import kdt.prgrms.kazedon.everevent.exception.NotFoundException; -import kdt.prgrms.kazedon.everevent.exception.UnAuthorizedException; import kdt.prgrms.kazedon.everevent.service.converter.UserConverter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,7 +26,6 @@ public class UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; private final UserConverter userConverter; - private final AuthenticationManager authenticationManager; @Transactional public void signUp(SignUpRequest request) { @@ -83,27 +76,6 @@ public void updateUser(UserUpdateRequest updateRequest, User user) { userRepository.save(user); } - @Transactional(readOnly = true) - public UserInfoResponse login(LoginRequest request) { - User user = userRepository.findByEmail(request.getEmail()) - .orElseThrow( - () -> new UnAuthorizedException(ErrorMessage.LOGIN_FAILED, request.getEmail())); - - if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { - throw new UnAuthorizedException(ErrorMessage.LOGIN_FAILED, request.getEmail()); - } - - setAuthenticate(request.getEmail(), request.getPassword()); - - return userConverter.convertToUserInfoResponse(user); - } - - private void setAuthenticate(String email, String password) { - Authentication authenticate = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(email, password)); - SecurityContextHolder.getContext().setAuthentication(authenticate); - } - public void checkPassword(User user, String password) { if (!passwordEncoder.matches(password, user.getPassword())) { throw new InvalidPasswordException(ErrorMessage.INVALID_PASSWORD, user.getEmail()); diff --git a/src/test/java/kdt/prgrms/kazedon/everevent/service/UserServiceTest.java b/src/test/java/kdt/prgrms/kazedon/everevent/service/UserServiceTest.java index 8e42d41..0d19995 100644 --- a/src/test/java/kdt/prgrms/kazedon/everevent/service/UserServiceTest.java +++ b/src/test/java/kdt/prgrms/kazedon/everevent/service/UserServiceTest.java @@ -5,8 +5,6 @@ import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -15,7 +13,6 @@ import java.util.Optional; import kdt.prgrms.kazedon.everevent.domain.user.User; import kdt.prgrms.kazedon.everevent.domain.user.UserType; -import kdt.prgrms.kazedon.everevent.domain.user.dto.request.LoginRequest; import kdt.prgrms.kazedon.everevent.domain.user.dto.request.SignUpRequest; import kdt.prgrms.kazedon.everevent.domain.user.dto.request.UserUpdateRequest; import kdt.prgrms.kazedon.everevent.domain.user.dto.response.UserInfoResponse; @@ -23,19 +20,14 @@ import kdt.prgrms.kazedon.everevent.domain.user.repository.UserRepository; import kdt.prgrms.kazedon.everevent.exception.DuplicateUserArgumentException; import kdt.prgrms.kazedon.everevent.exception.InvalidPasswordException; -import kdt.prgrms.kazedon.everevent.exception.InvalidUserArgumentException; import kdt.prgrms.kazedon.everevent.exception.NotFoundException; -import kdt.prgrms.kazedon.everevent.exception.UnAuthorizedException; import kdt.prgrms.kazedon.everevent.service.converter.UserConverter; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.util.ReflectionTestUtils; @@ -303,67 +295,6 @@ void updateUserUsingDuplicatedNicknameTest() { verify(userRepository).existsByNickname(updateRequest.getNickname()); } - @Test - void loginSuccessTest() { - //given - LoginRequest loginRequest = LoginRequest.builder() - .email(userEmail) - .password(user.getPassword()) - .build(); - - Authentication authenticate = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken( - loginRequest.getEmail(), loginRequest.getPassword()) - ); - - when(userRepository.findByEmail(user.getEmail())).thenReturn(Optional.of(user)); - when(passwordEncoder.matches(any(), any())).thenReturn(true); - when(authenticationManager.authenticate(any())).thenReturn(authenticate); - - //when - userService.login(loginRequest); - - //then - verify(userRepository).findByEmail(user.getEmail()); - verify(passwordEncoder).matches(any(), any()); - } - - @Test - void loginNotExistEmailTest() { - //given - LoginRequest loginRequest = LoginRequest.builder() - .email(userEmail) - .password(user.getPassword()) - .build(); - - when(userRepository.findByEmail(user.getEmail())).thenReturn(Optional.empty()); - - //when - assertThrows(UnAuthorizedException.class, () -> userService.login(loginRequest)); - - //then - verify(userRepository).findByEmail(user.getEmail()); - } - - @Test - void loginNotEqualsPasswordTest() { - //given - String password = "notEquals-password"; - LoginRequest loginRequest = LoginRequest.builder() - .email(userEmail) - .password(password) - .build(); - - when(userRepository.findByEmail(user.getEmail())).thenReturn(Optional.of(user)); - when(passwordEncoder.matches(password, user.getPassword())).thenReturn(false); - - //when - assertThrows(UnAuthorizedException.class, () -> userService.login(loginRequest)); - - //then - verify(userRepository).findByEmail(user.getEmail()); - } - @Test public void checkPasswordSuccessTest() { //given