brunch

You can make anything
by writing

C.S.Lewis

by 이수홍 Jan 29. 2017

Spring Boot로 만드는 OAuth2 시스템 8

OAuth2 서버를 커스터마이징 해보자(클라이언트 관리 편)

이제 까지 TokenStore를 제외하고 최소한의 설정만 하는 형태를 구현해 보았다.

하지만 실제 서비스에서 사용하기 위해서는 여러 가지 확장 형태를 고려해야 한다. 그중 클라이언트 관리하는 부분에 대해서 알아보자. 


앞선 챕터에서 설명한 형태로 구현했을 때에는 모든 권한을 가지고 있는 클라이언트를 하나만 사용한다고 가정하고 테스트했었다. ( foo:bar )

하지만 실제 서비스에서는 이렇게 사용할 일은 없다는 것은 많은 감(?)으로라도 느껴질 것이다.

심지어 현재 페이스북 및 구글 등에서 제공하는 클라이언트 형태는 사용자가 직접 생성할 수 있게 되어 있다.

기본적으로 "스프링 시큐리티 OAuth2"에서는 직접 기술할 수 있도록 하는 방법DB로 관리할 수 있는 클래스를 제공하고 있다.


직접 기술하는 방법

챕터 7에서 설명 소스를 기준으로 해서 추가하는 형태를 설명하겠다.

oauth2 프로젝트에서 JwtOAuth2AuthorizationServerConfiguration 클래스를 설정하는 부분이 있다. 여기서 아래와 같이 메서드를 재정의 한다.

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
        // 클라이언트 아이디
    .withClient("my_client_id")
        // 클라이언트 시크릿
        .secret("my_client_secret")
        // 액세스 토큰 발급 가능한 인증 타입
        //기본이 다섯 개, 여기 속성이 없으면 인증 불가
        .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
        // 클라이언트에 부여된 권한
        .authorities("ROLE_MY_CLIENT")
        // 이 클라이언트로 접근할 수 있는 범위 제한
        // 해당 클라이언트로 API를 접근했을 때 접근 범위를 제한시키는 속성
        .scopes("read", "write")
        // 이 클라이언트로 발급된 액세스 토큰의 시간 (단위:초)
        .accessTokenValiditySeconds(60 * 60 * 4) // default value 60 * 60 * 12
        // 이 클라이언트로 발급된 리프 러시 토큰의 시간 (단위:초)
        .refreshTokenValiditySeconds(60 * 60 * 24 * 120) // default value 60 * 60 * 24 * 30
    .and()
    .withClient("your_client_id")
        .secret("your_client_secret")
        .authorizedGrantTypes("authorization_code", "implicit")
        .authorities("ROLE_YOUR_CLIENT")
        .scopes("read")
    .and()
// ... 계속 추가 가능
}

위와 같이 직접 소스에 기술하여 사용하면 된다. 

즉 앞선 챕터에서 속성을 추가하여 생성한 foo:bar 클라이언트는 스프링 부트에서 위와 같은 형태로 생성시켜준 단순 클라이언트였던 것이다. 

참고로 이와 같이 생성해주는 부분은 스프링 시큐리티 OAuth2에서 해주는 것이 아니라 스프링 부트에서 우리가 쉽게 사용할 수 있도록 필요한 속성만 기술하면 알아서 기본 속성을 추가시켜 생성시켜주는 도우미 역할을 해주고 있었던 것이다.

(위 역할을 하는 클래스 OAuth2AuthorizationServerConfiguration 제일 하단의 소스 참고)


DB에서 관리하는 방법

위와 같이 소스에서 관리하는 방법은 제한된 형태의 작은 서비스에서는 쓸만하지만 클라이언트가 많이 필요하거나 규모가 큰 서비스에서 이용하기에는 부담이 있다. (클라이언트 추가하기 위해서 소스 수정하고 서버 리스타트가 필요하다!)

그래서 DB에서 관리하는 형태도 확인해둘 필요가 있을 것이다. 

기본적으로 스프링 시큐리티 OAuth2에서 jdbc를 직접 지원하고 있는 클래스를 제공한다.

챕터 5에서 토큰 스토어 확장에 사용했었던 기본 제공 스키마를 다시 살펴보자. 여기에 클라이언트 관리할 수 있는 스키마도 제공한다. 

create table oauth_client_details (
  client_id VARCHAR(256) PRIMARY KEY,
  resource_ids VARCHAR(256),
  client_secret VARCHAR(256),
  scope VARCHAR(256),
  authorized_grant_types VARCHAR(256),
  web_server_redirect_uri VARCHAR(256),
  authorities VARCHAR(256),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additional_information VARCHAR(4096),
  autoapprove VARCHAR(256)
);

resources폴더에 data.sql이라는 파일을 만들어서 아래와 같은 쿼리를 입력해보자.

insert into oauth_client_details (client_id, client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove) 
values ('my_client_id', 'my_client_secret', null, 'read,write', 'authorization_code,password,client_credentials,implicit,refresh_token', null, 'ROLE_MY_CLIENT', 36000, 2592000, null, null);

insert into oauth_client_details (client_id, client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove)
values ('your_client_id', 'your_client_secret', null, 'read', 'authorization_code,implicit', null, 'ROLE_YOUR_CLIENT', 36000, 2592000, null, null);

서버를 재시작 후 h2콘솔을 띄어보자. ( h2 콘솔을 여기 참고: https://brunch.co.kr/@sbcoba/5 )

위처럼 oauth_client_details 테이블을 조회하게 되면 아래처럼 데이터가 들어가게 된다.

h2콘솔 화면

데이터가 들어간 형태를 확인 후 클라이언트 설정을 DB로 바꿔 보자.

@Bean
@Primary
public JdbcClientDetailsService jdbcClientDetailsService(DataSource dataSource) {
    return new JdbcClientDetailsService(dataSource);
}

// ...
@Autowired
ClientDetailsService clientDetailsService;

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.withClientDetails(clientDetailsService);
}

위에서 직접 기술한 형태보다 설정 형태가 단순 해졌다. 

클라이언트 정보 자체를 소스에 기술하는 형태가 아니라 DB에 있는 정보를 가져와서 사용하는 방법으로 바뀌었기 때문이다.

기본적으로 스프링 시큐리티 OAuth2에서 지원하는 JdbcClientDetailsService 클래스는 DataSource만 연결해주면 위의 스키마 형태를 사용해서 클라이언트 정보를 관리한다.


스프링 시큐리티 OAuth2에서 기본적으로 클라이언트를 관리할 수 있는 속성에 대해서 간단히 알아보자.

Client Id: 클라이언트를 구별하는 ID, 구현하는 곳마다 다르나 직접 의미 있는 문자열이나 UUID 같은 랜덤 값으로 발급하기도 한다.
(직접 기술:. withClient(...), DB: client_id 칼럼)

Client Secret : 클라이언트의 비밀번호, 해당 정보로 OAuth2 서버에 접속할 때 체크한다. 유출되면 안 되는 정보. 회원으로 치면 패스워드에 가깝다.
(직접 기술:. secret(...), DB: client_secret 칼럼)

Authorized Grant Types: 액세스 토큰 받는 방법에 대한 권한 부여, 기본적으로 다섯 가지 방법을 제공하며 이 속성에 적힌 방법으로만 액세스 토큰을 발급받을 수 있다. 
(직접 기술:. authorizedGrantTypes("...", "...", ...), DB: authorized_grant_types 칼럼, 쉼표로 구분)

- 참고 
authorization_code: 권한 코드 방식
password: 사용자 소유자 비밀번호 방식
client_credentials: 클라이언트 인증 방식
implicit: 암묵적인 동의 방식
refresh_token: 리프 러시 토큰을 통한 발급 방식

5가지 방법에 대한 자세한 정보는 https://brunch.co.kr/@sbcoba/4에서 확인할 수 있다.

Access Token Validity: 해당 클라이언트로 발급될 액세스 토큰 유효 시간 (기본값: 12시간)
(직접 기술:accessTokenValiditySeconds(...), DB: access_token_validity 칼럼 )

Refresh Token Validity: 해당 클라이언트로 발급될 리프 러시 토큰 유효 시간 (기본값: 30일)
(직접 기술:refreshTokenValiditySeconds(...), DB: refresh_token_validity 칼럼)

authorities: 클라이언트의 권한 부여 ( 클라이언트 인증방식 때 해당 권한을 사용)
(직접 기술:authorities("...", "...",...) DB: authorities 칼럼, 쉼표로 구분)

scope: 해당 클라이언트로 발급될 액세스 토큰의 권한 범위, 예를 들면 이 클라이언트로 API를 접근하면 특정 API에 대해 접근이 가능 여부 제어를 물을 때 사용된다. scope에 대한 이야기는 앞으로 좀 더 자세히 다룰 예정이다.
(직접 기술:. scopes("...", "...",...) DB: scope 칼럼, 쉼표로 구분)

기본으로 제공되는 scope에 대한 허가 화면 

auto approve: 기본적으로 권한 코드 방식 같은 형태로 액세스 토큰을 발급받을 때에는 사용자에게 위와 같은 클라이언트가 가지고 있는 scope에 대해서 허가를 받도록 하는 화면이 나온다. 그런데 이 화면 자체가 나오지 않게 하기 위한 속성으로 true가 설정되면 위 화면이 나오지 않는다.
(직접 기술:. autoApprove(true), DB: autoapprove 칼럼, true or false 기본값은 false)

전체 소스는 https://github.com/sbcoba/spring-boot-oauth2-sample/tree/example8 여기서 확인 가능하다.


다음 포스팅: 9. OAuth2 시스템에서 Scope를 이용한 API 권한 제어
이전 포스팅: 7. JWT 방식으로 바꿔 보자

1. 스프링 부트와 OAuth2 (https://brunch.co.kr/@sbcoba/1 )  
2. 본격적인 개발 하기 전에 (https://brunch.co.kr/@sbcoba/2 )
3. API 서버 만들기 (https://brunch.co.kr/@sbcoba/3 )  
4. 간단한 OAuth2 서버 만들어 보기 (https://brunch.co.kr/@sbcoba/4 )  
5. OAuth2 서버를 커스터마이징 해보자(TokenStore 편) (https://brunch.co.kr/@sbcoba/5 )  
6. API 서버와 OAuth2 서버를 분리 (https://brunch.co.kr/@sbcoba/6 )
7. JWT 방식으로 바꿔 보자 (https://brunch.co.kr/@sbcoba/7 )
8. OAuth2 서버를 커스터마이징 해보자(클라이언트 관리 편) (https://brunch.co.kr/@sbcoba/8 )
9. OAuth2 시스템에서 Scope를 이용한 API 권한 제어 (https://brunch.co.kr/@sbcoba/15 )
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari