admin: at/rt 토큰 발급

This commit is contained in:
geonhos 2024-05-22 09:21:11 +09:00
parent d2fdb0824b
commit bbd2bc4ded
9 changed files with 38 additions and 27 deletions

View File

@ -8,6 +8,14 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.Optional; import java.util.Optional;
public class CookieHelper { public class CookieHelper {
public static Cookie createCookie(String cookieName, String cookieValue) {
Cookie jwtCookie = new Cookie(cookieName, cookieValue);
jwtCookie.setHttpOnly(true);
jwtCookie.setPath("/");
return jwtCookie;
}
public static Optional<String> getValueFromCookieWithName(String name) { public static Optional<String> getValueFromCookieWithName(String name) {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = attr.getRequest(); HttpServletRequest request = attr.getRequest();

View File

@ -5,6 +5,7 @@ import com.bpgroup.poc.admin.common.CookieHelper;
import com.bpgroup.poc.admin.common.JwtHelper; import com.bpgroup.poc.admin.common.JwtHelper;
import com.bpgroup.poc.admin.security.jwt.JwtTokenConstants; import com.bpgroup.poc.admin.security.jwt.JwtTokenConstants;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@ -50,7 +51,7 @@ public class LoggingFilter extends OncePerRequestFilter {
private void loggingRequest(ContentCachingRequestWrapper request, String sessionId) { private void loggingRequest(ContentCachingRequestWrapper request, String sessionId) {
String requestValue = new String(request.getContentAsByteArray(), StandardCharsets.UTF_8); String requestValue = new String(request.getContentAsByteArray(), StandardCharsets.UTF_8);
Optional<String> jwtToken = CookieHelper.getValueFromCookieWithName(request, JwtTokenConstants.NAME); Optional<String> jwtToken = CookieHelper.getValueFromCookieWithName(request, JwtTokenConstants.ACCESS_TOKEN);
if (jwtToken.isPresent()) { if (jwtToken.isPresent()) {
try { try {
@ -65,19 +66,21 @@ public class LoggingFilter extends OncePerRequestFilter {
username username
) )
); );
} catch (ExpiredJwtException e) {
log.info("SESSION ID: {} Request - JWT 토큰 만료", sessionId);
} catch (Exception e) { } catch (Exception e) {
log.info("session ID: {} Request - JWT 토큰 만료", sessionId); log.error("SESSION ID: {} Request - JWT 토큰 검증 실패", sessionId);
} }
} }
log.info("session ID: {} Request - method: {} uri: {}", sessionId, request.getMethod(), request.getRequestURI()); log.info("SESSION ID: {} Request - method: {} uri: {}", sessionId, request.getMethod(), request.getRequestURI());
log.info("session ID: {} Request - params: {}", sessionId, requestValue); log.info("SESSION ID: {} Request - params: {}", sessionId, requestValue);
} }
private void loggingResponse(ContentCachingResponseWrapper response, String sessionId) { private void loggingResponse(ContentCachingResponseWrapper response, String sessionId) {
String contentType = response.getContentType(); String contentType = response.getContentType();
log.info("Session ID: {} Response - status: {} Content-Type: {}", sessionId, response.getStatus(), contentType); log.info("SESSION ID: {} Response - status: {} Content-Type: {}", sessionId, response.getStatus(), contentType);
if (contentType != null && isLoggingContentType(contentType)) { if (contentType != null && isLoggingContentType(contentType)) {
String responseValue = new String(response.getContentAsByteArray(), StandardCharsets.UTF_8); String responseValue = new String(response.getContentAsByteArray(), StandardCharsets.UTF_8);
@ -89,7 +92,7 @@ public class LoggingFilter extends OncePerRequestFilter {
) )
); );
log.info("Session ID: {} Response - body: {}", sessionId, responseValue); log.info("SESSION ID: {} Response - body: {}", sessionId, responseValue);
} }
} }

View File

@ -1,8 +1,6 @@
package com.bpgroup.poc.admin.security; package com.bpgroup.poc.admin.security;
import com.bpgroup.poc.admin.security.authentication.CustomAuthenticationEntryPoint; import com.bpgroup.poc.admin.security.authentication.*;
import com.bpgroup.poc.admin.security.authentication.CustomAuthenticationFilter;
import com.bpgroup.poc.admin.security.authentication.CustomAuthenticationProvider;
import com.bpgroup.poc.admin.app.authentication.AuthenticationService; import com.bpgroup.poc.admin.app.authentication.AuthenticationService;
import com.bpgroup.poc.admin.security.authorization.CustomAccessDeniedHandler; import com.bpgroup.poc.admin.security.authorization.CustomAccessDeniedHandler;
import com.bpgroup.poc.admin.security.authorization.CustomAuthorizationManager; import com.bpgroup.poc.admin.security.authorization.CustomAuthorizationManager;
@ -81,7 +79,7 @@ public class SecurityConfig {
http.logout(c -> c http.logout(c -> c
.logoutRequestMatcher(new AntPathRequestMatcher(LOGOUT_PATH, "GET")) .logoutRequestMatcher(new AntPathRequestMatcher(LOGOUT_PATH, "GET"))
.logoutSuccessUrl(LOGIN_PATH) .logoutSuccessUrl(LOGIN_PATH)
.deleteCookies(JwtTokenConstants.NAME) .deleteCookies(JwtTokenConstants.ACCESS_TOKEN)
); );
return http.build(); return http.build();
@ -103,6 +101,8 @@ public class SecurityConfig {
public CustomAuthenticationFilter authenticationGenerateFilter() { public CustomAuthenticationFilter authenticationGenerateFilter() {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager()); filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
return filter; return filter;
} }

View File

@ -18,7 +18,7 @@ import java.util.Optional;
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override @Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
Optional<String> jwtToken = CookieHelper.getValueFromCookieWithName(JwtTokenConstants.NAME); Optional<String> jwtToken = CookieHelper.getValueFromCookieWithName(JwtTokenConstants.ACCESS_TOKEN);
if (jwtToken.isPresent()) { if (jwtToken.isPresent()) {
response.sendRedirect("/login?error=" + URLEncoder.encode("로그인 세션이 만료되었습니다. 다시 로그인 해주세요.", StandardCharsets.UTF_8)); response.sendRedirect("/login?error=" + URLEncoder.encode("로그인 세션이 만료되었습니다. 다시 로그인 해주세요.", StandardCharsets.UTF_8));
} else { } else {

View File

@ -24,8 +24,6 @@ public class CustomAuthenticationFilter extends AbstractAuthenticationProcessing
public CustomAuthenticationFilter() { public CustomAuthenticationFilter() {
super(new AntPathRequestMatcher(DEFAULT_LOGIN_REQUEST_URL, HTTP_METHOD)); // 위에서 설정한 /oauth2/login/* 요청에, GET으로 요청을 처리하기 위해 설정한다. super(new AntPathRequestMatcher(DEFAULT_LOGIN_REQUEST_URL, HTTP_METHOD)); // 위에서 설정한 /oauth2/login/* 요청에, GET으로 요청을 처리하기 위해 설정한다.
setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
} }
@Override @Override

View File

@ -1,6 +1,8 @@
package com.bpgroup.poc.admin.security.authentication; package com.bpgroup.poc.admin.security.authentication;
import com.bpgroup.poc.admin.app.authentication.AuthenticationResponse; import com.bpgroup.poc.admin.app.authentication.AuthenticationResponse;
import com.bpgroup.poc.admin.common.CookieHelper;
import com.bpgroup.poc.admin.security.jwt.JwtTokenConstants;
import com.bpgroup.poc.admin.security.jwt.JwtTokenGenerator; import com.bpgroup.poc.admin.security.jwt.JwtTokenGenerator;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.Cookie; import jakarta.servlet.http.Cookie;
@ -12,6 +14,7 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHand
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.UUID;
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler { public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override @Override
@ -21,8 +24,10 @@ public class CustomAuthenticationSuccessHandler implements AuthenticationSuccess
response.setCharacterEncoding(StandardCharsets.UTF_8.name()); response.setCharacterEncoding(StandardCharsets.UTF_8.name());
String jwtToken = JwtTokenGenerator.generate(authentication.getName()); String jwtToken = JwtTokenGenerator.generate(authentication.getName());
Cookie jwtCookie = JwtTokenGenerator.createJwtCookie(jwtToken); Cookie at = CookieHelper.createCookie(JwtTokenConstants.ACCESS_TOKEN, jwtToken);
response.addCookie(jwtCookie); Cookie rt = CookieHelper.createCookie(JwtTokenConstants.REFRESH_TOKEN, UUID.randomUUID().toString());
response.addCookie(at);
response.addCookie(rt);
String jsonResponse = new ObjectMapper().writeValueAsString( String jsonResponse = new ObjectMapper().writeValueAsString(
AuthenticationResponse.success() AuthenticationResponse.success()

View File

@ -5,8 +5,9 @@ package com.bpgroup.poc.admin.security.jwt;
*/ */
public class JwtTokenConstants { public class JwtTokenConstants {
public static final String ISSUER = "BP"; public static final String ISSUER = "BP";
public static final String SUBJECT = "Jwt Token"; public static final String SUBJECT = "TOKEN";
public static final String KEY = "8530b13adb4e420d9694b27570635b47"; public static final String KEY = "8530b13adb4e420d9694b27570635b47";
public static final String NAME = "JWT-TOKEN"; public static final String ACCESS_TOKEN = "AT";
public static final String REFRESH_TOKEN = "RT";
public static final long EXPIRATION_TIME = 30000; public static final long EXPIRATION_TIME = 30000;
} }

View File

@ -2,7 +2,6 @@ package com.bpgroup.poc.admin.security.jwt;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.Cookie;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -20,11 +19,4 @@ public class JwtTokenGenerator {
.signWith(key) .signWith(key)
.compact(); .compact();
} }
public static Cookie createJwtCookie(String jwtToken) {
Cookie jwtCookie = new Cookie(JwtTokenConstants.NAME, jwtToken);
jwtCookie.setHttpOnly(true);
jwtCookie.setPath("/");
return jwtCookie;
}
} }

View File

@ -3,6 +3,7 @@ package com.bpgroup.poc.admin.security.jwt;
import com.bpgroup.poc.admin.common.CookieHelper; import com.bpgroup.poc.admin.common.CookieHelper;
import com.bpgroup.poc.admin.common.JwtHelper; import com.bpgroup.poc.admin.common.JwtHelper;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@ -20,9 +21,10 @@ import java.util.stream.Stream;
public class JwtTokenValidateFilter extends OncePerRequestFilter { public class JwtTokenValidateFilter extends OncePerRequestFilter {
@Override @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String sessionId = request.getSession().getId();
try { try {
try { try {
Optional<String> jwtToken = CookieHelper.getValueFromCookieWithName(JwtTokenConstants.NAME); Optional<String> jwtToken = CookieHelper.getValueFromCookieWithName(JwtTokenConstants.ACCESS_TOKEN);
if (jwtToken.isPresent()) { if (jwtToken.isPresent()) {
Claims claims = JwtHelper.getClaims(JwtTokenConstants.KEY, jwtToken.get()); Claims claims = JwtHelper.getClaims(JwtTokenConstants.KEY, jwtToken.get());
String username = claims.get("username", String.class); String username = claims.get("username", String.class);
@ -30,8 +32,10 @@ public class JwtTokenValidateFilter extends OncePerRequestFilter {
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, null); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, null);
SecurityContextHolder.getContext().setAuthentication(auth); SecurityContextHolder.getContext().setAuthentication(auth);
} }
} catch (ExpiredJwtException e) {
log.info("SESSION ID: {} Request - JWT 토큰 만료", sessionId);
} catch (Exception e) { } catch (Exception e) {
log.error("JWT validation failed: {}", e.getMessage()); log.error("SESSION ID: {} Request - JWT 토큰 검증 실패", sessionId);
} }
} finally { } finally {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);