概要
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
参考
- [scribejava] (https://github.com/scribejava/scribejava)
- [Google Developers] (https://developers.google.com/)
- [Facebook開発者 - 開発者向けFacebook] (https://developers.facebook.com/)
- [Twitter Developers] (https://dev.twitter.com/)
準備
それぞれのサービスでアプリケーションを登録して認証に使う情報(クライアントIDやクライアントシークレットキーなど)を手に入れます。
アプリケーションの登録
アプリケーション登録時に重要な項目はCallback URL(Redirect URL)です。ローカル開発環境で試すので登録内容は下記のようになります。
SNS | Site URL | Callback URL (Redirect URL) |
---|---|---|
http://localhost:9000 | http://localhost:9000/google/callback | |
http://localhost:9000 | http://localhost:9000/facebook/callback | |
実際にアクセスできるURL | http://127.0.0.1:9000/twitter/callback |
プロジェクトが作成済み且つ、OAuth同意画面が設定済みという前提で説明を進めます。
[Google Developers console] (https://console.developers.google.com/home)にアクセスしAPI Managerで新しい認証情報を作成します。
図の様に作成する認証情報にOAuthクライアントIDを選択します。
アプリケーションの種類に"ウェブアプリケーション"を選択します。
"名前"、"承認済みのJavaScript生成元"、"承認済みのリダイレクトURI"を入力します。
-
承認済みのJavaScript生成元
:http://localhost:9000
-
承認済みのリダイレクトURI
:http://localhost:9000/google/callback
(モザイクをかけていますが)図のようにクライアントIDとクライアントシークレットが発行されます。
[開発者向けFacebook] (https://developers.facebook.com/)にアクセスし、ページ右上のメニューより"Add a New App"を選択します。
プラットフォームの選択画面で"ウェブサイト"を選択します。
アプリケーション名を入力します。
アプリケーションのカテゴリを選択します。
TOPページにアクセスしなおして(登録内容が直ぐに反映されないので)、ページ右上のメニューより作成したアプリケーションを選択します。
ダッシュボードの左側メニューよりTest Appsを選択し、テストアプリケーションを作成します。
テストアプリケーションの名前を入力します。今回はデフォルト値のままで登録しました。
SettingsメニューのBasicタブで"+Add Platform"をクリックします。
WebSiteを選択します。
App DomainsとSite URLを入力します。
-
Site URL
:http://localhost:9000
SettingsメニューのAdvancedタブで"Valid OAuth redirect URIs"を入力し、Save Changesボタンをクリックします。
"Valid OAuth redirect URIs"には複数のURIを指定できます。
-
Valid OAuth redirect URIs
:http://localhost:9000/facebook/callback
AppIDとApp Secretはダッシュボードで確認できます。
[Twitter Application Management] (https://apps.twitter.com/)にアクセスし、"Create New App"をクリックします。
Application Detailsに必要な情報を入力します。
Websiteには、実際にアクセスできるページ(localhostなどのローカルでは駄目なようです)を指定する必要があります。今回はQiitaの自分のプロフィールページを仮に入力しました。
Callback URLにもlocalhostなどは入力できませんが、IPアドレスは入力可能なので127.0.0.1
としました。
-
Callback URL
:http://127.0.0.1:9000/twitter/callback
Permissionsタブでアクセスタイプを"Read Only"に変更します。
Consumer KeyとConsumer Secret(図ではモザイクにしています)はKey and Access Tokensタブで確認できます。
アプリケーションの開発
使用するScribe OAuthライブラリの依存関係をpom.xmlに追加します。
<dependency>
<groupId>com.github.scribejava</groupId>
<artifactId>scribejava-apis</artifactId>
<version>2.0.1</version>
</dependency>
また、APIのレスポンスがjsonなのでパースするjsonライブラリも追加します。
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20151123</version>
</dependency>
雛形アプリケーションを作成
プロジェクト名: sbsns-example
mavenでアプリケーションの雛形を作成
> mvn archetype:generate -DgroupId=com.example.sbsns -DartifactId=sbsns-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
> cd sbsns-example
> mvn eclipse:eclipse
eclipseにインポート
- メニューバーの"File" → "Import..." → "Maven" → "Existing Maven Projects"を選択します。
- プロジェクトのディレクトリを選択し、"Finish"ボタンをクリックします。
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を作成します。
# 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"に指定しました。
<?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>
ビルド
この時点で動作検証を兼ねてビルドします。
> mvn package
ビルドが成功したら生成したjarファイルを実行します。
コマンドプロンプトに"Hello World!"と表示されれば成功です。
> cd target
> java -jar sbsns-example-1.0-SNAPSHOT.jar
Hello World!
サンプルアプリケーションの構成
ディレクトリ/ファイル
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
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ページのリンクを表示します。
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";
}
}
<!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>
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";
}
}
<!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 Sign-In for server-side apps] (https://developers.google.com/identity/sign-in/web/server-side-flow)
- [OAuth 2.0 Scopes for Google APIs] (https://developers.google.com/identity/protocols/googlescopes)
Google's OAuth 2.0 endpoint
https://accounts.google.com/o/oauth2/auth
userinfo endpoint
ユーザーのプロフィールを取得するAPI endpointです。
https://www.googleapis.com/oauth2/v1/userinfo
Google+のプロフィールを取得する場合は下記のendpointを使用します。またdevelopers consoleでGoogle+のAPIを有効化しておく必要があります。
https://www.googleapis.com/plus/v1/people/me
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";
}
}
<!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です。
https://graph.facebook.com/v2.5/me
- [Permissions Reference - Facebook Login] (https://developers.facebook.com/docs/facebook-login/permissions)
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";
}
}
<!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>
- [Application Permission Model] (https://dev.twitter.com/oauth/overview/application-permission-model)
- [API Rate Limits] (https://dev.twitter.com/rest/public/rate-limiting)
[GET oauth/authorize] (https://dev.twitter.com/oauth/reference/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();
https://api.twitter.com/oauth/authorize
[GET oauth/authenticate] (https://dev.twitter.com/oauth/reference/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();
https://api.twitter.com/oauth/authenticate
[GET account/verify_credentials] (https://dev.twitter.com/rest/reference/get/account/verify_credentials)
ユーザーのプロフィールを取得するAPI endpointです。
https://api.twitter.com/1.1/account/verify_credentials.json
メモ
OAuthについて
- [OAuth2.0の備忘録的まとめ] (http://www.ari-hiro.com/blog/2012/12/30/oauth2-summary)
- [Zusaar開発者に学ぶソーシャルアプリ入門一覧] (http://codezine.jp/article/corner/459)
- [グーグルのAPIを使うときに欠かせないGoogle OAuthの作り方と使い方 (1/3)] (http://www.atmarkit.co.jp/ait/articles/1509/15/news017.html)