Play + pac4jエントリー一覧
PlayFramework + pac4jでOAuthを使う ①Twitter認可
PlayFramework + pac4jでOAuthを使う ②DB連携
本記事のゴール
取得したプロファイル情報をDBに格納し、突合せによるログインログアウトを実装
全コード
実装
方針
-
ユーザーはSNSIDでDB内の一意性を確保
-
セッションにはクレデンシャル情報を入れない
Ebeanの準備
app/build.sbt
lazy val root = (project in file(".")).enablePlugins(PlayJava, PlayEbean)
app/project/plugins.sbt
アンコメント
// Play Ebean support, to enable, uncomment this line, and enable in your build.sbt using
// enablePlugins(PlayEbean).
addSbtPlugin("com.typesafe.sbt" % "sbt-play-ebean" % "3.0.2")
app/conf/application.conf
default.driverとdefault.urlをアンコメント
ebean.default = ["models.entity.*"]を追記
...
db {
# You can declare as many datasources as you want.
# By convention, the default datasource is named `default`
# https://www.playframework.com/documentation/latest/Developing-with-the-H2-Database
default.driver = org.h2.Driver
default.url = "jdbc:h2:mem:play"
#default.username = sa
#default.password = ""
# You can turn on SQL logging for any datasource
# https://www.playframework.com/documentation/latest/Highlights25#Logging-SQL-statements
#default.logSql=true
}
ebean.default = ["models.entity.*"]
...
DBエンティティ
app/models/entity/BaseEntity.java
全てのエンティティの親クラス
IDによるプライマリーキーと作成日時と更新日時を設定
package models.entity;
import java.time.LocalDateTime;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import com.avaje.ebean.Model;
import com.avaje.ebean.annotation.CreatedTimestamp;
import com.avaje.ebean.annotation.UpdatedTimestamp;
@MappedSuperclass
public class BaseEntity extends Model {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
@CreatedTimestamp
public LocalDateTime create_date;
@UpdatedTimestamp
public LocalDateTime update_date;
}
app/models/entity/User.java
SNSのユーザーを管理するエンティティ
SNSIDは@Column(unique = true)で一意性を保証する
package models.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
@Entity
public class User extends BaseEntity {
/* SNSが提供する一意なID */
@Column(unique = true)
public String sns_id;
/* ユーザーネーム */
public String name;
/* メールアドレス */
public String mail;
/* パスワード */
public String password;
public static Finder<Long, User> finder = new Finder<Long, User>(User.class);
}
サービスクラス
app/models/service/user/UserService.java
CommonProfile#getId()で認可したユーザーのSNSIDが取得できる
...
/**
* SNS経由の登録orログイン処理
*
* @param profile
* @return
*/
public Optional<User> createOrLogin(CommonProfile profile) {
// DBに同じSNSIDのユーザーが存在したら登録せずログイン
Optional<User> userOps = userModelService.findBySnsId(profile.getId());
if(userOps.isPresent()){
return userOps;
}
// 新規作成処理
User user = new User();
user.sns_id = profile.getId();
user.name = profile.getDisplayName();
return userModelService.save(user);
}
...
app/models/service/user/UserModelService.java
SNSIDでDBを検索
レコードが存在すればそのユーザーのエンティティを返し、存在しなければemptyを返す
...
public Optional<User> findBySnsId(String snsId) {
return Optional.ofNullable(snsId).map(sns_id -> User.finder.where().eq("sns_id", sns_id).findUnique());
}
...
コントローラー
app/controller/LoginController.java
public class LoginController extends Controller {
@Inject
UserService userService;
@Inject
protected PlayCacheStore playCacheStore;
@Inject
protected PlaySessionStore playSessionStore;
private List<CommonProfile> getProfiles() {
final PlayWebContext context = new PlayWebContext(ctx(), playSessionStore);
final ProfileManager<CommonProfile> profileManager = new ProfileManager<>(context);
return profileManager.getAll(true);
}
private void storeUserId(String sessionId, String userId) {
final PlayWebContext context = new PlayWebContext(ctx(), playSessionStore);
playCacheStore.set(context, sessionId, userId);
}
@Secure(clients = "TwitterClient")
public Result twitterLogin() {
Optional<User> userOps = userService.createOrLogin(getProfiles().get(0));
if (!userOps.isPresent()) {
return badRequest("FAILURE");
}
storeUserId(session(org.pac4j.core.context.Pac4jConstants.SESSION_ID), String.valueOf(userOps.get().id));
return ok(protectedIndex.render(getProfiles()));
}
}
@Inject protected PlayCacheStore playCacheStore;
Cacheを使用するためのフィールド
private void storeUserId(String sessionId, String userId)
CacheにUserのIDを保存するためのメソッド
storeUserId(session(org.pac4j.core.context.Pac4jConstants.SESSION_ID), String.valueOf(userOps.get().id));
セッションIDの値をキーにしてUserのIDをCacheに保存
これで特定のセッションIDを持つユーザーがDB内のどのUserかわかるようになる
セッションとキャッシュの図解
ブラウザ
認可したユーザーのブラウザには、pac4jSessionIdをキーとしてランダムなUUIDが紐付くクッキーが作られる
サーバー
サーバー内のCacheには、UUIDをキーとしてそのユーザーのIDが紐付く
動作確認
Skip...
Next
- pac4jを経由したフォームからの登録
or - ロールによる権限の管理
or - authorizersの応用