v2_techspec_auth - 100-hours-a-week/KTB4-8th-BE GitHub Wiki
- ApiFilterChain
- RefreshFilterChain
- LoginFilterChain
- FallbackFilterChain
์์กด์ฑ
JwtDecoderAccessTokenFailAuthenticationEntryPoint
์ธ์ฆ ๋ก์ง
- ์ ํจํ 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 |
์์กด์ฑ
RefreshFilterRefreshAuthenticationRefreshTokenManagerRefreshAuthorizationProviderRefreshTokenFailAuthenticationEntryPoint
์ธ์ฆ ๋ก์ง
- ์ ํจํ 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 |
์์กด์ฑ
JwtDecoder
์ธ์ฆ ๋ก์ง
- ์ ํจํ Access Token์ด ํ์ธ๋์ง ์์ ์ oAuth2Login
- ๋ก๊ทธ์ธ ์ฑ๊ณต ์ OAuthAccount ํ
์ด๋ธ๊ณผ ๋ฐํ๋ OIDC Id Token์ ์ด์ฉํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์๊ฐ ์๋น์ค์ ๊ธฐ์กด ํ์์ธ์ง ๊ฒ์ฆ
- ๊ธฐ์กด ํ์์ ํด๋นํ์ง ์์ ์ ํด๋น ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ์ ๊ท ํ์ ๋ฑ๋ก ๋ฐ ๋ก๊ทธ์ธ ์ ์ฐจ ์งํ
- ๋ก๊ทธ์ธ ์ฑ๊ณต ์ 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 |
๊ธฐ๋ณธ์ ์ผ๋ก ์ ํจํ์ง ์์ ์ฃผ์์ ๋ํ ์์ฒญ์ ์ฐจ๋จํ๋ฉฐ, ๋ชจ๋ํฐ๋ง์ฉ 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 |