๐[์คํ๋ง ์ํ๋ฆฌํฐ OAuth2] OAuth 2.0 Client Fundamentals
application.yml / OAuth2ClientProperties
ํด๋ผ์ด์ธํธ ๊ถํ ๋ถ์ฌ ์์ฒญ ์์
- ํด๋ผ์ด์ธํธ๊ฐ ์ธ๊ฐ์๋ฒ๋ก ๊ถํ ๋ถ์ฌ ์์ฒญ์ ํ๊ฑฐ๋ ํ ํฐ ์์ฒญ์ ํ ๊ฒฝ์ฐ ํด๋ผ์ด์ธํธ ์ ๋ณด ๋ฐ ์๋ํฌ์ธํธ ์ ๋ณด๋ฅผ ์ฐธ์กฐํด์ ์ ๋ฌํ๋ค
- application.yml ํ๊ฒฝ์ค์ ํ์ผ์ ํด๋ผ์ด์ธํธ ์ค์ ๊ณผ ์ธ๊ฐ์๋ฒ ์๋ํฌ์ธํธ ์ค์ ์ ํ๋ค
- ์ด๊ธฐํ๊ฐ ์งํ๋๋ฉด application.yml ์ ์๋ ํด๋ผ์ด์ธํธ ๋ฐ ์๋ํฌ์ธํธ ์ ๋ณด๊ฐ OAuth2ClientProperties ์ ๊ฐ ์์ฑ์ ๋ฐ์ธ๋ฉ ๋๋ค
- OAuth2ClientProperties ์ ๋ฐ์ธ๋ฉ ๋์ด ์๋ ์์ฑ์ ๊ฐ์ ์ธ๊ฐ์๋ฒ๋ก ๊ถํ๋ถ์ฌ ์์ฒญ์ ํ๊ธฐ ์ํ ClientRegistration ํด๋์ค์ ํ๋์ ์ ์ฅ๋๋ค
- OAuth2Client ๋ ClientRegistration ๋ฅผ ์ฐธ์กฐํด์ ๊ถํ๋ถ์ฌ ์์ฒญ์ ์ํ ๋งค๊ฐ๋ณ์๋ฅผ ๊ตฌ์ฑํ๊ณ ์ธ๊ฐ์๋ฒ์ ํต์ ํ๋ค
application.yml
spring:
security:
oauth2:
client:
registration: ## ํด๋ผ์ด์ธํธ ์ค์
keycloak:
authorization-grant-type: authorization_code # OAuth 2.0 ๊ถํ ๋ถ์ฌ ํ์
client-id: oauth2-client-app # ์๋น์ค ๊ณต๊ธ์์ ๋ฑ๋ก๋ ํด๋ผ์ด์ธํธ ์์ด๋
client-name: oauth2-client-app # ํด๋ผ์ด์ธํธ ์ด๋ฆ
client-secret: tynI8eYUw4H1fJYxwLQ36XhFC1Ge1w1x # ์๋น์ค ๊ณต๊ธ์์ ๋ฑ๋ก๋ ํด๋ผ์ด์ธํธ ๋น๋น๋ฒํธ
redirect-uri: http://localhost:8081/login/oauth2/code/keycloak # ์ธ๊ฐ์๋ฒ์์ ๊ถํ ์ฝ๋ ๋ถ์ฌ ํ ํด๋ผ์ด์ธํธ๋ก ๋ฆฌ๋ค์ด๋ ํธํ๋ ์์น
clientAuthenticationMethod: client_secret_post # ํด๋ผ์ด์ธํธ ์๊ฒฉ์ฆ๋ช
์ ์ก๋ฐฉ์
scope: openid,email # ๋ฆฌ์์ค์ ์ ๊ทผ ์ ํ ๋ฒ์
provider: ## ๊ณต๊ธ์ ์ค์
keycloak:
authorization-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/auth # OAuth 2.0 ๊ถํ ์ฝ๋ ๋ถ์ฌ ์๋ ํฌ์ธํธ
issuer-uri: http://localhost:8080/realms/oauth2 # ์๋น์ค ๊ณต๊ธ์ ์์น
jwk-set-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/certs # OAuth 2.0 JwkSetUri ์๋ ํฌ์ธํธ
token-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/token # OAuth 2.0 ํ ํฐ ์๋ ํฌ์ธํธ
user-info-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/userinfo # OAuth 2.0 UserInfo ์๋ ํฌ์ธํธ
user-name-attribute: preferred_username # OAuth 2.0 ์ฌ์ฉ์๋ช
์ ์ถ์ถํ๋ ํด๋ ์๋ช
OAuth2ClientProperties (prefix = โspring.security.oauth2.clientโ)
- Registration ์ ์ธ๊ฐ ์๋ฒ์ ๋ฑ๋ก๋ ํด๋ผ์ด์ธํธ ๋ฐ ์์ฒญ ํ๋ผ๋ฏธํฐ ์ ๋ณด๋ฅผ ๋ํ๋ธ๋ค
- Provider ๋ ๊ณต๊ธ์์์ ์ ๊ณตํ๋ ์๋ํฌ์ธํธ ๋ฑ์ ์ ๋ณด๋ฅผ ๋ํ๋ธ๋ค
- ํด๋ผ์ด์ธํธ ๋ฐ ๊ณต๊ธ์์ ์ ๋ณด๋ฅผ registration / provider ๋งต์ ์ ์ฅํ๊ณ ์ธ๊ฐ์๋ฒ์์ ํต์ ์ ๊ฐ ํญ๋ชฉ์ ์ฐธ์กฐํ์ฌ ์ฌ์ฉํ๋ค
ClientRegistration
๊ฐ๋
- OAuth 2.0 ๋๋ OpenID Connect 1.0 Provider ์์ ํด๋ผ์ด์ธํธ์ ๋ฑ๋ก ์ ๋ณด๋ฅผ ๋ํ๋ธ๋ค
- ClientRegistration ์ OpenID Connect Provider์ ์ค์ ์๋ํฌ์ธํธ๋ ์ธ๊ฐ ์๋ฒ์ ๋ฉํ๋ฐ์ดํฐ ์๋ํฌ์ธํธ๋ฅผ ์ฐพ์ ์ด๊ธฐํํ ์ ์๋ค.
- ClientRegistrations์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ ์์ ์ฒ๋ผ ํธ๋ฆฌํ๊ฒ ClientRegistration ์ ์ค์ ํ ์ ์๋ค
- ClientRegistration clientRegistration = ClientRegistrations.fromIssuerLocation(โhttps://idp.example.com/issuerโ).build();
- ์ ์ฝ๋๋ 200 ์๋ต์ ๋ฐ์ ๋๊น์ง https://idp.example.com/issuer/.well-known/openid-configuration, https://idp.example.com/.well-known/oauth-authorization-server ์ ์ฐจ๋ก๋๋ก ์ง์ํด๋ณธ๋ค.
- registrationId : ClientRegistration์ ์๋ณํ ์ ์๋ ์ ๋ํฌํ ID.
- clientId : ํด๋ผ์ด์ธํธ ์๋ณ์.
- clientSecret : ํด๋ผ์ด์ธํธ secret.
- clientAuthenticationMethod : provider์์ ํด๋ผ์ด์ธํธ๋ฅผ ์ธ์ฆํ ๋ ์ฌ์ฉํ ๋ฉ์๋๋ก์ basic, post, none (public ํด๋ผ์ด์ธํธ) ์ ์ง์ํ๋ค.
- authorizationGrantType : OAuth 2.0 ์ธ๊ฐ ํ๋ ์์ํฌ๋ ๋ค ๊ฐ์ง ๊ถํ ๋ถ์ฌ ํ์ ์ ์ ์ํ๊ณ ์์ผ๋ฉฐ ์ง์ํ๋ ๊ฐ์ authorization_code, implicit, client_credentials, password๋ค.
- redirectUriTemplate : ํด๋ผ์ด์ธํธ์ ๋ฑ๋กํ ๋ฆฌ๋ค์ด๋ ํธ URL๋ก, ์ฌ์ฉ์์ ์ธ์ฆ์ผ๋ก ํด๋ผ์ด์ธํธ์ ์ ๊ทผ ๊ถํ์ ๋ถ์ฌํ๊ณ ๋๋ฉด, ์ธ๊ฐ ์๋ฒ๊ฐ ์ด URL๋ก ์ต์ข ์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ๋ฅผ ๋ฆฌ๋ค์ด๋ ํธ์ํจ๋ค.
- scopes : ์ธ๊ฐ ์์ฒญ ํ๋ก์ฐ์์ ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญํ openid, ์ด๋ฉ์ผ, ํ๋กํ ๋ฑ์ scope.
- clientName : ํด๋ผ์ด์ธํธ๋ฅผ ๋ํ๋ด๋ ์ด๋ฆ์ผ๋ก ์๋ ์์ฑ๋๋ ๋ก๊ทธ์ธ ํ์ด์ง์์ ๋ ธ์ถํ๋ ๋ฑ์ ์ฌ์ฉํ๋ค.
- authorizationUri : ์ธ๊ฐ ์๋ฒ์ ์ธ๊ฐ ์๋ํฌ์ธํธ URI.
- tokenUri : ์ธ๊ฐ ์๋ฒ์ ํ ํฐ ์๋ํฌ์ธํธ URI.
- jwkSetUri : ์ธ๊ฐ ์๋ฒ์์ JSON ์น ํค (JWK) ์ ์ ๊ฐ์ ธ์ฌ ๋ ์ฌ์ฉํ URI. ์ด ํค ์ ์ ID ํ ํฐ์ JSON Web Signature (JWS) ๋ฅผ ๊ฒ์ฆํ ๋ ์ฌ์ฉํ ์ํธํค๊ฐ ์์ผ๋ฉฐ, UserInfo ์๋ต์ ๊ฒ์ฆํ ๋๋ ์ฌ์ฉํ ์ ์๋ค.
- configurationMetadata : OpenID Provider ์ค์ ์ ๋ณด๋ก์ application.properties ์spring.security.oauth2.client.provider.[providerId].issuerUri๋ฅผ ์ค์ ํ์ ๋๋ง ์ฌ์ฉํ ์ ์๋ค.
- (userInfoEndpoint)uri : ์ธ์ฆ๋ ์ต์ข ์ฌ์ฉ์์ ํด๋ ์/์์ฑ์ ์ ๊ทผํ ๋ ์ฌ์ฉํ๋ UserInfo ์๋ํฌ์ธํธ URI.
- (userInfoEndpoint)authenticationMethod : UserInfo ์๋ํฌ์ธํธ๋ก ์ก์ธ์ค ํ ํฐ์ ์ ์กํ ๋ ์ฌ์ฉํ ์ธ์ฆ ๋ฉ์๋. header, form, query ๋ฅผ ์ง์ํ๋ค.
- userNameAttributeName : UserInfo ์๋ต์ ์๋ ์์ฑ ์ด๋ฆ์ผ๋ก, ์ต์ข ์ฌ์ฉ์์ ์ด๋ฆ์ด๋ ์๋ณ์์ ์ ๊ทผํ ๋ ์ฌ์ฉํ๋ค
CommonOAuth2Provider
- OAuth 2.0 ๊ณต๊ธ์ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ํด๋์ค๋ก์ ๊ธ๋ก๋ฒ ์๋น์ค ์ ๊ณต์ ์ผ๋ถ๋ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต๋์ด์ง๋ค
- Client ID ์ Client Secret ๋ ๋ณ๋๋ก application.properties ์ ์์ฑํด์ผ ํ๋ค.
- Naver ๋ Kakao ์ ๊ฐ์ ๊ตญ๋ด ๊ณต๊ธ์ ์ ๋ณด๋ ์์ ๋ชจ๋ ํญ๋ชฉ์ ์๋์ผ๋ก ์์ฑํด์ ์ฌ์ฉํด์ผ ํ๋ค
- ํด๋ผ์ด์ธํธ ๊ธฐ์ค์ธ Registration ํญ๋ชฉ๊ณผ ์๋น์ค ์ ๊ณต์ ๊ธฐ์ค์ธ Provider ํญ๋ชฉ์ผ๋ก ๊ตฌ๋ถํ์ฌ ์ค์ ํ๋ค
- application.properties ๊ฐ ์๋ Java Config ๋ฐฉ์์ผ๋ก ClientRegistration ๋ฑ๋ก์ ์ค์ ํ ์ ์๋ค
- ClientRegistration ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์๋ ๋น๋ ํด๋์ค๋ฅผ ๋ฐํํ๋ค
ClientRegistrationRepository
๊ฐ๋
- ClientRegistrationRepository ๋ OAuth 2.0 & OpenID Connect 1.0 ์ ClientRegistration ์ ์ฅ์ ์ญํ ์ ํ๋ค.
- ํด๋ผ์ด์ธํธ ๋ฑ๋ก ์ ๋ณด๋ ๊ถ๊ทน์ ์ผ๋ก ์ธ๊ฐ ์๋ฒ๊ฐ ์ ์ฅํ๊ณ ๊ด๋ฆฌํ๋๋ฐ ์ด ๋ ํฌ์งํ ๋ฆฌ๋ ์ธ๊ฐ ์๋ฒ์ ์ผ์ฐจ์ ์ผ๋ก ์ ์ฅ๋ ํด๋ผ์ด์ธํธ ๋ฑ๋ก ์ ๋ณด์ ์ผ๋ถ๋ฅผ ๊ฒ์ํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
- ์คํ๋ง ๋ถํธ 2.X ์๋ ์ค์ ์ spring.security.oauth2.client.registration.[registrationId] ํ์ ํ๋กํผํฐ๋ฅผ ClientRegistration ์ธ์คํด์ค์ ๋ฐ์ธ๋ฉํ๋ฉฐ, ๊ฐ ClientRegistration๊ฐ์ฒด๋ฅผ ClientRegistrationRepository ์์ ๊ตฌ์ฑํ๋ค.
- ClientRegistrationRepository ์ ๋ํดํธ ๊ตฌํ์ฒด๋ InMemoryClientRegistrationRepository ๋ค.
-
์๋ ์ค์ ์ ์ฌ์ฉํ๋ฉด ClientRegistrationRepository ๋ ApplicationContext ๋ด @Bean ์ผ๋ก ๋ฑ๋กํ๋ฏ๋ก ํ์ํ๋ค๋ฉด ์ํ๋ ๊ณณ์ ์์กด์ฑ์ ์ฃผ์ ํ ์ ์๋ค.
-
์์กด์ฑ ์ฃผ์ ์์
@RestController public class IndexPageController { @Autowired private ClientRegistrationRepository clientRegistrationRepository; @GetMapping("/") public String index() { ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("keycloak"); ... return "index"; } }
-
ClientRegistration / ClientRegistrationRepository ๋น ๋ฑ๋กํ๊ธฐ
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryClientRegistrationRepository(this. keycloakClientRegistration());
}
private ClientRegistration keycloakClientRegistration() {
return ClientRegistration.withRegistrationId("keycloak")
.clientId("keycloak-client-id")
.clientSecret(" keycloak-client-secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}")
.scope("openid", "profile", "email", "address", "phone")
.authorizationUri("http://localhost:8080/realms/oauth2")
.tokenUri("http://localhost:8080/realms/oauth2/token")
.userInfoUri("http://localhost:8080/realms/oauth2/userinfo")
.userNameAttributeName(IdTokenClaimNames.SUB)
.jwkSetUri("http://localhost:8080/realms/oauth2/certs")
.clientName(โKeycloak")
.build();
}
๋๊ธ๋จ๊ธฐ๊ธฐ