Skip to content

Commit

Permalink
[SAMBAD-220] 클라이언트 기반 Redirect URL 설정 기능 구현 (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
kkjsw17 authored Aug 3, 2024
1 parent c782ae4 commit 638b539
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package org.depromeet.sambad.moring.auth.application;

import static org.depromeet.sambad.moring.auth.presentation.RedirectUrlFilter.*;
import static org.depromeet.sambad.moring.auth.presentation.exception.AuthExceptionCode.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;

import org.depromeet.sambad.moring.auth.domain.CustomOAuth2User;
import org.depromeet.sambad.moring.auth.domain.LoginResult;
import org.depromeet.sambad.moring.auth.infrastructure.SecurityProperties;
import org.depromeet.sambad.moring.auth.presentation.exception.AlreadyRegisteredUserException;
import org.depromeet.sambad.moring.meeting.member.application.MeetingMemberService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
Expand All @@ -35,7 +39,7 @@ public void onAuthenticationSuccess(
try {
LoginResult result = resolveLoginResultFromAuthentication(authentication);
tokenInjector.injectTokensToCookie(result, response);
redirectToSuccessUrl(result, response);
redirectToSuccessUrl(result, request, response);
} catch (AlreadyRegisteredUserException e) {
handleAlreadyExistUser(response);
}
Expand All @@ -46,12 +50,28 @@ private LoginResult resolveLoginResultFromAuthentication(Authentication authenti
return authService.handleLoginSuccess(oAuth2User.getAuthAttributes());
}

private void redirectToSuccessUrl(LoginResult result, HttpServletResponse response) throws IOException {
String redirectUrl = determineRedirectUrl(result);
private void redirectToSuccessUrl(
LoginResult result, HttpServletRequest request, HttpServletResponse response
) throws IOException {
String redirectUrlByCookie = getRedirectUrlByCookie(request);
String redirectUrl = determineRedirectUrl(result, redirectUrlByCookie);
response.sendRedirect(redirectUrl);
tokenInjector.invalidateCookie(REDIRECT_URL_COOKIE_NAME, response);
}

private String getRedirectUrlByCookie(HttpServletRequest request) {
return Arrays.stream(request.getCookies())
.filter(cookie -> Objects.equals(cookie.getName(), REDIRECT_URL_COOKIE_NAME))
.findFirst()
.map(Cookie::getValue)
.orElse(null);
}

private String determineRedirectUrl(LoginResult result) {
private String determineRedirectUrl(LoginResult result, String redirectCookie) {
if (StringUtils.hasText(redirectCookie)) {
return redirectCookie;
}

if (meetingMemberService.isNotEnterAnyMeeting(result.userId())) {
return result.isNewUser()
? securityProperties.newUserRedirectUrl() + "?newUser=true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void injectTokensToCookie(LoginResult result, HttpServletResponse respons
addCookie(REFRESH_TOKEN, result.refreshToken(), refreshTokenMaxAge, response);
}

private void addCookie(String name, String value, int maxAge, HttpServletResponse response) {
public void addCookie(String name, String value, int maxAge, HttpServletResponse response) {
Cookie cookie = new Cookie(name, value);
cookie.setPath("/");
cookie.setMaxAge(maxAge);
Expand All @@ -38,4 +38,16 @@ private void addCookie(String name, String value, int maxAge, HttpServletRespons

response.addCookie(cookie);
}

public void invalidateCookie(String name, HttpServletResponse response) {
Cookie cookie = new Cookie(name, null);
cookie.setPath("/");
cookie.setMaxAge(0);
cookie.setHttpOnly(securityProperties.cookie().httpOnly());
cookie.setDomain(securityProperties.cookie().domain());
cookie.setSecure(securityProperties.cookie().secure());
cookie.setAttribute("SameSite", "None");

response.addCookie(cookie);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.io.IOException;

import org.depromeet.sambad.moring.auth.application.RefreshTokenService;
import org.depromeet.sambad.moring.auth.application.TokenInjector;
import org.depromeet.sambad.moring.auth.domain.TokenResolver;
import org.depromeet.sambad.moring.auth.presentation.exception.AuthenticationRequiredException;
import org.depromeet.sambad.moring.auth.presentation.exception.RefreshTokenNotValidaException;
Expand All @@ -19,7 +20,6 @@
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
Expand All @@ -31,6 +31,7 @@
public class JwtTokenFilter extends OncePerRequestFilter {

private final TokenResolver tokenResolver;
private final TokenInjector tokenInjector;
private final UserDetailsService userDetailsService;
private final RefreshTokenService refreshTokenService;

Expand Down Expand Up @@ -87,10 +88,7 @@ private void setAuthentication(HttpServletRequest request, UserDetails userDetai
}

private void invalidateCookie(String cookieName, HttpServletResponse response) {
Cookie cookieForInvalidate = new Cookie(cookieName, null);
cookieForInvalidate.setMaxAge(0);
response.addCookie(cookieForInvalidate);

tokenInjector.invalidateCookie(cookieName, response);
SecurityContextHolder.clearContext();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.depromeet.sambad.moring.auth.presentation;

import java.io.IOException;
import java.util.List;

import org.depromeet.sambad.moring.auth.application.TokenInjector;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Component
public class RedirectUrlFilter extends OncePerRequestFilter {

private final TokenInjector tokenInjector;

public static final String REDIRECT_URL_QUERY_PARAM = "redirectUrl";
public static final String REDIRECT_URL_COOKIE_NAME = "redirect_url";
private static final List<String> REDIRECT_URL_INJECTION_PATTERNS = List.of(
"/oauth2/authorization/.*"
);

@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain
) throws ServletException, IOException {
if (isRedirectRequest(request)) {
tokenInjector.invalidateCookie(REDIRECT_URL_COOKIE_NAME, response);
String redirectUri = request.getParameter(REDIRECT_URL_QUERY_PARAM);

if (StringUtils.hasText(redirectUri)) {
tokenInjector.addCookie(REDIRECT_URL_COOKIE_NAME, redirectUri, 3600, response);
}
}

filterChain.doFilter(request, response);
}

private boolean isRedirectRequest(HttpServletRequest request) {
return REDIRECT_URL_INJECTION_PATTERNS.stream()
.anyMatch(request.getRequestURI()::matches);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

import org.depromeet.sambad.moring.auth.infrastructure.SecurityProperties;
import org.depromeet.sambad.moring.auth.presentation.JwtTokenFilter;
import org.depromeet.sambad.moring.auth.presentation.RedirectUrlFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
Expand All @@ -28,6 +30,7 @@ public class SecurityConfig {
private final DefaultOAuth2UserService defaultOAuth2UserService;
private final AuthenticationEntryPoint authenticationEntryPoint;
private final JwtTokenFilter jwtTokenFilter;
private final RedirectUrlFilter redirectUrlFilter;
private final SecurityProperties securityProperties;
private final AuthenticationSuccessHandler authenticationSuccessHandler;
private final AuthenticationFailureHandler authenticationFailureHandler;
Expand Down Expand Up @@ -94,6 +97,7 @@ private void configureContentSecurityPolicy(HttpSecurity http) throws Exception
}

private void configureOAuth2Login(HttpSecurity http) throws Exception {
http.addFilterBefore(redirectUrlFilter, OAuth2AuthorizationRequestRedirectFilter.class);
http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
http.oauth2Login(oauth2 ->
oauth2.loginPage(securityProperties.loginUrl())
Expand Down

0 comments on commit 638b539

Please sign in to comment.