LoginSignup
15
22

More than 5 years have passed since last update.

Spring BootとScribe OAuth Libraryでソーシャルログイン機能を実装する

Last updated at Posted at 2015-12-28

概要

JavaのOAuthライブラリであるScribeJavaを使用してWebアプリケーションに、SNS(Google、Facebook、Twitter)アカウントでソーシャルログインするサンプルアプリケーションです。
ちなみにSpringにはSpring Socialというプロジェクトがありますが、ScribeJavaの方が手軽に実装できそうだったのでこちらで試してみました。

環境

  • Windows7 (64bit)
  • Java 1.8.0_65
  • Spring Boot 1.3.0
  • ScribeJava 2.0.1

参考

準備

それぞれのサービスでアプリケーションを登録して認証に使う情報(クライアントIDやクライアントシークレットキーなど)を手に入れます。

アプリケーションの登録

アプリケーション登録時に重要な項目はCallback URL(Redirect URL)です。ローカル開発環境で試すので登録内容は下記のようになります。

SNS Site URL Callback URL (Redirect URL)
Google http://localhost:9000 http://localhost:9000/google/callback
Facebook http://localhost:9000 http://localhost:9000/facebook/callback
Twitter 実際にアクセスできるURL http://127.0.0.1:9000/twitter/callback

Google

プロジェクトが作成済み且つ、OAuth同意画面が設定済みという前提で説明を進めます。

Google Developers consoleにアクセスしAPI Managerで新しい認証情報を作成します。
図の様に作成する認証情報にOAuthクライアントIDを選択します。

p01.png

アプリケーションの種類に"ウェブアプリケーション"を選択します。
"名前"、"承認済みのJavaScript生成元"、"承認済みのリダイレクトURI"を入力します。

  • 承認済みのJavaScript生成元 : http://localhost:9000
  • 承認済みのリダイレクトURI : http://localhost:9000/google/callback

p02.png

(モザイクをかけていますが)図のようにクライアントIDとクライアントシークレットが発行されます。

p03.png

Facebook

開発者向けFacebookにアクセスし、ページ右上のメニューより"Add a New App"を選択します。

f01.png

プラットフォームの選択画面で"ウェブサイト"を選択します。

f02.png

アプリケーション名を入力します。

f03.png

アプリケーションのカテゴリを選択します。

f04.png

TOPページにアクセスしなおして(登録内容が直ぐに反映されないので)、ページ右上のメニューより作成したアプリケーションを選択します。

f05.png

ダッシュボードの左側メニューよりTest Appsを選択し、テストアプリケーションを作成します。

f07.png

テストアプリケーションの名前を入力します。今回はデフォルト値のままで登録しました。

f08.png

SettingsメニューのBasicタブで"+Add Platform"をクリックします。

f09a.png

WebSiteを選択します。

f09b.png

App DomainsとSite URLを入力します。

  • Site URL : http://localhost:9000

f09c.png

SettingsメニューのAdvancedタブで"Valid OAuth redirect URIs"を入力し、Save Changesボタンをクリックします。
"Valid OAuth redirect URIs"には複数のURIを指定できます。

  • Valid OAuth redirect URIs : http://localhost:9000/facebook/callback

f10.png

AppIDとApp Secretはダッシュボードで確認できます。

f11.png

Twitter

Twitter Application Managementにアクセスし、"Create New App"をクリックします。

b01.png

Application Detailsに必要な情報を入力します。
Websiteには、実際にアクセスできるページ(localhostなどのローカルでは駄目なようです)を指定する必要があります。今回はQiitaの自分のプロフィールページを仮に入力しました。
Callback URLにもlocalhostなどは入力できませんが、IPアドレスは入力可能なので127.0.0.1としました。

  • Callback URL : http://127.0.0.1:9000/twitter/callback

b02.png

Permissionsタブでアクセスタイプを"Read Only"に変更します。

b04.png

Consumer KeyとConsumer Secret(図ではモザイクにしています)はKey and Access Tokensタブで確認できます。

b03.png

アプリケーションの開発

使用するScribe OAuthライブラリの依存関係をpom.xmlに追加します。

scribe
<dependency>
  <groupId>com.github.scribejava</groupId>
  <artifactId>scribejava-apis</artifactId>
  <version>2.0.1</version>
</dependency>

また、APIのレスポンスがjsonなのでパースするjsonライブラリも追加します。

json
<dependency>
  <groupId>org.json</groupId>
  <artifactId>json</artifactId>
  <version>20151123</version>
</dependency>

雛形アプリケーションを作成

プロジェクト名: sbsns-example

mavenでアプリケーションの雛形を作成

generate
> mvn archetype:generate -DgroupId=com.example.sbsns -DartifactId=sbsns-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
eclipse
> cd sbsns-example
> mvn eclipse:eclipse

eclipseにインポート

  • メニューバーの"File" → "Import..." → "Maven" → "Existing Maven Projects"を選択します。
  • プロジェクトのディレクトリを選択し、"Finish"ボタンをクリックします。

pom.xmlの編集

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example.sbsns</groupId>
  <artifactId>sbsns-example</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>sbsns-example</name>
  <url>http://maven.apache.org</url>

  <properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.0.RELEASE</version>
  </parent>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <dependency>
      <groupId>com.github.scribejava</groupId>
      <artifactId>scribejava-apis</artifactId>
      <version>2.0.1</version>
    </dependency>

    <dependency>
      <groupId>org.json</groupId>
      <artifactId>json</artifactId>
      <version>20151123</version>
    </dependency>

    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.4</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          <version>1.3.0.RELEASE</version>
        </plugin>
      </plugins>
    </pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <verbose>true</verbose>
          <source>${java.version}</source>
          <target>${java.version}</target>
          <encoding>${project.build.sourceEncoding}</encoding>
          <compilerArgs>
            <arg>-verbose</arg>
            <arg>-Xlint:all,-options,-path</arg>
          </compilerArgs>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>versions-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

resourcesフォルダの作成

設定ファイルやテンプレートファイルなどを配置するresourcesフォルダをsrc/main下に作成します。

作成したらプロジェクトに反映させます。

  • "Build Path" → "Configure Build Path" → "Java Buld Path" → "Source"タブを選択する。
  • "Add Folder"ボタンをクリック → 作成した"resources"フォルダにチェックを入れる。

application.ymlの作成

src/main/resourcesフォルダ内にapplication.ymlを作成します。

application.yml
# EMBEDDED SERVER CONFIGURATION (ServerProperties)
server:
  port: 9000

spring:
# PROFILES
  profiles:
    active: dev
# THYMELEAF (ThymeleafAutoConfiguration)
  thymeleaf:
    enabled: true
    cache: false

logback.xmlの作成

src/main/resourcesフォルダ内にlogback.xmlを作成します。
ログの出力先フォルダを"D:/logs"に指定しました。

logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <property name="LOG_DIR" value="D:/logs" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{yyyy-MMM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{35} - %msg %n</pattern>
    </encoder>
  </appender>
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
     <file>${LOG_DIR}/sbsns-example.log</file>
     <encoder>
       <charset>UTF-8</charset>
       <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] - %msg %n</pattern>
     </encoder>
  </appender>

  <logger name="com.example" level="DEBUG" />

  <logger name="org.hibernate" level="ERROR"/>
  <logger name="org.springframework" level="INFO"/>
  <logger name="org.thymeleaf" level="INFO"/>
  <logger name="org.eclipse.jetty" level="INFO"/>
  <logger name="org.apache.http" level="INFO"/>
  <root>
    <appender-ref ref="STDOUT" />
    <appender-ref ref="FILE" />
  </root>
</configuration>

ビルド

この時点で動作検証を兼ねてビルドします。

package
> mvn package

ビルドが成功したら生成したjarファイルを実行します。
コマンドプロンプトに"Hello World!"と表示されれば成功です。

jar
> cd target
> java -jar sbsns-example-1.0-SNAPSHOT.jar
Hello World!

サンプルアプリケーションの構成

ディレクトリ/ファイル

tree
sbsns-example
 └─src
    └─main
       ├─java
       │  └─com
       │      └─example
       │          └─sbsns
       │              ├─App.java
       │              └─web
       │                 ├─FacebookController.java
       │                 ├─GooglekController.java
       │                 ├─IndexController.java
       │                 └─TwitterController.java
       └─resources
           └─templates
               ├─facebook
               │   └─profile.html
               ├─google
               │   └─profile.html
               ├─twitter
               │   └─profile.html
               └─index.html

App

App.java
package com.example.sbsns;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

Top

topページを表示するだけのコントローラーです。
各SNSのsigninページのリンクを表示します。

IndexController.java
package com.example.sbsns.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class IndexController {

  @RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
  public String index() {
    return "index";
  }

}
index.html
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
  <title>index</title>
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="col-md-12 well">
        <h3>index</h3>
        <ul>
          <li><a href="/google/signin" th:href="@{/google/signin}">Google signin</a></li>
          <li><a href="/facebook/signin" th:href="@{/facebook/signin}">Facebook signin</a></li>
          <li><a href="/twitter/signin" th:href="@{/twitter/signin}">Twitter signin</a></li>
        </ul>
        <h3>user profile</h3>
        <ul>
          <li><a href="/google/profile" th:href="@{/google/profile}">Google profile</a></li>
          <li><a href="/facebook/profile" th:href="@{/facebook/profile}">Facebook profile</a></li>
          <li><a href="/twitter/profile" th:href="@{/twitter/profile}">Twitter profile</a></li>
        </ul>
      </div>
    </div>
  </div>
</body>
</html>

Google

GoogleController.java
package com.example.sbsns.web;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.github.scribejava.apis.GoogleApi20;
import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Token;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.model.Verifier;
import com.github.scribejava.core.oauth.OAuthService;

@Controller
@RequestMapping(value = "/google")
public class GoogleController {
  private static Logger logger = LoggerFactory.getLogger(GoogleController.class);

  private static final String YOUR_API_KEY = "クライアントID";
  private static final String YOUR_API_SECRET = "クライアントシークレット";

  private static final String HOST = "http://localhost:9000";
  private static final String CALLBACK_URL = "/google/callback";
  private static final Token EMPTY_TOKEN = null;

  //permission scope
  private static final List<String> SCOPES = new ArrayList<String>(){
    private static final long serialVersionUID = 1L;
      {
        add("profile");
        add("email");
      }
  };

  //API End point
  //private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/plus/v1/people/me";
  private static final String USER_PROFILE_API = "https://www.googleapis.com/oauth2/v1/userinfo";
  private static final String QUERY = "?fields=id,name,email";

  @RequestMapping(value ="/signin", method = RequestMethod.GET)
  public void signin(HttpServletRequest request, HttpServletResponse response) throws IOException {
    logger.debug("signin");

    String secretState = "secret" + new Random().nextInt(999_999);
    request.getSession().setAttribute("SECRET_STATE", secretState);

    OAuthService service = new ServiceBuilder()
      .provider(GoogleApi20.class)
      .apiKey(YOUR_API_KEY)
      .apiSecret(YOUR_API_SECRET)
      .callback(HOST + CALLBACK_URL)
      .scope(String.join(" ", SCOPES))
      .state(secretState)
      .connectTimeout(10)
      .build();

    String redirectURL = service.getAuthorizationUrl(EMPTY_TOKEN);
    logger.info("redirectURL:{}", redirectURL);

    response.sendRedirect(redirectURL);
  }

  @RequestMapping(value ="/callback", method = RequestMethod.GET)
  public String callback(@RequestParam(value = "code", required = false) String code,
      @RequestParam(value = "state", required = false) String state,
      HttpServletRequest request, HttpServletResponse response, Model model) {
    logger.debug("callback");
    logger.info("code:{}", code);
    logger.info("state:{}", state);

    String secretState = (String) request.getSession().getAttribute("SECRET_STATE");

    if (secretState.equals(state)) {
      logger.info("State value does match!");
    } else {
      logger.error("Ooops, state value does not match!");
    }

    OAuthService service = new ServiceBuilder()
      .provider(GoogleApi20.class)
      .apiKey(YOUR_API_KEY)
      .apiSecret(YOUR_API_SECRET)
      .callback(HOST + CALLBACK_URL)
      .build();

    String requestUrl = USER_PROFILE_API + QUERY;

    final Verifier verifier = new Verifier(code);
    final Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier);

    final OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, requestUrl, service);
    service.signRequest(accessToken, oauthRequest);

    final Response resourceResponse = oauthRequest.send();

    logger.info("code:{}", resourceResponse.getCode());
    logger.info("body:{}", resourceResponse.getBody());
    logger.info("message:{}",resourceResponse.getMessage());

    final JSONObject obj = new JSONObject(resourceResponse.getBody());
    logger.info("json:{}" ,obj.toString());

    String googleid = obj.getString("id");
    String name = obj.getString("name");
    String email = obj.getString("email");

    model.addAttribute("id", googleid);
    model.addAttribute("name", name);
    model.addAttribute("email", email);

    request.getSession().setAttribute("GOOGLE_ACCESS_TOKEN", accessToken);

    return "/google/profile";
  }

  @RequestMapping(value ="/profile", method = RequestMethod.GET)
  public String profile(HttpServletRequest request, HttpServletResponse response, Model model) {
    logger.debug("profile");

    OAuthService service = new ServiceBuilder()
      .provider(GoogleApi20.class)
      .apiKey(YOUR_API_KEY)
      .apiSecret(YOUR_API_SECRET)
      .callback(HOST + CALLBACK_URL)
      .build();

    final String requestUrl = USER_PROFILE_API + QUERY;
    final Token accessToken = (Token) request.getSession().getAttribute("GOOGLE_ACCESS_TOKEN");

    final OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, requestUrl, service);
    service.signRequest(accessToken, oauthRequest);

    final Response resourceResponse = oauthRequest.send();

    logger.info("code:{}", resourceResponse.getCode());
    logger.info("body:{}", resourceResponse.getBody());
    logger.info("message:{}",resourceResponse.getMessage());

    final JSONObject obj = new JSONObject(resourceResponse.getBody());
    logger.info("json:{}" ,obj.toString());

    String googleid = obj.getString("id");
    String name = obj.getString("name");
    String email = obj.getString("email");

    model.addAttribute("id", googleid);
    model.addAttribute("name", name);
    model.addAttribute("email", email);

    return "google/profile";
  }

}
profile.html
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
  <title>google - profile</title>
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="col-md-12 well">
        <h3>user profile</h3>
        <ul>
          <li th:text="${id}">id</li>
          <li th:text="${name}">name</li>
          <li th:text="${email}">email</li>
        </ul>
        <h3>link</h3>
        <ul>
          <li><a href="/" th:href="@{/}">top</a></li>
        </ul>
      </div>
    </div>
  </div>
</body>
</html>
Google's OAuth 2.0 endpoint
api
https://accounts.google.com/o/oauth2/auth
userinfo endpoint

ユーザーのプロフィールを取得するAPI endpointです。

api
https://www.googleapis.com/oauth2/v1/userinfo

Google+のプロフィールを取得する場合は下記のendpointを使用します。またdevelopers consoleでGoogle+のAPIを有効化しておく必要があります。

api
https://www.googleapis.com/plus/v1/people/me

Facebook

FacebookController.java
package com.example.sbsns.web;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.github.scribejava.apis.FacebookApi;
import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Token;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.model.Verifier;
import com.github.scribejava.core.oauth.OAuthService;

@Controller
@RequestMapping(value = "/facebook")
public class FacebookController {
  private static Logger logger = LoggerFactory.getLogger(FacebookController.class);

  private static final String YOUR_API_KEY = "APP ID";
  private static final String YOUR_API_SECRET = "APP SECRET";

  private static final String HOST = "http://localhost:9000";
  private static final String CALLBACK_URL = "/facebook/callback";
  private static final Token EMPTY_TOKEN = null;

  //permission scope
  private static final List<String> SCOPES = new ArrayList<String>(){
    private static final long serialVersionUID = 1L;
      {
        add("public_profile");
        add("user_birthday");
        add("email");
      }
  };

  //API End point
  private static final String USER_PROFILE_API = "https://graph.facebook.com/v2.5/me";
  private static final String QUERY = "?fields=id,name,first_name,last_name,gender,birthday,email";

  @RequestMapping(value ="/signin", method = RequestMethod.GET)
  public void signin(HttpServletRequest request, HttpServletResponse response) throws IOException {
    logger.debug("signin");

    String secretState = "secret" + new Random().nextInt(999_999);
    request.getSession().setAttribute("SECRET_STATE", secretState);

    OAuthService service = new ServiceBuilder()
      .provider(FacebookApi.class)
      .apiKey(YOUR_API_KEY)
      .apiSecret(YOUR_API_SECRET)
      .callback(HOST + CALLBACK_URL)
      .scope(String.join(",", SCOPES))
      .state(secretState)
      .grantType("code")
      .connectTimeout(10)
      .build();

    String redirectURL = service.getAuthorizationUrl(EMPTY_TOKEN);
    logger.info("redirectURL:{}", redirectURL);

    response.sendRedirect(redirectURL);
  }

  @RequestMapping(value ="/callback", method = RequestMethod.GET)
  public String callback(@RequestParam(value = "code", required = false) String code,
      @RequestParam(value = "state", required = false) String state,
      HttpServletRequest request, HttpServletResponse response, Model model) {
    logger.debug("callback");
    logger.info("code:{}", code);
    logger.info("state:{}", state);

    String secretState = (String) request.getSession().getAttribute("SECRET_STATE");

    if (secretState.equals(state)) {
      logger.info("State value does match!");
    } else {
      logger.error("Ooops, state value does not match!");
    }

    OAuthService service = new ServiceBuilder()
      .provider(FacebookApi.class)
      .apiKey(YOUR_API_KEY)
      .apiSecret(YOUR_API_SECRET)
      .callback(HOST + CALLBACK_URL)
      .build();

    final String requestUrl = USER_PROFILE_API + QUERY;

    final Verifier verifier = new Verifier(code);
    final Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier);

    final OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, requestUrl, service);
    service.signRequest(accessToken, oauthRequest);

    final Response resourceResponse = oauthRequest.send();

    logger.info("code:{}", resourceResponse.getCode());
    logger.info("body:{}", resourceResponse.getBody());
    logger.info("message:{}",resourceResponse.getMessage());

    final JSONObject obj = new JSONObject(resourceResponse.getBody());
    logger.info("json:{}" ,obj.toString());

    String facdebookId = obj.getString("id");
    String name = obj.getString("name");
    String email = obj.getString("email");

    model.addAttribute("id", facdebookId);
    model.addAttribute("name", name);
    model.addAttribute("email", email);

    request.getSession().setAttribute("FACEBOOK_ACCESS_TOKEN", accessToken);

    return "facebook/profile";
  }

  @RequestMapping(value ="/profile", method = RequestMethod.GET)
  public String profile(HttpServletRequest request, HttpServletResponse response, Model model) {
    logger.debug("profile");

    OAuthService service = new ServiceBuilder()
      .provider(FacebookApi.class)
      .apiKey(YOUR_API_KEY)
      .apiSecret(YOUR_API_SECRET)
      .callback(HOST + CALLBACK_URL)
      .build();

    final String requestUrl = USER_PROFILE_API + QUERY;
    final Token accessToken = (Token) request.getSession().getAttribute("FACEBOOK_ACCESS_TOKEN");

    final OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, requestUrl, service);
    service.signRequest(accessToken, oauthRequest);

    final Response resourceResponse = oauthRequest.send();

    logger.info("code:{}", resourceResponse.getCode());
    logger.info("body:{}", resourceResponse.getBody());
    logger.info("message:{}",resourceResponse.getMessage());

    final JSONObject obj = new JSONObject(resourceResponse.getBody());
    logger.info("json:{}" ,obj.toString());

    String facdebookId = obj.getString("id");
    String name = obj.getString("name");
    String email = obj.getString("email");

    model.addAttribute("id", facdebookId);
    model.addAttribute("name", name);
    model.addAttribute("email", email);

    return "facebook/profile";
  }

}
profile.html
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
  <title>facebook - profile</title>
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="col-md-12 well">
        <h3>user profile</h3>
        <ul>
          <li th:text="${id}">id</li>
          <li th:text="${name}">name</li>
          <li th:text="${email}">email</li>
        </ul>
        <h3>link</h3>
        <ul>
          <li><a href="/" th:href="@{/}">top</a></li>
        </ul>
      </div>
    </div>
  </div>
</body>
</html>
userinfo endpoint

ユーザーのプロフィールを取得するAPI endpointです。

api
https://graph.facebook.com/v2.5/me

Twitter

TwitterController.java
package com.example.sbsns.web;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.github.scribejava.apis.TwitterApi;
import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Token;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.model.Verifier;
import com.github.scribejava.core.oauth.OAuthService;

@Controller
@RequestMapping(value = "/twitter")
public class TwitterController {
  private static Logger logger = LoggerFactory.getLogger(TwitterController.class);

  private static final String YOUR_API_KEY = "Consumer Key";
  private static final String YOUR_API_SECRET = "Consumer Secret";

  private static final String HOST = "http://localhost:9000";
  private static final String CALLBACK_URL = "/twitter/callback";

  //API End point
  private static final String PROTECTED_RESOURCE_URL = "https://api.twitter.com/1.1/account/verify_credentials.json";

  @RequestMapping(value ="/signin", method = RequestMethod.GET)
  public void signin(HttpServletRequest request, HttpServletResponse response) throws IOException {
    logger.debug("signin");

    OAuthService service = new ServiceBuilder()
      .provider(TwitterApi.Authenticate.class)
      .apiKey(YOUR_API_KEY)
      .apiSecret(YOUR_API_SECRET)
      .callback(HOST + CALLBACK_URL)
      .build();

    Token requestToken = service.getRequestToken();
    String redirectURL = service.getAuthorizationUrl(requestToken);
    logger.info("redirectURL:{}", redirectURL);

    response.sendRedirect(redirectURL);
  }

  @RequestMapping(value ="/callback", method = RequestMethod.GET)
  public String callback(@RequestParam(value = "oauth_token", required = false) String oauth_token,
      @RequestParam(value = "oauth_verifier", required = false) String oauth_verifier,
      HttpServletRequest request, HttpServletResponse response, Model model) {
    logger.debug("callback");
    logger.info("oauth_token:{}", oauth_token);
    logger.info("oauth_verifier:{}", oauth_verifier);

    OAuthService service = new ServiceBuilder()
      .provider(TwitterApi.Authenticate.class)
      .apiKey(YOUR_API_KEY)
      .apiSecret(YOUR_API_SECRET)
      .callback(HOST + CALLBACK_URL)
      .build();

    final Verifier verifier = new Verifier(oauth_verifier);
    final Token requestToken = new Token(oauth_token, oauth_verifier);
    final Token accessToken = service.getAccessToken(requestToken, verifier);

    final OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL, service);
    service.signRequest(accessToken, oauthRequest);

    final Response resourceResponse = oauthRequest.send();

    logger.info("code:{}", resourceResponse.getCode());
    logger.info("body:{}", resourceResponse.getBody());
    logger.info("message:{}",resourceResponse.getMessage());

    final JSONObject obj = new JSONObject(resourceResponse.getBody());
    logger.info("json:{}" ,obj.toString());

    String twitterId = obj.getString("id_str");
    String name = obj.getString("name");
    String profileImageUrl = obj.getString("profile_image_url");

    model.addAttribute("id", twitterId);
    model.addAttribute("name", name);
    model.addAttribute("imageUrl", profileImageUrl);

    request.getSession().setAttribute("TWITTER_ACCESS_TOKEN", accessToken);

    return "twitter/profile";
  }

  @RequestMapping(value ="/profile", method = RequestMethod.GET)
  public String profile(HttpServletRequest request, HttpServletResponse response, Model model) {
    logger.debug("profile");

    OAuthService service = new ServiceBuilder()
      .provider(TwitterApi.Authenticate.class)
      .apiKey(YOUR_API_KEY)
      .apiSecret(YOUR_API_SECRET)
      .callback(HOST + CALLBACK_URL)
      .build();

    final Token accessToken = (Token) request.getSession().getAttribute("TWITTER_ACCESS_TOKEN");

    final OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL, service);
    service.signRequest(accessToken, oauthRequest);

    final Response resourceResponse = oauthRequest.send();

    logger.info("code:{}", resourceResponse.getCode());
    logger.info("body:{}", resourceResponse.getBody());
    logger.info("message:{}",resourceResponse.getMessage());

    final JSONObject obj = new JSONObject(resourceResponse.getBody());
    logger.info("json:{}" ,obj.toString());

    String twitterId = obj.getString("id_str");
    String name = obj.getString("name");
    String profileImageUrl = obj.getString("profile_image_url");

    model.addAttribute("id", twitterId);
    model.addAttribute("name", name);
    model.addAttribute("imageUrl", profileImageUrl);

    return "twitter/profile";
  }

}
profile.html
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
  <title>twitter - profile</title>
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="col-md-12 well">
        <h3>user profile</h3>
        <ul>
          <li th:text="${id}">id</li>
          <li th:text="${name}">name</li>
          <li><img th:src="${imageUrl}"></img></li>
        </ul>
        <h3>link</h3>
        <ul>
          <li><a href="/" th:href="@{/}">top</a></li>
        </ul>
      </div>
    </div>
  </div>
</body>
</html>
GET oauth/authorize

毎回認証画面が表示されます。

providerにTwitterApi.classを指定するとこのAPIが使用されます。

OAuthService service = new ServiceBuilder()
  .provider(TwitterApi.class)
  .apiKey(YOUR_API_KEY)
  .apiSecret(YOUR_API_SECRET)
  .callback(HOST + CALLBACK_URL)
  .build();
api
https://api.twitter.com/oauth/authorize
GET oauth/authenticate

一度認証を行えば次から表示されません。

providerにTwitterApi.Authenticate.classを指定するとこのAPIが使用されます。

OAuthService service = new ServiceBuilder()
  .provider(TwitterApi.Authenticate.class)
  .apiKey(YOUR_API_KEY)
  .apiSecret(YOUR_API_SECRET)
  .callback(HOST + CALLBACK_URL)
  .build();
api
https://api.twitter.com/oauth/authenticate
GET account/verify_credentials

ユーザーのプロフィールを取得するAPI endpointです。

api
https://api.twitter.com/1.1/account/verify_credentials.json

メモ

OAuthについて

15
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
22