1. 概要

このチュートリアルでは、SpringセキュリティOAuthとSpring Bootを使用してSSO(シングルサインオン)を実装する方法について説明します。

3つの別々のアプリケーションを使用します。

  • 承認サーバー–これは中央の認証メカニズムです
  • 2つのクライアントアプリケーション:SSOを使用するアプリケーション

非常に簡単に言えば、ユーザーがクライアントアプリで保護されたページにアクセスしようとすると、認証サーバーを介して最初に認証するようにリダイレクトされます。

そして、OAuth2から認証コード付与タイプを使用して、認証の委任を駆動します。

:この記事ではSpringOAuthレガシープロジェクトを使用しています。 新しいSpringSecurity5スタックを使用したこの記事のバージョンについては、記事 Spring SecurityOAuth2を使用したシンプルなシングルサインオンをご覧ください。

2. クライアントアプリ

クライアントアプリケーションから始めましょう。 もちろん、構成を最小限に抑えるためにSpring Bootを使用します。

2.1. Mavenの依存関係

まず、pom.xmlに次の依存関係が必要になります。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

2.2. セキュリティ構成

次に、最も重要な部分である、クライアントアプリケーションのセキュリティ構成:

@Configuration
@EnableOAuth2Sso
public class UiSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**")
          .authorizeRequests()
          .antMatchers("/", "/login**")
          .permitAll()
          .anyRequest()
          .authenticated();
    }
}

もちろん、この構成の中核部分は、シングルサインオンを有効にするために使用している @EnableOAuth2Ssoアノテーションです。

WebSecurityConfigurerAdapter を拡張する必要があることに注意してください。拡張しないと、すべてのパスが保護されるため、ユーザーが任意のページにアクセスしようとすると、ログインにリダイレクトされます。 この例では、認証なしでアクセスできるページはインデックスページとログインページだけです。

最後に、リクエストスコープを処理するためのRequestContextListenerBeanも定義しました。

そしてapplication.yml

server:
    port: 8082
    servlet:
        context-path: /ui
    session:
      cookie:
        name: UISESSION
security:
  basic:
    enabled: false
  oauth2:
    client:
      clientId: SampleClientId
      clientSecret: secret
      accessTokenUri: http://localhost:8081/auth/oauth/token
      userAuthorizationUri: http://localhost:8081/auth/oauth/authorize
    resource:
      userInfoUri: http://localhost:8081/auth/user/me
spring:
  thymeleaf:
    cache: false

いくつかの簡単なメモ:

  • デフォルトの基本認証を無効にしました
  • accessTokenUri は、アクセストークンを取得するためのURIです。
  • userAuthorizationUri は、ユーザーがリダイレクトされる認証URIです。
  • userInfoUri現在のユーザーの詳細を取得するためのユーザーエンドポイントのURI

また、ここでの例では、承認サーバーをロールアウトしましたが、もちろん、FacebookやGitHubなどの他のサードパーティプロバイダーを使用することもできます。

2.3. フロントエンド

それでは、クライアントアプリケーションのフロントエンド構成を見てみましょう。 主にサイトですでに取り上げているため、ここではこれに焦点を当てません。

ここでのクライアントアプリケーションには、非常にシンプルなフロントエンドがあります。 これがindex.htmlです。

<h1>Spring Security SSO</h1>
<a href="securedPage">Login</a>

そしてsecuredPage.html

<h1>Secured Page</h1>
Welcome, <span th:text="${#authentication.name}">Name</span>

securedPage.html ページでは、ユーザーを認証する必要がありました。 認証されていないユーザーがsecuredPage.htmlにアクセスしようとすると、最初にログインページにリダイレクトされます。

3. 認証サーバー

ここで、認証サーバーについて説明しましょう。

3.1. Mavenの依存関係

まず、pom.xmlで依存関係を定義する必要があります。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.3.3.RELEASE</version>
</dependency>

3.2. OAuth構成

ここでは、承認サーバーとリソースサーバーを単一のデプロイ可能なユニットとして一緒に実行することを理解することが重要です。

リソースサーバーの構成から始めましょう。これは、プライマリブートアプリケーションを兼ねています。

@SpringBootApplication
@EnableResourceServer
public class AuthorizationServerApplication extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(AuthorizationServerApplication.class, args);
    }
}

次に、承認サーバーを構成します。

@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public void configure(
      AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll()")
          .checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
          .withClient("SampleClientId")
          .secret(passwordEncoder.encode("secret"))
          .authorizedGrantTypes("authorization_code")
          .scopes("user_info")
          .autoApprove(true) 
          .redirectUris(
            "http://localhost:8082/ui/login","http://localhost:8083/ui2/login"); 
    }
}

authentication_code付与タイプを使用する単純なクライアントのみを有効にしていることに注意してください。

また、 autoApprove がtrueに設定されているため、スコープが手動で承認されるようにリダイレクトおよび昇格されないことに注意してください。

3.3. セキュリティ構成

まず、 application.properties を使用して、デフォルトの基本認証を無効にします。

server.port=8081
server.servlet.context-path=/auth

それでは、構成に移動して、単純なフォームログインメカニズムを定義しましょう。

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
          .antMatchers("/login", "/oauth/authorize")
          .and()
          .authorizeRequests()
          .anyRequest().authenticated()
          .and()
          .formLogin().permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("john")
            .password(passwordEncoder().encode("123"))
            .roles("USER");
    }
    
    @Bean 
    public BCryptPasswordEncoder passwordEncoder(){ 
        return new BCryptPasswordEncoder(); 
    }
}

単純なメモリ内認証を使用しましたが、カスタムuserDetailsServiceに置き換えるだけでよいことに注意してください。

3.4. ユーザーエンドポイント

最後に、構成の前半で使用したユーザーエンドポイントを作成します。

@RestController
public class UserController {
    @GetMapping("/user/me")
    public Principal user(Principal principal) {
        return principal;
    }
}

当然、これはJSON表現でユーザーデータを返します。

4. 結論

このクイックチュートリアルでは、SpringSecurityOauth2とSpringBootを使用したシングルサインオンの実装に焦点を当てました。

いつものように、完全なソースコードはGitHubにあります。