1. 개요

이 예제에서는 Spring Boot 및 Spring Security OAuth를 사용하여 사용자 인증을 사용자 정의 인증 서버뿐만 아니라 제3자에게 위임하는 애플리케이션을 작성하는 방법을 설명합니다.

또한  Spring의  PrincipalExtractor 및  AuthoritiesExtractor  인터페이스 를 사용하여 Principal  과  Authorities 를 모두 추출하는 방법을 보여줍니다  .

Spring Security OAuth2에 대한 소개는 문서를 참조하십시오.

2. 메이븐 의존성

시작하려면  pom.xml 에 spring-security-oauth2-autoconfigure  의존성을 추가해야 합니다 .

<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.6.8</version>
</dependency>

3. Github을 이용한 OAuth 인증

다음으로 애플리케이션의 Security 구성을 생성해 보겠습니다.

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.antMatcher("/**")
            .authorizeRequests()
            .antMatchers("/login**")
            .permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .formLogin()
            .disable()
            .oauth2Login();
        return http.build();
    }
}

간단히 말해서 누구나 /login  엔드포인트에 액세스할 수 있으며 다른 모든 엔드포인트에는 사용자 인증이 필요합니다.

하나 이상의 클라이언트에 대해 아래 속성을 추가 하면 필요한 모든 빈을 설정 하는 Oauth2ClientAutoConfiguration  클래스 가 활성화됩니다.

spring.security.oauth2.client.registration.github.client-id=89a7c4facbb3434d599d
spring.security.oauth2.client.registration.github.client-secret= 
    9b3b08e4a340bd20e866787e4645b54f73d74b6a
spring.security.oauth2.client.registration.github.scope=read:user,user:email

spring.security.oauth2.client.provider.github.token-uri=
    https://github.com/login/oauth/access_token
spring.security.oauth2.client.provider.github.authorization-uri=
    https://github.com/login/oauth/authorize
spring.security.oauth2.client.provider.github.user-info-uri=https://api.github.com/user

사용자 계정 관리를 처리하는 대신 제3자(이 경우 Github)에 위임하므로 애플리케이션의 논리에 집중할 수 있습니다.

4. 주체 및 권한 추출

OAuth 클라이언트 역할을 하고 타사를 통해 사용자를 인증할 때 고려해야 할 세 가지 단계가 있습니다.

  1. 사용자 인증 – 사용자가 타사로 인증
  2. 사용자 권한 부여 – 인증 후, 사용자가 애플리케이션이 자신을 대신하여 특정 작업을 수행하도록 허용할 때입니다. 이것은  범위 가 들어오는 곳입니다.
  3. 사용자 데이터 가져오기 – 얻은 OAuth 토큰을 사용하여 사용자 데이터 검색

사용자 데이터를 검색하면 Spring은 자동으로 사용자의  Principal 및  Authorities 를 생성할 수 있습니다 .

그것이 받아들여질 수 있지만, 종종 우리는 그것들을 완전히 통제하기를 원하는 시나리오에 처하게 됩니다.

그렇게 하기 위해 Spring은 기본 동작을 재정의하는 데 사용할 수 있는 두 가지 인터페이스를 제공합니다 .

  • PrincipalExtractor – 사용자 지정 논리를 제공하여 Principal 을 추출하는 데 사용할 수 있는 인터페이스 
  • AuthoritiesExtractor  –  PrincipalExtractor 와 유사하지만 대신 Authorities 추출 을 사용자 지정하는 데 사용  됩니다.

기본적으로 Spring은   이러한 인터페이스를 구현하고 이를 생성하기 위해 미리 정의된 전략이 있는 두 가지 구성 요소( FixedPrincipalExtractor 및  FixedAuthoritiesExtractor ) 를 제공합니다.

4.1. Github의 인증 사용자 지정

우리의 경우 Github의 사용자 데이터가 어떻게 생겼는지, 필요에 따라 데이터를 조정하기 위해 무엇을 사용할 수 있는지 알고 있습니다 .

따라서 Spring의 기본 구성 요소를 재정의하려면 이러한 인터페이스도 구현하는 두 개의 Bean 을 생성하기만 하면 됩니다.

애플리케이션의  Principal  에 대해 사용자의 Github 사용자 이름을 사용하기만 하면 됩니다.

public class GithubPrincipalExtractor 
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map<String, Object> map) {
        return map.get("login");
    }
}

사용자의 Github 구독(무료 또는 그렇지 않은 경우)에 따라  GITHUB_USER_SUBSCRIBED 또는  GITHUB_USER_FREE 권한을 부여합니다.

public class GithubAuthoritiesExtractor 
  implements AuthoritiesExtractor {
    List<GrantedAuthority> GITHUB_FREE_AUTHORITIES
     = AuthorityUtils.commaSeparatedStringToAuthorityList(
     "GITHUB_USER,GITHUB_USER_FREE");
    List<GrantedAuthority> GITHUB_SUBSCRIBED_AUTHORITIES 
     = AuthorityUtils.commaSeparatedStringToAuthorityList(
     "GITHUB_USER,GITHUB_USER_SUBSCRIBED");

    @Override
    public List<GrantedAuthority> extractAuthorities
      (Map<String, Object> map) {
 
        if (Objects.nonNull(map.get("plan"))) {
            if (!((LinkedHashMap) map.get("plan"))
              .get("name")
              .equals("free")) {
                return GITHUB_SUBSCRIBED_AUTHORITIES;
            }
        }
        return GITHUB_FREE_AUTHORITIES;
    }
}

그런 다음 다음 클래스를 사용하여 빈을 생성해야 합니다.

@Configuration
public class SecurityConfig {
    
    // ...

    @Bean
    public PrincipalExtractor githubPrincipalExtractor() {
        return new GithubPrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor githubAuthoritiesExtractor() {
        return new GithubAuthoritiesExtractor();
    }
}

4.2. 사용자 지정 권한 부여 서버 사용

제3자에 의존하는 대신 사용자를 위해 자체 인증 서버를 사용할 수도 있습니다.

사용하기로 결정한 인증 서버에도 불구하고 Principal과 Authorities를 모두 사용자 지정해야 하는 구성 요소는  PrincipalExtractor AuthoritiesExtractor 와  같이 동일  하게 유지  됩니다.

user-info-uri 끝점 에서 반환된 데이터를 인식하고  적절 하다고 판단되는 대로 사용하기만 하면 됩니다.

기사 에 설명된 인증 서버를 사용하여 사용자를 인증하도록 애플리케이션을 변경해 보겠습니다 .

spring.security.oauth2.client.registration.baeldung.client-id=SampleClientId
spring.security.oauth2.client.registration.baeldung.client-secret=secret

spring.security.oauth2.client.provider.baeldung.token-uri=http://localhost:8081/auth/oauth/token
spring.security.oauth2.client.provider.baeldung.authorization-uri=
    http://localhost:8081/auth/oauth/authorize
spring.security.oauth2.client.provider.baeldung.user-info-uri=http://localhost:8081/auth/user/me

이제 인증 서버를 가리키고 있으므로 두 추출기를 모두 만들어야 합니다. 이 경우  PrincipalExtractor 는 이름를 사용하여  맵에서  Principal 추출합니다  .

public class BaeldungPrincipalExtractor 
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map<String, Object> map) {
        return map.get("name");
    }
}

권한에 관해서는 Authorization Server가 이미  user-info-uri 의 데이터에 이를 배치하고 있습니다.

따라서 우리는 그것들을 추출하고 강화할 것입니다:

public class BaeldungAuthoritiesExtractor 
  implements AuthoritiesExtractor {

    @Override
    public List<GrantedAuthority> extractAuthorities
      (Map<String, Object> map) {
        return AuthorityUtils
          .commaSeparatedStringToAuthorityList(asAuthorities(map));
    }

    private String asAuthorities(Map<String, Object> map) {
        List<String> authorities = new ArrayList<>();
        authorities.add("BAELDUNG_USER");
        List<LinkedHashMap<String, String>> authz = 
          (List<LinkedHashMap<String, String>>) map.get("authorities");
        for (LinkedHashMap<String, String> entry : authz) {
            authorities.add(entry.get("authority"));
        }
        return String.join(",", authorities);
    }
}

그런 다음 SecurityConfig 클래스 에 빈을 추가합니다  .

@Configuration
public class SecurityConfig {

    // ...

    @Bean
    public PrincipalExtractor baeldungPrincipalExtractor() {
        return new BaeldungPrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor baeldungAuthoritiesExtractor() {
        return new BaeldungAuthoritiesExtractor();
    }
}

5. 결론

이 기사에서는 사용자 인증을 사용자 지정 권한 부여 서버뿐만 아니라 제3자에게 위임하는 애플리케이션을 구현하고  Principal 및  Authorities 모두를 사용자 지정하는 방법을 보여주었습니다 .

평소와 같이 이 예제의 구현은 Github 에서 찾을 수 있습니다 .

로컬에서 실행할 때  localhost:8082 에서 애플리케이션을 실행하고 테스트할 수 있습니다.

Security footer banner