LoginSignup
0
0

More than 5 years have passed since last update.

PlayFramework + pac4jでOAuthを使う ②DB連携

Last updated at Posted at 2016-09-17

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によるプライマリーキーと作成日時と更新日時を設定

BaseEntity.java
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)で一意性を保証する

User.java
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が取得できる

UserService.java
...

    /**
     * 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を返す

UserModelService.java
...

    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

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かわかるようになる

セッションとキャッシュの図解

playsessioncache.png

ブラウザ

認可したユーザーのブラウザには、pac4jSessionIdをキーとしてランダムなUUIDが紐付くクッキーが作られる

サーバー

サーバー内のCacheには、UUIDをキーとしてそのユーザーのIDが紐付く

動作確認

Skip...

Next

  • pac4jを経由したフォームからの登録 or
  • ロールによる権限の管理 or
  • authorizersの応用
0
0
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
0
0