v2_techspec_auth - 100-hours-a-week/KTB4-8th-BE GitHub Wiki

์ธ์ฆ/์ธ๊ฐ€ ๋„๋ฉ”์ธ ํ…Œํฌ์ŠคํŽ™

SecurityFilterChain ๋ชฉ๋ก

  • ApiFilterChain
  • RefreshFilterChain
  • LoginFilterChain
  • FallbackFilterChain

๊ฐ SecurityFilterChain๋ณ„ ์ƒ์„ธ

ApiFilterChain

์˜์กด์„ฑ

  • JwtDecoder
  • AccessTokenFailAuthenticationEntryPoint

์ธ์ฆ ๋กœ์ง

  • ์œ ํšจํ•œ Access Token์ด ํ™•์ธ๋˜์ง€ ์•Š์„ ์‹œ 401(invalid-token)
    • ํ”„๋ก ํŠธ๋Š” ํ•ด๋‹น ์˜ค๋ฅ˜ ์‹œ Access Token ๋ฐœ๊ธ‰ API๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ

Spring OAuth2 Resource Server ํ™•์žฅ์ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ์„ค์ •์„ ์ตœ๋Œ€ํ•œ ์ด์šฉํ•˜์˜€์œผ๋‚˜, JwtDecoder๋Š” ๊ธฐ๋ณธ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์œผ๋กœ ํŒŒ์•…ํ•˜์—ฌ JwtDecoder๋งŒ ์ง์ ‘ ๊ตฌํ˜„ํ•จ

Config
http
                .securityMatcher("/api/**")
                .csrf(AbstractHttpConfigurer::disable)
                .cors(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .httpBasic(AbstractHttpConfigurer::disable)
                .oauth2Login(AbstractHttpConfigurer::disable)
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers(
                                "/error",
                                "/api/v1/users/me/profile-image",
                                "/public"
                        ).permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt
                                .decoder(jwtDecoder)
                        )
                        .authenticationEntryPoint(entryPoint)
                )
                .exceptionHandling(exception -> exception
                        .authenticationEntryPoint(entryPoint)
                )
                .build();

HttpSecurity

์†์„ฑ ์„ค์ •๊ฐ’
securityMatcher "/api/**"
csrf ๋น„ํ™œ์„ฑํ™”
cors ๋น„ํ™œ์„ฑํ™”
formLogin ๋น„ํ™œ์„ฑํ™”
httpBasic ๋น„ํ™œ์„ฑํ™”
oAuthLogin ๋น„ํ™œ์„ฑํ™”
sessionManagement Stateless
permitAll "/error", "/api/v1/users/me/profile-image", "/public"
authenticated ๋‚˜๋จธ์ง€
exceptionHandling AccessTokenFailAuthenticationEntryPoint

oauth2ResourceServer

์†์„ฑ ์„ค์ •๊ฐ’
jwt.decoder jwtDecoder
authenticationEntryPoint AccessTokenFailAuthenticationEntryPoint

RefreshFilterChain

์˜์กด์„ฑ

  • RefreshFilter
  • RefreshAuthentication
  • RefreshTokenManager
  • RefreshAuthorizationProvider
  • RefreshTokenFailAuthenticationEntryPoint

์ธ์ฆ ๋กœ์ง

  • ์œ ํšจํ•œ Refresh Token์ด ์—†์œผ๋ฉด 401 (invalid_grant)
Config
var authManager = http.getSharedObject(AuthenticationManager.class);
        return http
                .securityMatchers(
                        matchers -> matchers
                                .requestMatchers(
                                        PathPatternRequestMatcher.pathPattern(HttpMethod.POST, "/api/v1/auth/user-session/refresh"),
                                        PathPatternRequestMatcher.pathPattern(HttpMethod.DELETE, "/api/v1/auth/user-session")
                                )
                )
                .csrf(AbstractHttpConfigurer::disable)
                .cors(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .httpBasic(AbstractHttpConfigurer::disable)
                .oauth2Login(AbstractHttpConfigurer::disable)
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                .authorizeHttpRequests(auth -> auth
                        .anyRequest()
                        .authenticated()
                )
                .addFilterBefore(
                        new RefreshFilter(authManager), AuthorizationFilter.class
                )
                .exceptionHandling(exception -> exception
                        .authenticationEntryPoint(entryPoint)
                )
                .build();

HttpSecurity

์†์„ฑ ์„ค์ •๊ฐ’
securityMatcher POST /api/v1/user/auth-session/refresh, DELETE /api/v1/user/auth-session
csrf ๋น„ํ™œ์„ฑํ™”
cors ๋น„ํ™œ์„ฑํ™”
formLogin ๋น„ํ™œ์„ฑํ™”
httpBasic ๋น„ํ™œ์„ฑํ™”
oAuthLogin ๋น„ํ™œ์„ฑํ™”
sessionManagement Stateless
authenticated ๋ชจ๋“  ์š”์ฒญ
exceptionHandling RefreshTokenFailAuthenticationEntryPoint

LoginFilterChain

์˜์กด์„ฑ

  • JwtDecoder

์ธ์ฆ ๋กœ์ง

  • ์œ ํšจํ•œ Access Token์ด ํ™•์ธ๋˜์ง€ ์•Š์„ ์‹œ oAuth2Login
    • ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ OAuthAccount ํ…Œ์ด๋ธ”๊ณผ ๋ฐ˜ํ™˜๋œ OIDC Id Token์„ ์ด์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋น„์Šค์˜ ๊ธฐ์กด ํšŒ์›์ธ์ง€ ๊ฒ€์ฆ
      • ๊ธฐ์กด ํšŒ์›์— ํ•ด๋‹นํ•˜์ง€ ์•Š์„ ์‹œ ํ•ด๋‹น ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‹ ๊ทœ ํšŒ์› ๋“ฑ๋ก ๋ฐ ๋กœ๊ทธ์ธ ์ ˆ์ฐจ ์ง„ํ–‰

์‹ค์ œ ๋กœ๊ทธ์ธ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. (๋”ฐ๋ผ์„œ ๋กœ๊ทธ์ธ ๊ด€๋ จ DSL์ด ์œ ์ผํ•˜๊ฒŒ ํ™œ์„ฑํ™”๋จ)

๋˜ํ•œ OAuth ๋กœ๊ทธ์ธ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ฝœ๋ฐฑ์ด ๋ถˆํ•„์š”ํ•œ ์ธ์ฆ/์ธ๊ฐ€ ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๋„๋ก ํ•œ๋‹ค.

Config
http
                .securityMatchers(
                        matchers -> matchers.requestMatchers(
                                PathPatternRequestMatcher.pathPattern(HttpMethod.POST, "/api/v1/user/auth-session"),
                                PathPatternRequestMatcher.pathPattern(HttpMethod.GET, "/oauth2/**"),
                                PathPatternRequestMatcher.pathPattern(HttpMethod.GET, "/login/oauth2/code/**")
                        )
                )
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers(
                                PathPatternRequestMatcher.pathPattern(HttpMethod.POST, "/api/v1/user/auth-session"),
                                PathPatternRequestMatcher.pathPattern(HttpMethod.GET, "/oauth2/**"),
                                PathPatternRequestMatcher.pathPattern(HttpMethod.GET, "/login/oauth2/code/**")
                        ).permitAll()
                        .anyRequest().authenticated()
                )
                .sessionManagement(session ->
                        session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
                .csrf(AbstractHttpConfigurer::disable)
                .cors(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .httpBasic(AbstractHttpConfigurer::disable)
                .oauth2Login(auth -> auth
                        .successHandler(oidcLoginSuccessHandler)
                )
                .build();

HttpSecurity

์†์„ฑ ์„ค์ •๊ฐ’
securityMatcher POST /api/v1/user/auth-session, GET /oauth2/, GET /login/oauth2/code/
csrf ๋น„ํ™œ์„ฑํ™”
cors ๋น„ํ™œ์„ฑํ™”
formLogin ๋น„ํ™œ์„ฑํ™”
httpBasic ๋น„ํ™œ์„ฑํ™”
oAuthLogin ํ™œ์„ฑํ™”
sessionManagement If Required
permitAll POST /api/v1/user/auth-session, GET /oauth2/**, GET /login/oauth2/code/**

oauth2Login

์†์„ฑ ์„ค์ •๊ฐ’
successHandler OidcLoginSuccessHandler

FallbackFilterChain

๊ธฐ๋ณธ์ ์œผ๋กœ ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฃผ์†Œ์— ๋Œ€ํ•œ ์š”์ฒญ์„ ์ฐจ๋‹จํ•˜๋ฉฐ, ๋ชจ๋‹ˆํ„ฐ๋ง์šฉ API๋“ค์€ ์„œ๋น„์Šค์˜ ํ•ต์‹ฌ API๋Š” ์•„๋‹ˆ์ง€๋งŒ ํ•„์š”ํ•˜๋ฏ€๋กœ ํ—ˆ์šฉํ•œ๋‹ค

Config
http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers(
                                EndpointRequest.to(HealthEndpoint.class)
                        ).permitAll()
                        .requestMatchers(
                                EndpointRequest.to(PrometheusScrapeEndpoint.class)
                        ).permitAll()
                        .requestMatchers("/public/**")
                        .permitAll()
                        .dispatcherTypeMatchers(DispatcherType.ERROR)
                        .permitAll()
                        .anyRequest()
                        .denyAll()
                )
                .build();

HttpSecurity

์†์„ฑ ์„ค์ •๊ฐ’
permitAll ํ—ฌ์Šค ์ฒดํฌ ์—”๋“œํฌ์ธํŠธ, ํ”„๋กœ๋ฉ”ํ…Œ์šฐ์Šค ์—”๋“œํฌ์ธํŠธ, "/public/**", ERROR Dispatcher
โš ๏ธ **GitHub.com Fallback** โš ๏ธ