この記事で説明すること
spring-boot, spring-security, spring-socialを使ってソーシャルログインを実装したので、そのサンプルを利用してどのように実装したかを解説します。長くなると思うので自分が興味のあるChapterだけ選んで読んでみてください。
今回のサンプルでは、ソーシャルログインだけでなく、userid/passwordを入力してログインすることもできます。
経緯
spring-bootやspring-securityははじめてのSpring Bootで勉強した。
この本では、spring-boot, securityを使って、ログイン/ログアウトができる名簿アプリを作るのだが、そこに 「Sign in with Twitter」の機能も付けたくなり、つけようとしたが思いの外苦労してしまった…
私はこの苦労をこの世から根絶すべく、当記事の執筆に踏み切ったのである。
サンプルについて
ソースコード
こちらです。 https://github.com/PonzyPon/spring-boot-social-demo
サンプル君のできること
- 独自のID/Passwordでログインできます
- Twitterでログインできます
- Twitterでログインした際にそれがそのユーザーにとって初めてのログインだった場合、裏で暗黙的にユーザー登録をします
- ログインしたら、画面左上にそのユーザーの名前を表示できます
- サーバー側ロジックで現在ログイン中のユーザーのUserIdを取得できます
UserContext#getUserId
サンプル君のできないこと
- ユーザー登録機能は実装していません。あらかじめ
V1__create-tables.sql
にてuser1というサンプルユーザーを作成しています
サンプル君のDB設計
このサンプルでは、独自ID/Passwordでのログインと、ソーシャルログインのどちらもを実現したかった。そのため2つのテーブルを作成した。ユーザーのマスタであるUser
テーブルと、ソーシャルログイン情報を保持するUserConnection
テーブルである。User#user_id
とUserConnection#userId
は同じ物。この値を使って連結する。
User テーブル
カラム | 型 | 補足 |
---|---|---|
user_id | VARCHAR | primary key |
username | VARCHAR | これもUNIQUE。 ログイン時にユーザーが入力する |
display_name | VARCHAR | |
encoded_password | VARCHAR |
UserConnection テーブル
カラム | 型 |
---|---|
userId | VARCHAR |
providerId | VARCHAR |
providerUserId | VARCHAR |
rank | INT |
displayName | VARCHAR |
profileUrl | VARCHAR |
imageUrl | VARCHAR |
accessToken | VARCHAR |
secret | VARCHAR |
refreshToken | VARCHAR |
expireTime | BIGINT |
このUserConnectionテーブルだが、実はもうこの構成はほぼ固定。このテーブルを操作するソースコードは自分では全く書いておらず、全てspring任せ。spring-frameworkにこんな感じでsqlのサンプルが提供されている。コピペして使おう。
ソースコード解説
spring-securityとspring-socialを使っている中でよく分からないのが俺の責務はどこだ?ということ。つまり、springがやってくれる部分と自分がやるべき部分との境界が分かりにくい。なのでその辺を含めて解説していく。
処理フローとその責任者
ユーザーログイン
UserをDBからロードする部分はspringはやってくれない。だからそこはUserDetailService
というInterfaceを実装する必要がある。そうしておけば、その実装クラスが勝手に呼ばれてspringがUserのロード処理を行ってくれる。
UserDetailsServiceImpl
のソースはこちら
ソーシャルログイン
ソーシャルログインの場合は、SNSアカウント情報を保持しているUserConnection
テーブルに対しての存在確認や、データの登録が必要となる。ただし、これは全てspringがやってくれる。我々開発者は、Userの作成処理と、そのUserテーブルからのロード処理をやってやればよい。
ConnectionSignUpImpl
のソースはこちら
SocialUserDetailsServiceImpl
のソースはこちら
その他のクラス
SignupService
とUserContext
は必須ではない
基本的には最小構成でソーシャルログインのサンプルを作成しようとしているので、不要なクラスは無いが、例えばSignupService
は作成しなくてもよい。このInterfaceは自分で作成したものだし、SignupServiceImpl#createUser
の処理はConnectionSignUpImpl#execute
に書いてしまってもよい。今回は、今後ユーザーの登録機能が付けば別クラスに分けることになるだろうと思ってクラスを分けただけ。
また、UserContext
はログインユーザーのuserIdを簡単に取得できるよう作った便利クラス。無くてもソーシャルログインは動作する。
springの設定はSecurityConfigで
上でusernameとpasswordを受け取って認証する処理はspring任せという話をしたが、その設定はSecurityConfig
で行っている。
解説はここまで。ここから下はサンプルの動かし方やいじくり方を説明します。
サンプル君の動かし方
(自分のtwitterアプリのConsumer Key
とConsumer Secret
がある方は3から)
twitter でアプリを登録する (https://apps.twitter.com/)
こんな感じで入力してください。
アプリをCreateしたら、twitter アプリの
Consumer Key
とConsumer Secret
を( ..)φメモメモしておく
git clone https://github.com/PonzyPon/spring-boot-social-demo.git
src/main/resources以下のapplication.properties.sampleをコピーしてapplication.propertiesを作る
application.properties内の
spring.social.twitter.app-id
とspring.social.twitter.app-secret
にそれぞれ上で( ..)φメモメモしたConsumer Key
とConsumer Secret
を書くプロジェクトのルート(pom.xmlがある場所)で
mvnw spring-boot:run
を実行する(mavenインストール済の人はmvn spring-boot:runでOK)localhost:7000にアクセスする
(*^-^*)
(※もし、Twitterログイン時にエラーが出たらまずはTwitterアプリのCallback URL Locked
をNoにしてみてください。それでダメならググって解決するしかないのです(^○^))
サンプル君のいじり方
Twitterでログインした後ツイートしたい
HomeController#writeTweetIdIfLoggedInWithTwitter
というメソッドをデモ用に作成してあり、もしTwitterでログインしている場合は、そのアカウントのタイムラインの最新のつぶやきのtweetIdを取得しコンソールに書き出している。ここで使われているtwitter
変数を使えばつぶやきも可能なのだ。サンプルとしてソースに書いてあるのでコメントアウトして試してみてほしい
// Hello!! とツイートする
twitter.timelineOperations().updateStatus("Hello!!");
現在ログインしているユーザーのIDを取得したい
UserContext#getUserId
を呼び出してくれ。誰もログインしていない場合もあるから、戻り値はOptional<String>
だ。使うときはこんなイメージ。
public class Sample {
@Autowired
private final UserContext userContext;
public Data getData() {
dao.getData(userContext.getUserId().get());
}
}
おわりに
このサンプルを作る前の自分が見たら嬉しくなるような記事をイメージして書きました。もし間違っているところや意見・質問あればよろしくお願いします。以上です。