Spring Security _ Temp - taeyun-ham/andalos GitHub Wiki

Version

  • Spring Security : 6.2.3

Servlet Application Architecture

HttpSecurity ์„ค์ •๊ณผ SercurityFilter ์™€์˜ ๊ด€๊ณ„

// HeaderWriterFilter ๋ฅผ securityFilterChain์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
http.headers((headers) -> headers. ~~);

// CorsFilter๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
http.cors(withDefaults());

// SessionManagementFilter, ConcurrentSessionFilter, DisableEncodeUrlFilter, ForceEagerSessionCreationFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
http.sessionManagement((sessionManagement) -> sessionManagement.~~);

// portMapper ์— port ๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. requiresChannel ์„ค์ •์— ์˜ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
// 9090 port ๋ฅผ 9443 ์œผ๋กœ redirect
http.portMapper((portMapper) -> portMapper
  					.http(9090).mapsTo(9443)
  					.http(80).mapsTo(443));

// X509AuthenticationFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// X509 ์ธ์ฆ์„œ์—์„œ ์‚ฌ์šฉ์ž ์ด๋ฆ„์„ ์ถ”์ถœํ•˜๋ ค๊ณ  ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์ด ์ž‘๋™ํ•˜๋ ค๋ฉด ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ์„œ๋ฅผ ์š”์ฒญํ•˜๋„๋ก ๊ตฌ์„ฑ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
http.x509(withDefaults());

// RememberMeAuthenticationFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// session ์ด ๋งŒ๋ฃŒ๋˜๊ฑฐ๋‚˜ ์ฟ ํ‚ค๊ฐ€ ์—†๋”๋ผ๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ธฐ์–ตํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. (์ž๋™๋กœ๊ทธ์ธ)
http.rememberMe(withDefaults());

// AuthorizationFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” url ๊ณผ ๊ถŒํ•œ์„ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค.
http.authorizeHttpRequests((authorizeHttpRequests) -> ~~);

// RequestCacheAwareFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// ์ €์žฅ๋œ ์š”์ฒญ์ด ์บ์‹œ๋˜์–ด ์žˆ๊ณ  ํ˜„์žฌ ์š”์ฒญ๊ณผ ์ผ์น˜ํ•˜๋Š” ๊ฒฝ์šฐ ์ €์žฅ๋œ ์š”์ฒญ์„ ์žฌ๊ตฌ์„ฑํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
http.requestCache((requestCache) -> ~~);

// ExceptionTranslationFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// ํ•„ํ„ฐ ์ฒด์ธ ๋‚ด์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ชจ๋“  AccessDeniedException ๋ฐ AuthenticationException์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
http.exceptionHandling((exceptionHandling) ->~~)

// SecurityContextHolderFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// SecurityContextRepository๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SecurityContext๋ฅผ ์–ป๊ณ  ์ด๋ฅผ SecurityContextHolder์— ์„ค์ •ํ•˜๋Š” jakarta.servlet.Filter์ž…๋‹ˆ๋‹ค
http.securityContext((securityContext) -> ~~)

// SecurityContextHolderAwareRequestFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// exceptionHandling, logout ์„ค์ •์ •๋ณด๋ฅผ ์ฐธ์กฐํ•˜์—ฌ SecurityContextHolderAwareRequestFilter ์— ์…‹ํŒ…ํ•ฉ๋‹ˆ๋‹ค.
http.servletApi((servletApi) -> ~~);

// CsrfFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// ๋™๊ธฐํ™” ํ† ํฐ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ CSRF ๋ณดํ˜ธ๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
// logout, sessionManagement ์„ค์ •์ •๋ณด๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.
http.csrf((csrf) -> ~~);

// LogoutFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// ๋กœ๊ทธ์•„์›ƒ ํ›„ ์‚ฌ์šฉ๋œ ์ƒ์„ฑ์ž์— ๋”ฐ๋ผ ๊ตฌ์„ฑ๋œ LogoutSuccessHandler ๋˜๋Š” logoutSuccessUrl์— ์˜ํ•ด ๊ฒฐ์ •๋œ URL๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๊ฐ€ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.
http.logout((logout) -> ~~);

// AnonymousAuthenticationFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// SecurityContextHolder์— Authentication ๊ฐ์ฒด๊ฐ€ ์—†๋Š”์ง€ ๊ฐ์ง€ํ•˜๊ณ  ํ•„์š”ํ•œ ๊ฒฝ์šฐ ํ•˜๋‚˜๋ฅผ ์ฑ„์›๋‹ˆ๋‹ค.
http.anonymous((anonymous) -> ~~);

// UsernamePasswordAuthenticationFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// login url ๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์„๊ฒฝ์šฐ DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter ๊ฐ€ ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.
http.formLogin((formLogin) -> ~~);

// Saml2WebSsoAuthenticationRequestFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
http.saml2Login(withDefaults());

// logout ์„ค์ •์„ ์ฐธ์กฐํ•˜๊ณ  CsrfFilter ์•ž์— Saml2LogoutRequestFilter, Saml2LogoutResponseFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
http.saml2Logout(withDefaults());

// BasicAuthenticationFilter ์•ž์— Saml2MetadataFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
http.saml2Metadata(Customizer. withDefaults());

// OAuth2AuthorizationRequestRedirectFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// ์ด ํ•„ํ„ฐ๋Š” ์ตœ์ข… ์‚ฌ์šฉ์ž์˜ ์‚ฌ์šฉ์ž ์—์ด์ „ํŠธ๋ฅผ ์ธ์ฆ ์„œ๋ฒ„์˜ ์ธ์ฆ ์—”๋“œํฌ์ธํŠธ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•จ์œผ๋กœ์จ ๊ถŒํ•œ ๋ถ€์—ฌ ์ฝ”๋“œ ๋ถ€์—ฌ ํ๋ฆ„์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
http.oauth2Login(withDefaults());

// OAuth2AuthorizationRequestRedirectFilter, OAuth2AuthorizationCodeGrantFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// OAuth 2.0 ๊ถŒํ•œ ๋ถ€์—ฌ ์ฝ”๋“œ ๋ถ€์—ฌ๋ฅผ ์œ„ํ•œ ํ•„ํ„ฐ๋กœ, OAuth 2.0 ์ธ์ฆ ์‘๋‹ต ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
http.oauth2Client(withDefaults());

// BearerTokenAuthenticationFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// OAuth 2.0 ๋ฒ ์–ด๋Ÿฌ ํ† ํฐ์„ ํฌํ•จํ•˜๋Š” ์š”์ฒญ์„ ์ธ์ฆํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•„ํ„ฐ๋Š” OAuth 2.0 ๋ฒ ์–ด๋Ÿฌ ํ† ํฐ์„ ์ธ์ฆํ•  ์ˆ˜ ์žˆ๋Š” AuthenticationManager์™€ ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
http.oauth2ResourceServer((oauth2ResourceServer) -> ~~);

// ChannelProcessingFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// ์š”์ฒญ์ด ํ•„์š”ํ•œ ์ฑ„๋„์„ ํ†ตํ•ด ์ „๋‹ฌ๋˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์ฑ„๋„ ๋ณด์•ˆ ๊ฒฐ์ •๊ณผ ํ•„์š”ํ•œ ์กฐ์น˜๋ฅผ ๊ตฌ์„ฑ๋œ ChannelDecisionManager์— ์œ„์ž„ํ•ฉ๋‹ˆ๋‹ค. 
http.requiresChannel((requiresChannel) -> ~~)

// BasicAuthenticationFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// HTTP ์š”์ฒญ์˜ BASIC ์ธ์ฆ ํ—ค๋”๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ SecurityContextHolder์— ๋„ฃ์Šต๋‹ˆ๋‹ค.
http.httpBasic(withDefaults());

// RequestMatcherRedirectFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
// RequestMatcher์™€ ์ผ์น˜ํ•˜๋Š” ์š”์ฒญ์„ ์ง€์ •๋œ URL๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•˜๋Š” ํ•„ํ„ฐ์ž…๋‹ˆ๋‹ค.
http.passwordManagement(passwordManagement -> ~~);

// ์ง€์ •๋œ Url ์—์„œ๋งŒ security ๊ฐ€ ์ ์šฉ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
http.securityMatchers((matchers) -> matchers.requestMatchers("/api/**"));

// J2eePreAuthenticatedProcessingFilter ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
http.jee((jee) -> ~~)

Default Filter

  • DisableEncodeUrlFilter
  • WebAsyncManagerIntegrationFilter
  • SecurityContextHolderFilter
  • HeaderWriterFilter
  • CsrfFilter
  • LogoutFilter
  • UsernamePasswordAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • BasicAuthenticationFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • AnonymousAuthenticationFilter
  • ExceptionTranslationFilter
  • AuthorizationFilter
UsernamePasswordAuthenticationFilter
DefaultLoginPageGeneratingFilter
DefaultLogoutPageGeneratingFilter
BasicAuthenticationFilter

์œ„ 4๊ฐœ์˜ ํ•„ํ„ฐ๋Š” ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ฐ˜์˜ http ์ธ์ฆ์„ ์‚ฌ์šฉํ•  ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค.
Form ๊ธฐ๋ฐ˜ login ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ 4๊ฐœ์˜ ํ•„ํ„ฐ๋Š” ์ œ์™ธ๋œ๋‹ค.

DisableEncodeUrlFilter

  • HttpServletResponse๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ URL์„ ์ธ์ฝ”๋”ฉํ•˜๋Š” ๊ฒƒ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜์—ฌ, ์„ธ์…˜ ID๊ฐ€ HTTP ์ ‘๊ทผ ๋กœ๊ทธ์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์—์„œ ์œ ์ถœ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, URL์— ์„ธ์…˜ ID๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
/**
 * Disables encoding URLs using the {@link HttpServletResponse} to prevent including the
 * session id in URLs which is not considered URL because the session id can be leaked in
 * things like HTTP access logs.
 *
 * @author Rob Winch
 * @since 5.7
 */
public class DisableEncodeUrlFilter extends OncePerRequestFilter {

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		filterChain.doFilter(request, new DisableEncodeUrlResponseWrapper(response));
	}

	/**
	 * Disables URL rewriting for the {@link HttpServletResponse} to prevent including the
	 * session id in URLs which is not considered URL because the session id can be leaked
	 * in things like HTTP access logs.
	 *
	 * @author Rob Winch
	 * @since 5.7
	 */
	private static final class DisableEncodeUrlResponseWrapper extends HttpServletResponseWrapper {

		/**
		 * Constructs a response adaptor wrapping the given response.
		 * @param response the {@link HttpServletResponse} to be wrapped.
		 * @throws IllegalArgumentException if the response is null
		 */
		private DisableEncodeUrlResponseWrapper(HttpServletResponse response) {
			super(response);
		}

		@Override
		public String encodeRedirectURL(String url) {
			return url;
		}

		@Override
		public String encodeURL(String url) {
			return url;
		}

	}

}

filterChain.doFilter(request, new DisableEncodeUrlResponseWrapper(response));

  • ๋‹ค์Œ ํ•„ํ„ฐ์— response ๋ฅผ DisableEncodeUrlResponseWrapper ๋กœ ๊ต์ฒดํ•ด์„œ ์ „๋‹ฌํ•œ๋‹ค.
  • DisableEncodeUrlResponseWrapper ๋Š” HttpServletResponseWrapper ์˜ encodeURL, encodeRedirectURL ๋ฉ”์„œ๋“œ๋งŒ ์žฌ์ •์˜ ํ•˜์˜€๋‹ค.
  • ์ „๋‹ฌ ๋ฐ›์€ url ์„ ๊ทธ๋Œ€๋กœ ๋ฆฌํ„ดํ•œ๋‹ค.
  • ๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒฝ์šฐ org.apache.catalina.connector.Response.class ์˜ encodeURL ์— ์˜ํ•ด session id ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค.
    /**
     * Encode the session identifier associated with this response into the specified URL, if necessary.
     *
     * @param url URL to be encoded
     *
     * @return <code>true</code> if the URL was encoded
     */
    @Override
    public String encodeURL(String url) {

        String absolute;
        try {
            absolute = toAbsolute(url);
        } catch (IllegalArgumentException iae) {
            // Relative URL
            return url;
        }

        if (isEncodeable(absolute)) {
            // W3c spec clearly said
            if (url.equalsIgnoreCase("")) {
                url = absolute;
            } else if (url.equals(absolute) && !hasPath(url)) {
                url += '/';
            }
            return toEncoded(url, request.getSessionInternal().getIdInternal());  // <=== ์—ฌ๊ธฐ์„œ session id ๊ฐ€ url ์— query string ์œผ๋กœ ์ถ”๊ฐ€๋œ๋‹ค.
        } else {
            return url;
        }

    }
  • ์ฆ‰ DisableEncodeUrlFilter ๋Š” ์„ธ์…˜์„ ์‚ฌ์šฉํ• ๋•Œ๋งŒ ๊ด€๋ จ์ด ์žˆ๋‹ค.
  • Security config ์„ค์ •์—์„œ sessionManagement ๋ฅผ disable ์ฒ˜๋ฆฌํ•˜๋ฉด ํ•ด๋‹น ํ•„ํ„ฐ๊ฐ€ ์ž‘๋™๋˜์ง€ ์•Š๋Š”๋‹ค.
...
http.sessionManagement(AbstractHttpConfigurer::disable)
...

WebAsyncManagerIntegrationFilter

  • SecurityContextCallableProcessingInterceptor๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SecurityContext์™€ Spring Web์˜ WebAsyncManager ์‚ฌ์ด์˜ ํ†ตํ•ฉ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. beforeConcurrentHandling(org.springframework.web.context.request.NativeWebRequest, Callable)์„ ์‚ฌ์šฉํ•˜์—ฌ Callable์—์„œ SecurityContext๋ฅผ ์ฑ„์›๋‹ˆ๋‹ค.
  • ์ฆ‰, ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์‹œ ํŒŒ์ƒ๋˜๋Š” Thread ์—์„œSecurityContext ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.
public final class WebAsyncManagerIntegrationFilter extends OncePerRequestFilter {

	private static final Object CALLABLE_INTERCEPTOR_KEY = new Object();

	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
		.getContextHolderStrategy();

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor) asyncManager
			.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
		if (securityProcessingInterceptor == null) {
			SecurityContextCallableProcessingInterceptor interceptor = new SecurityContextCallableProcessingInterceptor();
			interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
			asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY, interceptor);
		}
		filterChain.doFilter(request, response);
	}

	/**
	 * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
	 * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
	 *
	 * @since 5.8
	 */
	public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
		Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
		this.securityContextHolderStrategy = securityContextHolderStrategy;
	}

}

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

  • WebAsyncManager ๋ฅผ ๋ฐ›์•„์™€ SecurityContextHolderStrategy ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์ฃผ์ž…์‹œํ‚ต๋‹ˆ๋‹ค.
  • WebAsyncManager ๋ž€ ๋น„๋™๊ธฐ ์š”์ฒญ ์ฒ˜๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์ค‘์‹ฌ ํด๋ž˜์Šค ์ž…๋‹ˆ๋‹ค.

Security Config

SecurityFilterChain

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.csrf(AbstractHttpConfigurer::disable) 
			.addFilterBefore(new CustomJwtAuthenticationFilter(customJwtProvider, SECRET_KEY),
				UsernamePasswordAuthenticationFilter.class)
			.authorizeHttpRequests(authorizeRequests ->
				authorizeRequests
					.requestMatchers(
						AntPathRequestMatcher.antMatcher("/api/**")
					).authenticated() // api ๊ฐ€ ํฌํ•จ๋œ url ์€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋งŒ.
					.requestMatchers(
						AntPathRequestMatcher.antMatcher("/api/service/**")
					).hasAuthority("ROLE_MEMBER") // /api/service/** ๋Š” ํšŒ์›๋งŒ.
					.requestMatchers(
						AntPathRequestMatcher.antMatcher("/api/admin/**")
					).hasAuthority("ROLE_ADMIN") // /api/admin/** ๋Š” ์–ด๋“œ๋ฏผ๋งŒ.
					.requestMatchers(
						AntPathRequestMatcher.antMatcher("/**")
					).permitAll()
			);
		return http.build();
	}
  • ์—ฌ๊ธฐ์„œ๋Š” Rest ๊ธฐ๋ฐ˜์˜ token ์ธ์ฆ๋ฐฉ์‹์œผ๋กœ ํ•  ๊ฒƒ์ด๊ธฐ์— .csrf(AbstractHttpConfigurer::disable) ๋Š” disable ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • disable ์„ ๋ช…์‹œํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ api ํ˜ธ์ถœ์‹œ csrf token ์ด ์—†๊ธฐ์— 403 ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • token ์ธ์ฆ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” CustomJwtAuthenticationFilter ๋ฅผ ๋ณ„๋„๋กœ ๋งŒ๋“ค์–ด UsernamePasswordAuthenticationFilter ์ด์ „์— ์ถ”๊ฐ€ํ•œ๋‹ค. (.addFilterBefore(new CustomJwtAuthenticationFilter(customJwtProvider, SECRET_KEY), UsernamePasswordAuthenticationFilter.class))
  • .formLogin ์„ ์„ค์ •ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— UsernamePasswordAuthenticationFilter ๋Š” ์ž‘๋™๋˜์ง€ ์•Š์œผ๋‚˜ CustomJwtAuthenticationFilter ๊ฐ€ ์œ„์น˜ ํ•ด์•ผ ํ•  ๊ณณ์€ UsernamePasswordAuthenticationFilter ์ด์ „์— ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • URL ์€ ํฌ๊ฒŒ 3๊ฐ€์ง€๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค.
  • ์ฒซ๋ฒˆ์งธ, /api/** ์— ํ•ด๋‹นํ•˜๋Š” url ์€ ์ธ์ฆ์ฒ˜๋ฆฌ๊ฐ€ ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ๋‘๋ฒˆ์งธ, /api/service/** ์— ํ•ด๋‹นํ•˜๋Š” url ์€ ํšŒ์›๊ถŒํ•œ์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ์„ธ๋ฒˆ์งธ, /api/admin/** ์— ํ•ด๋‹นํ•˜๋Š” url ์€ ์–ด๋“œ๋ฏผ๊ถŒํ•œ์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ์—ฌ๊ธฐ์„œ์˜ ๊ถŒํ•œ์€ ํ”ํžˆ๋“ค ๋งํ•˜๋Š” ๋ฉ”๋‰ด๋ณ„ ๊ถŒํ•œ, ์œ ์ €๋ณ„ ๊ถŒํ•œ, ๋ถ€์„œ๋ณ„ ๊ถŒํ•œ ๋ณด๋‹ค๋Š” ์กฐ๊ธˆ ๋” ๋„“์€ ๋ฒ”์œ„์˜ ์„œ๋น„์Šค๋ณ„ ๊ถŒํ•œ์œผ๋กœ ํ•ด์„ํ•˜๋Š” ๊ฒƒ์ด ์˜ณ๋‹ค.
  • Actor ๊ฐ€ ์–ด๋“œ๋ฏผ ์‚ฌ์šฉ์ž์ธ์ง€, BtoC ์‚ฌ์šฉ์ž์ธ์ง€, BtoB ์‚ฌ์šฉ์ž์ธ์ง€ ๊ตฌ๋ณ„ํ•˜์—ฌ ์„œ๋น„์Šค๋ณ„๋กœ ๊ถŒํ•œ ์ฒดํฌํ•  ๋•Œ ์‚ฌ์šฉํ•ด์•ผ ํ™œ์šฉ ๋ฒ”์œ„๊ฐ€ ๋„“์–ด์ง„๋‹ค.

CustomJwtAuthenticationFilter

doFilterInternal

	@Override
	protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
		@NonNull FilterChain filterChain) throws ServletException, IOException {
		try {
			String token = CustomJwtProvider.resolveToken(request);
			CustomJwtProvider.validateToken(token, secretKey);
			Authentication auth = customJwtProvider.getAuthentication(token, secretKey);
			SecurityContextHolder.getContext().setAuthentication(auth);
		} catch (AuthorizationNotFoundException ae) {
			// skip
		} catch (ExpiredJwtException ee) {
			log.warn(ee.getMessage());
			response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token has expired.");
			return; // ๋‹ค์Œ ์ง„ํ–‰์ด ๋˜์ง€ ์•Š์•„์•ผ 401 ๋กœ ์‘๋‹ต๋จ.
		} catch (Exception e) {
			log.warn(e.getMessage());
			response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token is invalid.");
			return; // ๋‹ค์Œ ์ง„ํ–‰์ด ๋˜์ง€ ์•Š์•„์•ผ 401 ๋กœ ์‘๋‹ต๋จ.
		}

		filterChain.doFilter(request, response);
	}
  • CustomJwtProvider.validateToken(token, secretKey); : CustomJwtAuthenticationFilter ์ƒ์„ฑ์‹œ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›์€ CustomJwtProvider ์™€ secretKey ๋กœ token ์„ ๊ฒ€์ฆํ•œ๋‹ค.
  • Authentication auth = customJwtProvider.getAuthentication(token, secretKey); : CustomJwtProvider ์—์„œ ์ธ์ฆ์ •๋ณด๋ฅผ ํš๋“ํ•˜๊ณ 
  • SecurityContextHolder.getContext().setAuthentication(auth); : SecurityContextHolder ์— ์ €์žฅํ•œ๋‹ค.

CustomJwtProvider

...

	// header.Authorization ์—์„œ bearer token ์ทจ๋“
	public static String resolveToken(HttpServletRequest request) {
		String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
		if (authorization == null)
			throw new AuthorizationNotFoundException("The request header does not contain Authorization.");
		return authorization.split(" ")[1].trim();
	}

	public static boolean validateToken(String token, String secretKey) {
		secretKey = getBase64EncodeSecretKey(secretKey);
		Jws<Claims> claims = Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);
		return claims.getBody().getExpiration().after(new Date());
	}

	public Authentication getAuthentication(String token, String secretKey) {
		// todo ์ฐจํ›„ redis ์—์„œ ์กฐํšŒํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝ.
		UserDetails userDetails = userServiceImpl.loadUserByUsername(getSubject(token, secretKey));
		return new UsernamePasswordAuthenticationToken(userDetails, token, userDetails.getAuthorities());
	}

...
โš ๏ธ **GitHub.com Fallback** โš ๏ธ