モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

工作HardwareHub ロゴ画像 (Laptop端末利用時)
工作HardwareHub ロゴ画像 (Mobile端末利用時)

Spring Boot における OAuth2 サンプルコード

モーダルを閉じる

ステッカーを選択してください

モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2017/08/01
最終更新最終更新
2021/10/04
記事区分記事区分
一般公開

目次

    アカウント プロフィール画像 (サイドバー)

    Spring Bootでの実用的な機能をわかりやすく解説中

    0
    ステッカーを贈るとは?

    Spring Boot における OAuth2 のサンプルコードをまとめます。OAuth 1.0A と区別します。Spring Security プロジェクト配下の Spring Security OAuth を利用することで、OAuth2 プロバイダ (サーバー) および OAuth2 コンシューマ (クライアント) を簡単に構築できます。

    OAuth2 サーバーのサンプル

    OAuth2 ではアクセストークンの発行を行う認可サーバー Authorization Server と アクセストークンをもとにユーザー情報などを返す Resource Server の二つのサーバーが登場します。負荷分散などの必要がない通常の場合、これらのサーバーは同一のものになりますが、構造上分離できるということになります。それぞれ、Spring Security OAuth のアノテーションが用意されており、@EnableAuthorizationServer および @EnableResourceServer を設定するだけで、OAuth2 の基本的な機能を提供できます。

    .
    |-- build.gradle
    |-- gradle
    |   `-- wrapper
    |       |-- gradle-wrapper.jar
    |       `-- gradle-wrapper.properties
    |-- gradlew
    |-- gradlew.bat
    `-- src
        `-- main
            |-- java
            |   `-- hello
            |       |-- Application.java
            |       `-- HelloController.java
            `-- resources
                `-- application.yml
    

    build.gradle

    spring-security-oauth2 を設定します。

    buildscript {
        ext {
            springBootVersion = '1.5.3.RELEASE'
        }
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        }
    }
    
    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'idea'
    apply plugin: 'org.springframework.boot'
    
    jar {
        baseName = 'gs-spring-boot'
        version =  '0.1.0'
    }
    
    repositories {
        mavenCentral()
    }
    
    sourceCompatibility = 1.8
    targetCompatibility = 1.8
    
    dependencies {
        compile('org.springframework.boot:spring-boot-starter-web')
        compile('org.springframework.security.oauth:spring-security-oauth2')
    }
    

    src/main/java/hello/Application.java

    前述のとおり @EnableAuthorizationServer および @EnableResourceServer の二つのアノテーションを設定します。その際、@EnableAuthorizationServer に関する設定を二つ行っています。

    OAuth2 コンシューマの登録

    OAuth2 では事前にコンシューマをプロバイダである本アプリケーションに登録しておく必要があります。Spring Security OAuth は既定では application.yml などで inMemory() にコンシューマを設定できるようになっています。後述の allowFormAuthenticationForClients() を設定する都合上、ここでは Java で設定しています。コンシューマの client-id と client-secret をそれぞれ demo とし、発行されたアクセストークンで本アプリケーションに対して readwrite という権限範囲 scope で許可されたリクエストを行えるようにします。認可サーバーは OAuth2 アクセストークンをいくつかの方法 authorized-grant-types で発行することができます。今回は特に authorization_code での発行を許可するように設定します。また、認可コードを含んだ状態でユーザをリダイレクトする先の URL を前方一致条件で限定することができ、ここでは検証用に http://localhost:8080http://oauth-callback.com を許可しています。

    アクセストークンを取得する際に Basic 認証を強制しない

    OAuth2 において、http://localhost:18080/myprovider/oauth/token に POST してアクセストークンの発行をリクエストする際、OAuth2 コンシューマは自身の client-id と client-secret を Basic 認証の id と password として含めます。しかしながら、後に構築するアプリケーションのように、Basic 認証に対応していないクライアントが存在するため、allowFormAuthenticationForClients() で client-id と client-secret を HTTP POST のボディに含めてリクエストすることを許可しています。

    package hello;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
    
    @SpringBootApplication
    @EnableAuthorizationServer
    @EnableResourceServer
    public class Application extends AuthorizationServerConfigurerAdapter {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory().withClient("demo").secret("demo").scopes("read", "write")
                    .authorizedGrantTypes("authorization_code")
                    .redirectUris("http://localhost:8080", "http://oauth-callback.com");
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.allowFormAuthenticationForClients();
        }
    }
    

    src/main/java/hello/HelloController.java

    リソースサーバとしてのエンドポイントを一つだけ追加してみます。簡単のため Authentication をすべてそのまま返していますが、実際には情報を限定して返すべきです。

    package hello;
    
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.security.core.Authentication;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    @RestController
    public class HelloController {
    
        @RequestMapping(path = "/userinfo", method = RequestMethod.GET)
        Object userinfo(Authentication authentication) {
            return authentication;
        }
    }
    

    src/main/resources/application.yml

    OAuth2 プロバイダ上のユーザー登録

    今回のように特に設定しない場合は自動的にユーザー名 user が作成されます。今回はパスワードを mypassword と設定しました。OAuth2 コンシューマがアクセストークンで情報を取得することができるユーザーです。

    パスとポートの設定

    後述の OAuth2 コンシューマから本 OAuth2 プロバイダを利用する際に、ドメインが localhost で共通のため、ポート番号およびパスを設定しています。パスの設定は JSESSIONID という名称のクッキーをプロバイダとコンシューマで別々に持たせるための回避策です。OAuth2 の本質的な設定ではありません。

    ログの出力設定

    OAuth2 サーバーへの HTTP リクエストを確認する検証目的で、ログレベルをデバッグに設定しています。

    security:
      user:
        password: mypassword
    
    server:
      port: 18080
      context-path: /myprovider
    
    logging:
      level:
        root: DEBUG
    

    curl を OAuth2 クライアントとして利用

    後述の Spring Boot アプリケーションなどの Web アプリケーションを OAuth2 クライアントとして利用することが一般的ですが、動作検証を兼ねて以下のコマンドを実行すると user 情報が取得できることが分かります。

    read scope 認可コードの発行

    http://localhost:18080/myprovider/oauth/authorize?response_type=code&client_id=demo&redirect_uri=http://oauth-callback.com&scope=read にアクセスして、Basic 認証で user, mypassword を入力します。この Basic 認証は Spring フレームワークの都合上、簡単のため認証で利用されているだけです。OAuth2 におけるアクセストークン発行の Basic 認証と区別します。認証が通ったら、Approve を選択してから Authorize をクリックします。以下のような URL にリダイレクトされて、発行された認可コードが確認できます。

    http://oauth-callback.com/?code=bbSOgq
    

    アクセストークンの発行

    OAuth2 認可サーバーは Basic 認証の -u demo:demo で OAuth2 コンシューマを識別します。

    $ curl -s -u demo:demo http://localhost:18080/myprovider/oauth/token -d grant_type=authorization_code -d code=bbSOgq -d redirect_uri=http://oauth-callback.com | jq
    {
      "access_token": "91c3fa7a-ac6c-479f-b779-de45236f4b67",
      "token_type": "bearer",
      "expires_in": 43199,
      "scope": "read"
    }
    

    allowFormAuthenticationForClients() しているため、以下のように Basic 認証なしでもアクセストークンを発行できます。

    $ curl -s http://localhost:18080/myprovider/oauth/token -d grant_type=authorization_code -d code=bbSOgq -d redirect_uri=http://oauth-callback.com -d client_id=demo -d client_secret=demo | jq
    {
      "access_token": "91c3fa7a-ac6c-479f-b779-de45236f4b67",
      "token_type": "bearer",
      "expires_in": 43047,
      "scope": "read"
    }
    

    アクセストークンの利用

    $ curl -s http://localhost:18080/myprovider/userinfo -H 'Authorization: Bearer 91c3fa7a-ac6c-479f-b779-de45236f4b67' | jq
    {
      "authorities": [
        {
          "authority": "ROLE_USER"
        }
      ],
      "details": {
        "remoteAddress": "0:0:0:0:0:0:0:1",
        "sessionId": null,
        "tokenValue": "91c3fa7a-ac6c-479f-b779-de45236f4b67",
        "tokenType": "Bearer",
        "decodedDetails": null
      },
      "authenticated": true,
      "userAuthentication": {
        "authorities": [
          {
            "authority": "ROLE_USER"
          }
        ],
        "details": {
          "remoteAddress": "0:0:0:0:0:0:0:1",
          "sessionId": "01D3FAB19EEB918904162D84AF8D1F85"
        },
        "authenticated": true,
        "principal": {
          "password": null,
          "username": "user",
          "authorities": [
            {
              "authority": "ROLE_USER"
            }
          ],
          "accountNonExpired": true,
          "accountNonLocked": true,
          "credentialsNonExpired": true,
          "enabled": true
        },
        "credentials": null,
        "name": "user"
      },
      "clientOnly": false,
      "oauth2Request": {
        "clientId": "demo",
        "scope": [
          "read"
        ],
        "requestParameters": {
          "code": "bbSOgq",
          "grant_type": "authorization_code",
          "scope": "read",
          "response_type": "code",
          "redirect_uri": "http://oauth-callback.com",
          "client_secret": "demo",
          "client_id": "demo"
        },
        "resourceIds": [],
        "authorities": [],
        "approved": true,
        "refresh": false,
        "redirectUri": "http://oauth-callback.com",
        "responseTypes": [
          "code"
        ],
        "extensions": {},
        "grantType": "authorization_code",
        "refreshTokenRequest": null
      },
      "credentials": "",
      "principal": {
        "password": null,
        "username": "user",
        "authorities": [
          {
            "authority": "ROLE_USER"
          }
        ],
        "accountNonExpired": true,
        "accountNonLocked": true,
        "credentialsNonExpired": true,
        "enabled": true
      },
      "name": "user"
    }
    

    OAuth2 クライアントのサンプル

    以下のサンプルにおいて、いずれも Gradle プロジェクト構成および build.gradle の設定内容は前述「OAuth2 サーバーのサンプル」と同じです。

    Single Sign On によるログイン

    OAuth2 コンシューマがアクセストークンを取得するためには、OAuth2 コンシューマが動作するアプリケーションを利用するユーザーによる OAuth2 プロバイダ認証がとおる必要があります。そのため、取得したアクセストークンで OAuth2 プロバイダのリソースサーバから確かにユーザーの情報が取得できることを確認することを、現在 OAuth2 コンシューマが動作するアプリケーションを利用しているユーザーの Sign On 処理に代えることができます。ユーザーは OAuth2 コンシューマが動作するアプリケーションではログイン処理を行わず、OAuth2 プロバイダでのみログイン認証を行えばよいため、これは Single Sign On の実現方法のひとつです。この Single Sign On を実現するために OAuth2 コンシューマとして必要な処理は、Spring Security OAuth の @EnableOAuth2Sso アノテーション設定で実現できます。

    Application.java (@EnableOAuth2Sso を設定)

    package hello;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
    
    @SpringBootApplication
    @EnableOAuth2Sso
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    HelloController.java (動作確認のための / エンドポイントを設定)

    package hello;
    
    import org.springframework.security.core.Authentication;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloController {
    
        @RequestMapping("/")
        Object userinfo(Authentication authentication) {
            return authentication;
        }
    }
    

    application.yml (GitHub の OAuth2 プロバイダで SSO 動作検証)

    security:
      oauth2:
        client:
          client-id: bd1c0a783ccdd1c9b9e4
          client-secret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
          access-token-uri: https://github.com/login/oauth/access_token
          user-authorization-uri: https://github.com/login/oauth/authorize
          client-authentication-scheme: form
        resource:
          user-info-uri: https://api.github.com/user
    
    server:
      port: 8080
    

    設定概要は以下のとおりです。

    • client-id, client-secret, port
    • user-authorization-uri
      • ユーザーが認証を行うための GitHub のページです。Sign On していない状態で localhost:8080 にリクエストがなされるとリダイレクトされます。
    • access-token-uri
      • ユーザーが認証を終えると GitHub から localhost:8080 にリダイレクトされます。その際 URL に認可コードがクエリストリングで含まれます。この認可コードをもとにアクセストークンを取得するための、認可サーバーのエンドポイントです。
    • user-info-uri
      • 取得したアクセストークンをもとにユーザーの情報を取得するための、リソースサーバのエンドポイントです。確かに情報が取得できれば Sign On させます。

    エンドポイント user-authorization-uri で認証する際の画面例です。

    localhost:18080 で動作する OAuth2 プロバイダで SSO

    application.yml を localhost:18080 に向けます。その他のファイルは GitHub のサンプルと同じ内容のままです。

    security:
      oauth2:
        client:
          client-id: demo
          client-secret: demo
          access-token-uri: http://localhost:18080/myprovider/oauth/token
          user-authorization-uri: http://localhost:18080/myprovider/oauth/authorize
          client-authentication-scheme: form
        resource:
          user-info-uri: http://localhost:18080/myprovider/userinfo
    
    server:
      port: 8080
      context-path: /myconsumer
    

    Spring Security OAuth の Basic 認証です。上述のとおり、設定されたパスワードは mypassword です。

    一つ以上の scope を選択して認可します。

    OAuth2 クライアントを直接利用

    以下のサンプルにおいて build.gradle および application.yml の内容は、前述「Single Sign On によるログイン」と同じです。

    Application.java

    SSO では OAuth2 認可サーバから得たアクセストークンを内部的に利用してリソースサーバの user-info-uri から取得した情報でログイン処理を行います。このアクセストークンはリソースサーバのその他のリソースを取得するために流用することができます。そのためのクライアント OAuth2RestTemplate を利用するためには @EnableOAuth2Client を設定して @Bean を定義します。

    package hello;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.oauth2.client.OAuth2ClientContext;
    import org.springframework.security.oauth2.client.OAuth2RestTemplate;
    import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
    
    @SpringBootApplication
    @EnableOAuth2Sso
    @EnableOAuth2Client
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
        @Bean
        public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext, OAuth2ProtectedResourceDetails details) {
            return new OAuth2RestTemplate(details, oauth2ClientContext);
        }
    }
    

    HelloController.java

    定義した OAuth2RestTemplate Bean を @Autowired して利用します。RestTemplate と同様の手順で GitHub REST API を利用しています。返された JSON 文字列を後述の User クラスのオブジェクトに変換して返します。

    package hello;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.oauth2.client.OAuth2RestTemplate;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloController {
    
        @Autowired
        private OAuth2RestTemplate oauth2RestTemplate;
    
        @RequestMapping("/")
        Object userinfo(Authentication authentication) {
            User user = oauth2RestTemplate.getForObject("https://api.github.com/user", User.class);
            return user.toString();
        }
    }
    

    User.java

    前述の oauth2RestTemplate で JSON 文字列を変換する対象となるクラスです。@JsonIgnoreProperties(ignoreUnknown=true) は、JSON 文字列に存在して User クラスに存在しない項目を変換対象から除外するための設定です。

    package hello;
    
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    
    @JsonIgnoreProperties(ignoreUnknown=true)
    public class User {
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User [name=" + name + "]";
        }
    }
    

    レスポンス例

    User [name=なつかしのねこ]
    
    0
    詳細設定を開く/閉じる
    アカウント プロフィール画像 (本文下)

    Spring Bootでの実用的な機能をわかりやすく解説中

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    さらに詳しく →
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      関連記事

      • Spring Security フォームログインのサンプルコード
        Spring フレームワークによる Web アプリケーション開発で、ログイン処理を実装する際は Spring Security が便利です。ここでは特に Spring Boot で Web アプリケーションを開発する場合を対象とし、フォームによる ID/Password ログインを行うためのサンプルコードをまとめます。 公式ドキュメント [Spring Security チュートリアル](http...
        えびちゃんえびちゃん12/4/2019に更新
        いいねアイコン画像0
      • Java配列の宣言方法 (C/C++との違い)
        Javaの配列 Javaの配列宣言方法はC/C++と似ているようで若干異なる。 初期化しない場合 C/C++の int array[10]; はJavaでは int array[] = new int[10]; となる。同様にC/C++の int array[3][3]; はJavaでは int array[][] = new int[3][3]; となる。 初期化
        てんとうむしてんとうむし5/13/2018に更新
        いいねアイコン画像0
      • PlantUML による UML 図の描き方
        サムネイル画像-c788fffde5
        PlantUML はテキスト形式で表現されたシーケンス図やクラス図といった UML (Unified Modeling Language) 図の情報から画像を生成するためのツールです。簡単な使い方をまとめます。 インストール方法の選択 Atom や Eclipse のプラグインをインストールしてエディタから利用する方法、JAR をダウンロードして Java コマンドで実行する方法、Redmine ...
        kentakenta1/21/2020に更新
        いいねアイコン画像0
      • Akka HTTP サンプルコード (Scala)
        サムネイル画像-a98142497c
        Akka アクターを用いて実装された汎用 HTTP フレームワークです。Spray の後継です。コアモジュールである akka-http-core は 2016/2/17 に experimental が外れました。akka-http などのいくつかのサブモジュールは 2016/3/1 現在 experimental のままですが、基本的な
        雄太雄太10/7/2021に更新
        いいねアイコン画像0
      • Kestrel の使用例
        Kestrel は Message Queue (MQ) の実装のひとつです。一般に MQ はアプリケーション間やプロセス間、スレッド間で非同期に通信するために用いられます。メッセージの送信側は MQ に書き込めば受信側の応答を待たずに次の処理に非同期に進むことができます。Kestrel はわずか 2500 行程の Scala で実装されており JVM で動作します。MQ 自体はメモリ上に存在する...
        したくんしたくん10/12/2017に更新
        いいねアイコン画像0