Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TLVB-236] 로그인 api 시큐리티 적용 #77

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래에 예외를 던지는데 이거 사용한 이유가 무엇인가요?

@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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

X-AUTH-TOKEN 이 값을 상수로 할 수 있도록 하면 어떨까요?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JwtProperty 클래스에 변수(HEADER_STRING)로 추가 해도 좋을 것 같슴다ㅏㅏ!!

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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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/**");
Comment on lines +65 to +67
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍👍

}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
Expand All @@ -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()
Expand Down Expand Up @@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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<UserInfoResponse> 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<Void> signUp(@RequestBody @Valid SignUpRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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());
Expand Down
Loading