반응형

스프링 부트와 OAuth2

Spring Boot And OAuth2

(원문소스: https://spring.io/guides/tutorials/spring-boot-oauth2/)

OAuth2 클라이언트의 수동설정 Manual Configuration of OAuth2 Client

이 섹션에서 우리는 @EnableOAuth2Sso 어노테이션의 '마법'으로 이미 만들어본  logout 앱을 모든 설정을 명시적으로 직접 설정하도록 수정해볼 것이다.

클라이언트와 인증 Clients and Authentication

 @EnableOAuth2Sso에는 OAuth2 클라이언트와 인증의 2가지 기능이 있다. 클라이언트는 재사용이 가능하고, 또한 당신의 인가서버Authorization Server (우리의 경우 페이스북)가 제공하는(우리의 경우 Graph API) OAuth2 리소스들과 상호작동하는데 사용할 수 있다. 이 인증 기능은 당신의 앱을 스프링시큐리티와 맞도록 맞춰준다. 일단 페이스북과 소통하는게 끝나면, 당신의 앱은 정확히 다른 보호된 스프링 앱secure Spring app과 똑같이 동작할 것이다.

클라이언트 

클라이언트 부분은 스프링 시큐리티 OAuth2에 의해 제공되며 @EnableOAuth2Client라는 다른 어노테이션을 쓴다. 따라서 이 수정의 첫 걸음은 @EnableOAuth2Sso를 삭제하고 더 낮은 레벨의 어노테이션으로 변경하는 것이다:

SocialApplication
@SpringBootApplication
@EnableOAuthClient
@RestController
public class SocialApplication extends WebSecurityConfigurerAdapter {
  ...
}

일단 이렇게 하면 우리는 우리를 위해 만들어진 유용한 몇가지 기능을 가지게 된다. 먼저 OAuth2ClientContext를 주입할 수 있어, 우리의 시큐리티 설정에 추가할 인증 필터를 만드는데 쓸 수 있다:

SocialApplication
@SpringBootApplication
@EnableOAuthClient
@RestController
public class SocialApplication extends WebSecurityConfigurerAdapter {

  @Autowired
  OAuth2ClientContext oauth2ClientContext;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/**")
      ...
      .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
  }

  ...

}

이 필터는 우리가 OAuth2ClientContext를 사용하는 새 메소드에서 생성된다:

SocialApplication
private Filter ssoFilter() {
  OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/facebook");
  OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);
  facebookFilter.setRestTemplate(facebookTemplate);
  facebookFilter.setTokenServices(new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()));
  return facebookFilter;
}

이 필터 역시 페이스북에 등록된 클라이언트 설정에 대해 알아야한다:

SocialApplication
  @Bean
  @ConfigurationProperties("facebook.client")
  OAuth2ProtectedResourceDetails facebook() {
    return new AuthorizationCodeResourceDetails();
  }

그리고 인증을 완료하려면 페이스북의 사용자 정보 종단user info endpoint이 어딘지 알아야 한다:

SocialApplication
  @Bean
  @ConfigurationProperties("facebook.resource")
  ResourceServerProperties facebookResource() {
    return new ResourceServerProperties();
  }

이 두개의 "정적인" 데이터 객체(facebook()과 facebookResource())에 우리가  @ConfigurationProperties. 로 설정된 @Bean 을 사용했다는 것을 알아두자. 이것은 우리가 application.yml를, 설정의 접두어를 security.oauth2대신 facebook로 사용하는 약간 새로운 포멧으로 전환할 수 있다는 것을 의미한다:

application.yml
facebook:
  client:
    clientId: 233668646673605
    clientSecret: 33b17e044ee6a4fa383f46ec6e28ea1d
    accessTokenUri: https://graph.facebook.com/oauth/access_token
    userAuthorizationUri: https://www.facebook.com/dialog/oauth
    tokenName: oauth_token
    authenticationScheme: query
    clientAuthenticationScheme: form
  resource:
    userInfoUri: https://graph.facebook.com/me

리다이렉트 처리하기 Handling the Redirects

마지막 수정은 우리의 앱에서 페이스북으로 리다이렉트를 명시적으로 지원하게 만들어주는 것이다. 이는 서블릿 Filter를 가진 Spring OAuth2에서 처리되어진다. 이 필터는 어플리케이션 컨텍스트application context에서 이미 사용가능한데 우리가 @EnableOAuthClient선언을 해두었기 때문이다. 우리가 이 필터를 엮어서 사용하려면 그냥 스프링부트 어플리케이션의 올바른 순서right order안에서 호출해 주기만 하면 된다. 이것을 위해 우리는 FilterRegistrationBean이 필요하다:

SocialApplication.java
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(
    OAuth2ClientContextFilter filter) {
  FilterRegistrationBean registration = new FilterRegistrationBean();
  registration.setFilter(filter);
  registration.setOrder(-100);
  return registration;
}

우리는 이미 사용가능할 필터를 Autowire해두었고, 이 필터를 메인 스프링 시큐리티 필터가 불러지기 전에 호출되도록 충분히 낮은 순서로 등록하였다. 이 방법으로 우리는 인증요청의 예외exception발생을 통해 리다이렉트를 처리할 수 있다.

이 단계까지의 수정을 통해 앱은 동작이 가능하며, 실행시 지난 섹션에서 만드 logout예제와 동일하게 동작한다. 설정을 단계별로 쪼개고 몇시적으로 우리에게 가르쳐줌으로서 스프링 부트가 해주는 마법과 같은 자동화는 더이상 없다. (이는 단지 설정의 뼈대일 뿐이다). 그리고 이는 막바로 사용가능하게 자동으로 제공되었던 기능을 확장하기 위한, 우리 자신의 의사과 비지니스 요구사항을 추가하기 위한 준비가 된다.

Github으로 로그인하기 Login with Github

이 섹션에서 우리가 이미 페이스북 링크를 통해 로그인이 가능했던 앱을 Github 인증을 추가하여 사용자가 선택할 수 있는 링크를 추가하도록 기존의 app을 수정할 것이다. 

Github 링크 추가하기 Adding the Github Link

클라이언트에서의 수정은 매우 사소하다. 단지 또다른 링크만 추가해주면 된다:

index.html
<div class="container" ng-show="!home.authenticated">
  <div>
    With Facebook: <a href="/login/facebook">click here</a>
  </div>
  <div>
    With Github: <a href="/login/github">click here</a>
  </div>
</div>

원칙적으로, 일단 우리가 인증 제공자를 추가하려면 "/user" 종단으로부터 되돌아오는 데이터에 대해 더 신중해져야 한다. Github과 페이스북 둘다 사용자 정보안에 "name"필드가 똑같이 있다. 따라서 우리의 종단에 실제로 수정을 해줄 필요가 없다.

Github 인증필터 추가하기 Adding the Github Authentication Filter

서버단의 주요 수정은 우리의 새 링크로 부터 오는 "/login/github" 요청을 처리하는 부가적인 시큐리티 필터를 추가하는 것이다. 이미 우리는 ssoFilter() 메소드에서 만들어진 페이스북을 위한 커스텀 인증 필터를 가지고 있으므로, 인증 경로를 하나이상 처리할 수 있도록 그냥 기존의 것을 Composite필터를 써서 바꿔주기만 하면 된다

SocialApplication.java
private Filter ssoFilter() {

  CompositeFilter filter = new CompositeFilter();
  List<Filter> filters = new ArrayList<>();

  OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/facebook");
  OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);
  facebookFilter.setRestTemplate(facebookTemplate);
  facebookFilter.setTokenServices(new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()));
  filters.add(facebookFilter);

  OAuth2ClientAuthenticationProcessingFilter githubFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/github");
  OAuth2RestTemplate githubTemplate = new OAuth2RestTemplate(github(), oauth2ClientContext);
  githubFilter.setRestTemplate(githubTemplate);
  githubFilter.setTokenServices(new UserInfoTokenServices(githubResource().getUserInfoUri(), github().getClientId()));
  filters.add(githubFilter);

  filter.setFilters(filters);
  return filter;

}

우리의 예전 ssoFilter()의 코드는 하나는 페이스북, 다른 하나는 Github용으로 중복된다. 이 두개의 필터는 하나의 컴포지트composite로 합쳐진다.

facebook()과 facebookResource() 메소드 역시 유사하게 github()과 githubResource()로 추가해줘야한다:

SocialApplication.java
@Bean
@ConfigurationProperties("github.client")
OAuth2ProtectedResourceDetails github() {
	return new AuthorizationCodeResourceDetails();
}

@Bean
@ConfigurationProperties("github.resource")
ResourceServerProperties githubResource() {
	return new ResourceServerProperties();
}

그리고 이에 상응하는 설정들 역시 추가해준다:

application.yml
github:
  client:
    clientId: bd1c0a783ccdd1c9b9e4
    clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
    accessTokenUri: https://github.com/login/oauth/access_token
    userAuthorizationUri: https://github.com/login/oauth/authorize
    clientAuthenticationScheme: form
  resource:
    userInfoUri: https://api.github.com/user

여기의 클라이언트 디테일들은 Github에 등록된 정보여야 하며 (페이스북과 동일하게) localhost:8080주소를 가르켜야한다.

앱은 이제 준비되었고 사용자가 페이스북 또는 Github으로 인증을 선택하도록 동작할 것이다.

로컬 사용자 데이터베이스를 추가하기 How to Add a Local User Database

많은 어플리케이션은 인증을 외부 제공자에의해 위임할 경우에도, 그들 사용자의 데이터를 로컬에서 가지고 있어야 한다. 여기에 그 코드를 보여주진 않을 것이지만 이를 쉽게 해주는 두가지 단계가 있다:

  1. 당신의 데이터베이스를 위한 백엔드를 선택한 후, 외부 인증으로부터 일부 또는 전체를 불러올 수 오는데 사용하는 커스텀 User 객체를 위한 (스프링 데이터 등을 사용하여) 리파지토리repository를 설정하라

  2. 당신의 /user 종단에서 리파지토리 검사에 의해 로그인된 각각 고유한 사용자를 위한  User 객체를 규정하자. 만일 현재의 Principal의 식별자를 가진 사용자가 이미 있다면, 업데이트하거나, 그렇지않다면 생성할 수 있다.

힌트: User 객체에 필드 하나를 추가하여 외부 제공자의 고유 식별자를 링크해주자 (사용자의 이름같은 게 아닌 무언가 고유한 것)


반응형

+ Recent posts