Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 격파르타장점
- 인터프린터언어
- sqld자격증합격
- 작은수제거하기
- java map 출력
- 격파르타비전공자
- java map 저장
- javaJVM
- 격파르타합격후기
- 항해15기
- java set 저장
- 컴파일
- javaJRE
- 노베이스부트캠프
- 프로그래머스
- 프로그래머스제일작은수
- java set 출력
- java map
- 비전공자sqld
- 코딩부트캠프후기
- java list 저장
- 항해99후기
- java최솟값구하기
- java참조자료형
- 격파르타후기
- java list 출력
- java알고리즘
- java기본자료형
- java알고리즘문제풀이
- java 자료구조 활용
Archives
- Today
- Total
코딩과 결혼합니다
231025 - [Game_Crew]리팩토링 : Spring Security JWT 로그인 본문
728x90
기존에는 로그인 기능을 어플리케이션 도메인에 구현하였는데, 이번에 Spring Security JWT 로그인으로 Filter에서 로그인을 처리하도록 리팩토링 해보았다. 이것에는 몇 가지 이점이 있다.
1. Spring Security는 보안 관련 다양한 기능을 제공하므로 보안 구현 부담을 줄일 수 있다.
2. Spring Security의 다양한 확장 기능을 활용하여 인증 방식이나 사용자 관리 기능을 유연하게 변경하거나 확장할 수 있다.
📌JWT 검증 및 인가
Slf4j(topic = "JWT 검증 및 인가")
@RequiredArgsConstructor
public class JwtAuthorizationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final UserDetailsServiceImpl userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) throws ServletException, IOException {
String tokenValue = jwtUtil.getTokenFromRequest(req);
if (StringUtils.hasText(tokenValue)) {
// JWT 토큰 substring
tokenValue = jwtUtil.substringToken(tokenValue);
log.info(tokenValue);
if (!jwtUtil.validateToken(tokenValue)) {
log.error("Token Error");
return;
}
Claims info = jwtUtil.getUserInfoFromToken(tokenValue);
try {
setAuthentication(info.getSubject());
} catch (Exception e) {
log.error(e.getMessage());
return;
}
}
filterChain.doFilter(req, res);
}
- doFilterInternal 메소드 : HTTP 요청이 들어올 때마다 실행되는 메소드이다. 여기서 JWT 토큰을 추출하고, 토큰의 유효성을 검증하며, 토큰에서 사용자 정보를 추출하여 인증 처리를 진행한다.
- 토큰 검증이 실패하거나 예외가 발생하면 로그를 출력하고 다음 필터로 넘어가지 않는다.
// 인증 처리
public void setAuthentication(String nickname) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = createAuthentication(nickname);
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
}
- SecurityContext context = SecurityContextHolder.createEmptyContext(); : 빈 SecurityContext 객체를 생성한다. SecurityContext는 보안 관련 정보를 담고 있는 객체로, 인증 정보를 포함한다.
- createAuthentication 메소드를 호출하여 주어진 닉네임을 기반으로 인증 객체(Authentication)를 생성한다.
- createAuthentication 메소드는 사용자의 닉네임을 받아서 UserDetails 객체를 로드하고, 이를 이용하여 UsernamePassWordAuthenticationToken 객체를 생성한다.
- context.setAuthentication(authentication); : 생성된 인증 객체를 SecurityContext에 설정한다. 이를 통해 현재 사용자의 인증 정보가 SecurityContext에 저장된다.
- SecurityContext 객체를 SecurityContextHolder에 설정 한다.
- SecurityContextHolder는 SecurityContext 를 전역적으로 접근할 수 있는 홀더 객체로 이를 통해 다른 곳에서도 해당 인증 정보를 참조할 수 있다.
📌로그인 및 JWT 생성
@Slf4j(topic = "로그인 및 JWT 생성")
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final JwtUtil jwtUtil;
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
setFilterProcessesUrl("/auth/login");
}
- UsernamePasswordAuthenticationFilter : 폼 로그인 방식의 인증을 처리하는
- JwtUtil 인스턴스를 주입받아 이를 통해 JWT 관련 작업을 수행
- setFilterProcessesUrl("/auth/login"); : 이 필터가 처리할 요청 URL을 설정
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
log.info("로그인 시도");
try {
LoginRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), LoginRequestDto.class);
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
requestDto.getEmail(),
requestDto.getPassword(),
null
)
);
} catch (IOException e) {
log.error(e.getMessage());
throw new RuntimeException(e.getMessage());
}
}
- 클라이언트로부터 받은 요청을 LoginRequestDto로 변환한다. ObjectMapper는 Jackson 라이브러리에서 제공하는 클래스로, JSON 데이터를 자바 객체로 변환하는 역직렬화를 수행한다.
- 변환된 LoginRequestDto에 담긴 이메일과 비밀번호를 바탕으로 UsernamePasswordAuthenticationToken인스턴스를 생성하고, 이를 authenticate 메소드에 전달하여 인증을 시도하고, 성공하면 인증 정보를 담은 객체를 반환한다.
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
log.info("로그인 성공 및 JWT 생성");
String email = ((UserDetailsImpl) authResult.getPrincipal()).getUsername();
String token = jwtUtil.createToken(email);
jwtUtil.addJwtToCookie(token, response);
// 클라이언트로 토큰을 응답으로 보내주기
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
// 토큰을 JSON 형식으로 응답 바디에 담기
String jsonResponse = "{\"token\":\"" + token + "\"}";
response.getWriter().write(jsonResponse);
response.getWriter().flush();
}
- 인증 성공 결과로 받은 Authenticaion 객체에서 UserDetailsImpl 객체를 가져와 이메일을 추출한다.
- 추출한 이메일을 이용하여 JWT 토큰을 생성한다.
- 생성된 JWT 토큰을 쿠키에 추가한다.
- 응답의 컨텐트 타입을 JSON으로 설정하고, 문자 인코딩을 UTF-8로 설정한다.
- 생성된 JWT 토큰을 JSON 형식으로 변환하여 이 문자열을 응답 바디에 쓰고, 출력 스트림을 플러시하여 클라이언트에 전송한다.
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
log.info("로그인 실패");
// 클라이언트로 실패 메시지를 응답으로 보내기
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // HTTP 상태 코드 401 - Unauthorized
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
// 실패 메시지를 JSON 형식으로 응답 바디에 담기
String jsonResponse = "{\"message\":\"로그인에 실패하였습니다.\"}";
response.getWriter().write(jsonResponse);
response.getWriter().flush();
}
}
- 사용자의 인증이 실패했을 때 실행되는 코드로 상태코드 401과 실패 메세지를 클라이언트에 전송한다.
'코딩과 매일매일♥ > Game_Crew' 카테고리의 다른 글
[Game_Crew] 유저 평점 기능 (0) | 2023.10.27 |
---|---|
231026 - [Game_Crew]트러블슈팅 : CORS 설정하기 (0) | 2023.10.26 |
231025 - [Game_Crew]트러블슈팅 : 자동 배포 환경 구축 (0) | 2023.10.25 |
231024 - [Game_Crew]JWT (0) | 2023.10.24 |
231021 - [Game_Crew]트러블 슈팅 : 지옥의 304 (1) | 2023.10.21 |