DEV Community

NeNoVen
NeNoVen

Posted on

oauth-authorization-server

1. api-client , resource server

https://www.baeldung.com/spring-security-oauth-auth-server

baeldung gihub

https://github.com/Baeldung/spring-security-oauth/tree/master/oauth-authorization-server

2. api-client server ์†Œ์Šค๋ถ„์„

2.1 yml ์„ค์ •

server:
  port: 8080

spring:
  security:
    oauth2:
      client:
        registration:
          articles-client-oidc: #ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ
            provider: spring
            client-id: articles-client
            client-secret: secret
            authorization-grant-type: authorization_code
            redirect-uri: "http://127.0.0.1:8080/login/oauth2/code/{registrationId}"
            scope: openid
            client-name: articles-client-oidc
          articles-client-authorization-code: #ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋กœ ์•ก์„ธ์Šค ํ† ํฐ ์š”์ฒญ์‹œ ์‚ฌ์šฉ
            provider: spring
            client-id: articles-client
            client-secret: secret
            authorization-grant-type: authorization_code
            redirect-uri: "http://127.0.0.1:8080/authorized"
            scope: articles.read
            client-name: articles-client-authorization-code
        provider:
          spring:
            issuer-uri: http://auth-server:9000
Enter fullscreen mode Exit fullscreen mode

2.1.1 ์š”์ฒญ ์‘๋‹ต ํ๋ฆ„ (์‹œํ€€์Šค)

  1. ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ ์š”์ฒญ: ์‚ฌ์šฉ์ž๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋กœ๊ทธ์ธ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.
  2. ๋ฆฌ๋‹ค์ด๋ ‰์…˜: ํด๋ผ์ด์–ธํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆ ์„œ๋ฒ„์˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ client-id, redirect-uri, scope ๋“ฑ์˜ ์ •๋ณด๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์ด ์ •๋ณด๋Š” registration ์„น์…˜์—์„œ ์ •์˜๋ฉ๋‹ˆ๋‹ค.
  3. ์‚ฌ์šฉ์ž ์ธ์ฆ: ์‚ฌ์šฉ์ž๋Š” ์ธ์ฆ ์„œ๋ฒ„์—์„œ ๋กœ๊ทธ์ธ์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ issuer-uri (์ธ์ฆ ์„œ๋ฒ„ ์ฃผ์†Œ)๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  4. ์ธ์ฆ ์ฝ”๋“œ ๋ฐœ๊ธ‰: ๋กœ๊ทธ์ธ์ด ์„ฑ๊ณตํ•˜๋ฉด ์ธ์ฆ ์„œ๋ฒ„๋Š” redirect-uri๋กœ ์‚ฌ์šฉ์ž๋ฅผ ๋‹ค์‹œ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ํ•˜๋ฉฐ, ์ด ๋•Œ ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ํ•จ๊ป˜ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  5. ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ํ† ํฐ์œผ๋กœ ๊ตํ™˜: ํด๋ผ์ด์–ธํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋ฐ›์€ ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฆ ์„œ๋ฒ„์— ํ† ํฐ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ client-id ๋ฐ client-secret์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  6. ํ† ํฐ ์‘๋‹ต: ์ธ์ฆ ์„œ๋ฒ„๋Š” ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์•ก์„ธ์Šค ํ† ํฐ(๋ฐ ํ•„์š”์‹œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ)์„ ๋ฐœ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.
  7. ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„ ์ ‘๊ทผ: ํด๋ผ์ด์–ธํŠธ๋Š” ๋ฐ›์€ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณดํ˜ธ๋œ ๋ฆฌ์†Œ์Šค์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, scope: articles.read๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • API์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2.1.2 ์„ค์ • ํŒŒ์ผ ๋‚ด์˜ ์—ญํ• 

  • client-id ๋ฐ client-secret: ํด๋ผ์ด์–ธํŠธ์˜ ์‹ ์›์„ ์ธ์ฆ ์„œ๋ฒ„์— ์ฆ๋ช…ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • redirect-uri: ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ ํ›„ ๋ฆฌ๋‹ค์ด๋ ‰์…˜๋  URI์ž…๋‹ˆ๋‹ค. ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ๋ฐ›๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • scope: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์˜ ๋ฒ”์œ„๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  • issuer-uri: ์ธ์ฆ ์„œ๋ฒ„์˜ ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐ ํ† ํฐ ๋ฐœ๊ธ‰์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

2.1.3 ์ƒ์„ธ์„ค๋ช…

  1. articles-client-oidc: ์ตœ์ดˆ ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ
    • ์ด ์„ค์ •์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ตœ์ดˆ๋กœ ๋กœ๊ทธ์ธํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ์ž๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋กœ๊ทธ์ธ์„ ์š”์ฒญํ•˜๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์‚ฌ์šฉ์ž๋ฅผ OAuth 2.0 ์ธ์ฆ ์„œ๋ฒ„์˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ํ•ฉ๋‹ˆ๋‹ค.
    • articles-client-oidc ์„ค์ •์— ํฌํ•จ๋œ client-id, client-secret, redirect-uri, scope ๋“ฑ์˜ ์ •๋ณด๊ฐ€ ์ด ๊ณผ์ •์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ ์„œ๋ฒ„์—์„œ ์„ฑ๊ณต์ ์œผ๋กœ ์ธ์ฆ์„ ์™„๋ฃŒํ•˜๋ฉด, ์ธ์ฆ ์„œ๋ฒ„๋Š” redirect-uri๋กœ ์‚ฌ์šฉ์ž๋ฅผ ๋ฆฌ๋””๋ ‰์…˜ํ•˜๊ณ  ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  2. articles-client-authorization-code: ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„์— ๋Œ€ํ•œ ์š”์ฒญ
    • ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ๋ฐ›์€ ํ›„, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ด ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฆ ์„œ๋ฒ„์— ์•ก์„ธ์Šค ํ† ํฐ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ articles-client-authorization-code ์„ค์ •์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • client-id, client-secret๋Š” ์ด ๊ณผ์ •์—์„œ ํ† ํฐ ๊ตํ™˜์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋ฉฐ, redirect-uri๋Š” ์ด์ „ ๋‹จ๊ณ„์—์„œ ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค.
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•ก์„ธ์Šค ํ† ํฐ์„ ๋ฐ›์œผ๋ฉด, ์ด ํ† ํฐ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณดํ˜ธ๋œ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ scope: articles.read์— ๋”ฐ๋ผ ํŠน์ • ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์•ก์„ธ์Šค ๊ถŒํ•œ์ด ๋ถ€์—ฌ๋ฉ๋‹ˆ๋‹ค.

์š”์•ฝํ•˜์ž๋ฉด, articles-client-oidc ์„ค์ •์€ ์‚ฌ์šฉ์ž ์ธ์ฆ์„ ์œ„ํ•œ ์ดˆ๊ธฐ ๋กœ๊ทธ์ธ ๊ณผ์ •์— ์‚ฌ์šฉ๋˜๋ฉฐ, articles-client-authorization-code ์„ค์ •์€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ณดํ˜ธ๋œ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์•ก์„ธ์Šค ํ† ํฐ์„ ์–ป๋Š” ๊ณผ์ •์— ์‚ฌ์šฉ

2.2 ์†Œ์Šค

@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
          .authorizeRequests(authorizeRequests ->
            authorizeRequests.anyRequest().authenticated()
          )
          .oauth2Login(oauth2Login ->
            oauth2Login.loginPage("/oauth2/authorization/articles-client-oidc"))
          .oauth2Client(withDefaults());
        return http.build();
    }
}
Enter fullscreen mode Exit fullscreen mode

oauth2Login.loginPage("/oauth2/authorization/articles-client-oidc"))

ํ•ด๋‹น ํŽ˜์ด์ง€๋Š” ํด๋ผ์ด์–ธํŠธ ์„œ๋ฒ„์—์„œ ์ œ๊ณต๋˜๋Š” ํŽ˜์ด์ง€์ด๋ฉฐ, security5์— ์ œ๊ณต๋˜๋Š” ํŽ˜์ด์ง€๋กœ ์‚ฌ์šฉ๋œ๊ฑฐ.

@RestController
public class ArticlesController {

    private WebClient webClient;

    @GetMapping(value = "/articles")
    public String[] getArticles(
      @RegisteredOAuth2AuthorizedClient("articles-client-authorization-code") OAuth2AuthorizedClient authorizedClient
    ) {
        return this.webClient
          .get()
          .uri("http://127.0.0.1:8090/articles")
          .attributes(oauth2AuthorizedClient(authorizedClient))
          .retrieve()
          .bodyToMono(String[].class)
          .block();
    }
}
Enter fullscreen mode Exit fullscreen mode

OAuth2AuthorizedClientย ํด๋ž˜์Šค ํ˜•์‹์˜ ์š”์ฒญ์—์„œ OAuth ์ธ์ฆ ํ† ํฐ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹คย .ย 

์ด๋Š” ์ ์ ˆํ•œ ์‹๋ณ„๊ณผ ํ•จ๊ป˜ย @RegisterdOAuth2AuthorizedClientย ์ฃผ์„์„ ์‚ฌ์šฉํ•˜์—ฌ Spring์— ์˜ํ•ด ์ž๋™์œผ๋กœ ๋ฐ”์ธ๋”ฉ๋ฉ๋‹ˆ๋‹ค .ย ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ ์ด์ „์— .ymlย ํŒŒ์ผ ์—์„œ ๊ตฌ์„ฑํ•œย article-client-authorizaiton-codeย ์—์„œ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค

3. ํ…Œ์ŠคํŠธ ํ˜ธ์ถœ

๋ธŒ๋ผ์šฐ์ €์—์„œ ํ˜ธ์ถœ http://127.0.0.1:8080/articles

์ธ์ฆ url ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ http://auth-server:9000/login

4. ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„

๋ณด์•ˆ๊ตฌ์„ฑ - ์ธ์ฆ์„œ๋ฒ„ ์„ค์ •

์ธ์ฆ์„œ๋ฒ„์— ์„ค์ •ํ•œ ProviderSettings ๊ฐ’์„ issuer-uri ๋กœ ์„ค์ •

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://auth-server:9000
Enter fullscreen mode Exit fullscreen mode

์›น๋ณด์•ˆ ๊ตฌ์„ฑ ์„ค์ • : ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ๊ถŒํ•œ ์„ค์ •( ์ฝ๊ธฐ, ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•œ ์Šน์ธ์š”์ฒญ)

@EnableWebSecurity
public class ResourceServerConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.mvcMatcher("/articles/**")
          .authorizeRequests() //1.์š”์ฒญ์— ๋Œ€ํ•œ ์ธ์ฆ์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ๋Š” ์ดํ›„์— ์ •์˜๋  ์ธ์ฆ ๊ทœ์น™๋“ค์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค
          .mvcMatchers("/articles/**")//2.ํŠน์ • ๊ฒฝ๋กœ(/articles/**)์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ๊ทœ์น™์„ ์ •์˜. 
                                                                            // ํ•ด๋‹น ๊ฒฝ๋กœ์— ๋Œ€ํ•œ ๋ณด์•ˆ ๊ทœ์น™์„ ์„ธ๋ถ„ํ™”
          .access("hasAuthority('SCOPE_articles.read')")//3./articles/** ๊ฒฝ๋กœ์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ œ์–ด
                    // 'SCOPE_articles.read' ๊ถŒํ•œ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ๋งŒ ํ•ด๋‹น ๊ฒฝ๋กœ์— ์ ‘๊ทผ ๊ฐ€๋Šฅ
                    // ์ด ๊ถŒํ•œ์€ ์ผ๋ฐ˜์ ์œผ๋กœ OAuth 2.0 ํ† ํฐ์— ์ •์˜๋œ ์Šค์ฝ”ํ”„์—์„œ ํŒŒ์ƒ๋ฉ๋‹ˆ๋‹ค.
          .and()
          .oauth2ResourceServer() //3. OAuth 2.0 ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ  JWT (JSON Web Token)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฆ์„ ์ˆ˜ํ–‰
          .jwt(); //  ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์ด ์˜ฌ๋ฐ”๋ฅธ JWT๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”์ง€ ๊ฒ€์ฆํ•˜์—ฌ ํ•ด๋‹น ์š”์ฒญ์ด ์œ ํšจํ•œ์ง€ ํ™•์ธ
        return http.build();
    }
}
Enter fullscreen mode Exit fullscreen mode

5. ์ธ์ฆ์„œ๋ฒ„

@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
          .clientId("articles-client")
          .clientSecret("{noop}secret")
          .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
          .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
          .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
          .redirectUri("http://127.0.0.1:8080/login/oauth2/code/articles-client-oidc")
          .redirectUri("http://127.0.0.1:8080/authorized")
          .scope(OidcScopes.OPENID)
          .scope("articles.read")
          .build();
        return new InMemoryRegisteredClientRepository(registeredClient);
    }
}
Enter fullscreen mode Exit fullscreen mode
  • RegisteredClient.withId(UUID.randomUUID().toString()): ํด๋ผ์ด์–ธํŠธ ID๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๊ณ ์œ ํ•œ UUID๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • .clientId("articles-client"): OAuth ํด๋ผ์ด์–ธํŠธ์˜ ์‹๋ณ„์ž๋กœ "articles-client"๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. Spring์€ ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์†Œ์Šค์— ์•ก์„ธ์Šคํ•˜๋ ค๋Š” ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค.
  • .clientSecret("{noop}secret"): ํด๋ผ์ด์–ธํŠธ์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. {noop}์€ ํŒจ์Šค์›Œ๋“œ ์ธ์ฝ”๋”๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC): ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ ๋ฐฉ์‹์„ CLIENT_SECRET_BASIC์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํด๋ผ์ด์–ธํŠธ ID์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฆ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
  • .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) (์ธ์ฆ ๋ถ€์—ฌ ์œ ํ˜•): ์ธ์ฆ ์ฝ”๋“œ ๋ถ€์—ฌ ์œ ํ˜•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” OAuth 2.0์˜ ํ‘œ์ค€ ํ”Œ๋กœ์šฐ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.
  • .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) (์ธ์ฆ ๋ถ€์—ฌ ์œ ํ˜•): ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๋ถ€์—ฌ ์œ ํ˜•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ๋Š” ์•ก์„ธ์Šค ํ† ํฐ์„ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • .redirectUri("http://127.0.0.1:8080/login/oauth2/code/articles-client-oidc"): OAuth ์ธ์ฆ ํ›„ ์‚ฌ์šฉ์ž๋ฅผ ๋ฆฌ๋””๋ ‰์…˜ํ•  URI๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • .redirectUri("http://127.0.0.1:8080/authorized"): ์ถ”๊ฐ€์ ์ธ ๋ฆฌ๋””๋ ‰์…˜ URI๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • .scope(OidcScopes.OPENID): OpenID Connect ์Šค์ฝ”ํ”„๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • .scope("articles.read"): ์‚ฌ์šฉ์ž ์ •์˜ ์Šค์ฝ”ํ”„์ธ "articles.read"๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ์Šค์ฝ”ํ”„๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํŠน์ • ์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • .build(): RegisteredClient ๊ตฌ์„ฑ์„ ์™„์„ฑํ•ฉ๋‹ˆ๋‹ค.

5.1 ์ƒ์„ธ

.redirectUri ์„ค์ •์— ๋Œ€ํ•ด ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ OAuth 2.0 ์ธ์ฆ ํ”„๋กœ์„ธ์Šค์—์„œ ๋งค์šฐ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

5.1.1. ์ธ์ฆ ์ฝ”๋“œ ํ๋ฆ„ (Authorization Code Flow):
- .redirectUri("http://127.0.0.1:8080/login/oauth2/code/articles-client-oidc"): ์ด ๋ฆฌ๋””๋ ‰์…˜ URI๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ธ์ฆ ์ฝ”๋“œ ํ๋ฆ„์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ OAuth ์ธ์ฆ์„ ์‹œ์ž‘ํ•˜๋ฉด, ์‚ฌ์šฉ์ž๋Š” ์ด URI๋กœ ๋ฆฌ๋””๋ ‰์…˜๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉ์ž๋Š” ๋กœ๊ทธ์ธํ•˜๊ณ  ๋™์˜๋ฅผ ์ œ๊ณตํ•œ ํ›„, ์ธ์ฆ ์„œ๋ฒ„๋Š” ์ด URI๋กœ ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ๋ฆฌ๋””๋ ‰์…˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ธ์ฆ ์ฝ”๋“œ๋Š” ๋‚˜์ค‘์— ์•ก์„ธ์Šค ํ† ํฐ์„ ์–ป๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
5.1.2. ์•ก์„ธ์Šค ํ† ํฐ ์š”์ฒญ:
- .redirectUri("http://127.0.0.1:8080/authorized"): ์ด URI๋Š” ์•ก์„ธ์Šค ํ† ํฐ์„ ๋ฐ›๊ธฐ ์œ„ํ•œ ์ถ”๊ฐ€์ ์ธ ๋ฆฌ๋””๋ ‰์…˜ ํฌ์ธํŠธ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ก์„ธ์Šค ํ† ํฐ์„ ์š”์ฒญํ•  ๋•Œ, ์ธ์ฆ ์„œ๋ฒ„๋Š” ์ด URI๋กœ ์‚ฌ์šฉ์ž๋ฅผ ๋ฆฌ๋””๋ ‰์…˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฆฌ๋””๋ ‰์…˜ URI์˜ ์ˆœ์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ค‘์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ค‘์š”ํ•œ ๊ฒƒ์€ ํด๋ผ์ด์–ธํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ด URI๋“ค ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฆ ์„œ๋ฒ„์— ๋ฆฌ๋””๋ ‰์…˜์„ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ธ์ฆ ์š”์ฒญ ์‹œ์— ์‚ฌ์šฉํ•˜๋Š” redirect_uri ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’์€ ๋“ฑ๋ก๋œ ๋ฆฌ๋””๋ ‰์…˜ URI ์ค‘ ํ•˜๋‚˜์™€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์š”์•ฝํ•˜์ž๋ฉด, .redirectUri("http://127.0.0.1:8080/login/oauth2/code/articles-client-oidc")๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•˜๊ณ  ์ธ์ฆ ์„œ๋ฒ„๊ฐ€ ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ, .redirectUri("http://127.0.0.1:8080/authorized")๋Š” ์ถ”๊ฐ€์ ์ธ ๋ฆฌ๋””๋ ‰์…˜ ์˜ต์…˜์œผ๋กœ, ํŠน์ • ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋‘ URI๋Š” ์ธ์ฆ ํ”„๋กœ์„ธ์Šค์˜ ๋‹ค๋ฅธ ๋‹จ๊ณ„์—์„œ ์‚ฌ์šฉ๋˜๋ฉฐ, ๊ทธ ์ˆœ์„œ๋Š” ํŠน๋ณ„ํ•œ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

5.2 ๊ตฌ์„ฑ

๊ธฐ๋ณธ OAuth ๋ณด์•ˆ์„ ์ ์šฉํ•˜๊ณ  ๊ธฐ๋ณธ ์–‘์‹ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก Bean์„ ๊ตฌ์„ฑ

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    return http.formLogin(Customizer.withDefaults()).build();
}
Enter fullscreen mode Exit fullscreen mode

๊ฐ ์ธ์ฆ ์„œ๋ฒ„์—๋Š” ๋ณด์•ˆ ๋„๋ฉ”์ธ ๊ฐ„์˜ ์ ์ ˆํ•œ ๊ฒฝ๊ณ„๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํ† ํฐ์— ๋Œ€ํ•œ ์„œ๋ช… ํ‚ค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.ย 2048๋ฐ”์ดํŠธ RSA ํ‚ค๋ฅผ ์ƒ์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@Bean
public JWKSource<SecurityContext> jwkSource() {
    RSAKey rsaKey = generateRsa();
    JWKSet jwkSet = new JWKSet(rsaKey);
    return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}

private static RSAKey generateRsa() {
    KeyPair keyPair = generateRsaKey();
    RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
    RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
    return new RSAKey.Builder(publicKey)
      .privateKey(privateKey)
      .keyID(UUID.randomUUID().toString())
      .build();
}

private static KeyPair generateRsaKey() {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);
    return keyPairGenerator.generateKeyPair();
}
Enter fullscreen mode Exit fullscreen mode

์„œ๋ช… ํ‚ค๋ฅผ ์ œ์™ธํ•˜๊ณ  ๊ฐ ์ธ์ฆ ์„œ๋ฒ„์—๋Š” ๊ณ ์œ ํ•œ ๋ฐœ๊ธ‰์ž URL๋„ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย ProviderSettingsย ๋นˆ์„ ์ƒ์„ฑํ•˜์—ฌย ํฌํŠธย 9000 ์—์„œย http://auth-serverย ์— ๋Œ€ํ•œ localhost ๋ณ„์นญ์œผ๋กœ ์„ค์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹คย .

@Bean
public ProviderSettings providerSettings() {
    return ProviderSettings.builder()
      .issuer("http://auth-server:9000")
      .build();
}
Enter fullscreen mode Exit fullscreen mode

๋˜ํ•œย /etc/hosts ํŒŒ์ผ์— "ย 127.0.0.1 auth-serverย " ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹คย .ย ์ด๋ฅผ ํ†ตํ•ด ๋กœ์ปฌ ์ปดํ“จํ„ฐ์—์„œ ํด๋ผ์ด์–ธํŠธ์™€ ์ธ์ฆ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‘˜ ์‚ฌ์ด์˜ ์„ธ์…˜ ์ฟ ํ‚ค ๋ฎ์–ด์“ฐ๊ธฐ ๋ฌธ์ œ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ @EnableWebSecurityย ์ฃผ์„์ด ๋‹ฌ๋ฆฐ ๊ตฌ์„ฑ ํด๋ž˜์Šค ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Spring ์›น ๋ณด์•ˆ ๋ชจ๋“ˆ์„ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค

@EnableWebSecurity
public class DefaultSecurityConfig {

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests(authorizeRequests ->
          authorizeRequests.anyRequest().authenticated()
        )
          .formLogin(withDefaults());
        return http.build();
    }

    // ...
}
Enter fullscreen mode Exit fullscreen mode
  • ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•ด ์ธ์ฆ์„ ์š”๊ตฌํ•˜๊ธฐ ์œ„ํ•ดย AuthorizeRequests.anyRequest().authenticated()๋ฅผย ํ˜ธ์ถœ
  • formLogin(defaults())ย ๋ฉ”์„œ๋“œ ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์–‘์‹ ๊ธฐ๋ฐ˜ ์ธ์ฆ์„ ์ œ๊ณตํ•˜๊ณ 

ํ…Œ์ŠคํŠธ์— ์‚ฌ์šฉํ•  ์˜ˆ์ œ ์‚ฌ์šฉ์ž ์ง‘ํ•ฉ์„ ์ •์˜

@Bean
UserDetailsService users() {
    UserDetails user = User.withDefaultPasswordEncoder()
      .username("admin")
      .password("password")
      .build();
    return new InMemoryUserDetailsManager(user);
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)