はじめに
昔、Firebaseでたてに、すこしやってみて、sumみたいのができなくて、チャットあぷりなんだ・・・で、
終わって以降、さわってなかったので、どうも集計らしきができるらしいので再度チャレンジ・・
Firebase
Firebaseは、2011年にFirebase, Inc.が開発したモバイル・Webアプリケーション開発プラットフォーム
2014年にGoogleに買収されたらしい。
サービス
Firebase Cloud Messaging
Firebase Authentication
Firebase Realtime Database
Cloud Firestore
Firebase Storage
Firebase Hosting
データベース系は2つある・・・
今回の目的は、Firestore Database Cloud Firestore
Realtime Databaseは別ものぽい
Realtime DatabaseのJavascriptの記述例
var db = firebase.database();
db.ref("user_data").push({
name: "名前",
zip: "485",
});
Cloud FirestoreのJavascriptの記述例
var db = firebase.firestore();
db.collection("user_data").add({
name: "名前",
zip: "485"
}).then((doc) => {
console.log("追加しました");
}).catch((error) => {
console.log(error);
});
ぐぐって、サンプル拾ったりする場合は、上記を注意してみるとよいかも・・
PHPなどのライブラリ等も、同様な感じになっているので・・
$realtimeDatabase = $factory->createDatabase();
$firestore = $factory->createFirestore();
無料利用枠/1日あたり
データの保存 1GBストレージ
ドキュメントの読み込み 50,000
ドキュメントの書き込み 20,000
ドキュメントの削除 20,000
Cloud Firestoreの場合。用語が・・・
コレクション→ドキュメント→コレクション
データベース→レコード→フィールド
コレクション+ドキュメント+コレクション+フィールド
| +フィールド
| +フィールド
+ドキュメント+コレクション+フィールド
+フィールド
+フィールド
認証とセキュリティ
Firebase Authenticationを使用しない場合・デバックモードで
下記のタグをさえ書いてしまえば、どこからでも登録できる?
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-firestore.js"></script>
<script>
var config = {
apiKey: "",
authDomain: "xxxxxxxxfirebaseapp.com",
databaseURL: "https://xxxxxxxxxx.firebaseio.com",
projectId: "xxxxxxxx",
storageBucket: "xxxxxxxx.appspot.com",
messagingSenderId: "",
appId: ":web"
};
//////////////////////////////////////////////////////////////
firebase.initializeApp(config);
var db = firebase.firestore();
db.collection("user_data").add({
name: "名前",
zip: "485"
}).then((doc) => {
console.log("追加しました");
}).catch((error) => {
console.log(error);
});
</script>
認証を使用した場合
ログインページを利用して入力されたID,パスワードか、下記の中でログイン情報を記述すると・・・セキュリティの意味がない...
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-firestore.js"></script>
<script>
var config = {
apiKey: "",
authDomain: "xxxxxxxxfirebaseapp.com",
databaseURL: "https://xxxxxxxxxx.firebaseio.com",
projectId: "xxxxxxxx",
storageBucket: "xxxxxxxx.appspot.com",
messagingSenderId: "",
appId: ":web"
};
//////////////////////////////////////////////////////////////
firebase.initializeApp(config);
email = "xxxxx";
password = "xxxxxx";
firebase.auth().signInWithEmailAndPassword(auth, email, password).then(function(result) {
var db = firebase.firestore();
db.collection("user_data").add({
name: "名前",
zip: "485"
}).then((doc) => {
console.log("追加しました");
}).catch((error) => {
console.log(error);
});
}).catch(function(error) {
console.log(error);
});
</script>
匿名ログイン
読み出しだけで制御すれば、あり?
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-firestore.js"></script>
<script>
var config = {
apiKey: "",
authDomain: "xxxxxxxxfirebaseapp.com",
databaseURL: "https://xxxxxxxxxx.firebaseio.com",
projectId: "xxxxxxxx",
storageBucket: "xxxxxxxx.appspot.com",
messagingSenderId: "",
appId: ":web"
};
//////////////////////////////////////////////////////////////
firebase.initializeApp(config);
firebase.auth().signInAnonymously().then(function(result) {
var id ="??";
var db = firebase.firestore();
db.collection("user_data").doc(id).get()
.then((doc) => {
var data = doc.data();
})
.catch((error) => {
console.log(error);
});
}).catch(function(error) {
console.log(error);
});
</script>
カスタムトークン認証・・
PHP内に、ログイン情報埋め込み、秘密鍵のjson埋め込み・ダウンロード禁止
PHPファイルの自サーバーアクセスのみ許可など・・・
あとは、トークン有効期間・・の制御?
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-firestore.js"></script>
<script>
(function ($) {
var config = {
apiKey: "",
authDomain: "xxxxxxxxfirebaseapp.com",
databaseURL: "https://xxxxxxxxxx.firebaseio.com",
projectId: "xxxxxxxx",
storageBucket: "xxxxxxxx.appspot.com",
messagingSenderId: "",
appId: ":web"
};
//////////////////////////////////////////////////////////////
firebase.initializeApp(config);
$.ajax({
url: "firebaseClientToken.php",
method: "POST",
success: function(token) {
firebase.auth().signInWithCustomToken(token)
.then(function (result) {
db = firebase.firestore();
//処理・・
})
.catch(function (error) {
alert("failed to anonymously sign-in"+error.code);
});
},
error: function(xhr) {
}
});
}(jQuery));
</script>
Authentication
Monthly active users 50k/month
1 か月のアクティブ ユーザー(SAML / OIDC) 50/month
今回のテーマ
Firebase Hostingからnode.jsとかしないで、サーバーサイドからアクセスできるのか・・検証・・・
レンタルサーバーとか想定・・
PHP側で接続アカウント隠蔽・トークンの作成とか・・できるのか・・とか・・
ぐぐると・・エンドユーザーもログインが必要な感じだけど・・
データベースのようなアカウント風でつかえるのか・・匿名ログインとかもあるようだけど・・
権限とか使って・・制限とか・・どうなのとか・・
Firebaseで準備
はじめに、当然Googleアカウントが必要なので、準備・・
下記からログインして
https://firebase.google.com/?hl=ja
下記からFirebaseプロジェクトを作る。
プロジェクト名をいれて、各種同意して続行する。
現在は、テストなのでアナリティクスは無効で、本番は検討が必要かと・・
作成ボタンを押すと下記が出るので・・続行・・
まずは、Hostingを選択
始めるを・・
ウェブアプリケーションにしたいからチェックして次へ
なんとく読んで・・次へ
テストなので適当に・・・w
ウェブアプリでしたいので、scriptタグを・・選択
貼り付けタグは、コピペして保存しておく・・サンプルもちょっとだけみて。。次へ・・・
わからんけど・・コンソールに進むとかを押してみる・・
Cloud Firestoreを選択して・・
メニューは、Firestore Databaseで中身は・・Cloud Firestoreなのね。。データベース作成を押してみる
何も考えず・・Tokyoで・・
テストなので、変なパーミッションではまるのいやなので・・テストモードで・・・
本番は、本番で・・・・
コレクション開始?!
コレクションID・・・
ドキュメントID・・・
まぁ。フィールド入れて・・・ここでは、構造とデータを登録する感じなので・・注意・・
ここから、APIを使う、アカウントを登録しておく・・Googleアカウントとかそのまま使うと・・
こわいし・・、
この時点で、プロジェクトの設定などを作成するGoogleアカウントとAPI、アプリを利用するアカウントの2個
出てきます・・ここでは、API利用、アプリ利用のアカウントを登録していく感じかと・・
なんか、ぐぐっても、古い画面ばかりで・・
ユーザを追加をぽっちっと・・・
ちょっとこの辺りの順序がさだかで、ないです・・
先に・・こっちでプロバイダーを押してから、ログイン情報登録だったと・・
メアドとパスワードで・・・とりえずね。
Firebaseをドキュメントみてたら。UIDよく出てきてたから...
コピーボタン出るし、、コピーしておく・・
ドキュメントで秘密鍵のjsonがってあったので、どこか。探して・・
プロジェクトの設定にあったので、メモ・・
とりあえず・・ダウンロード・・・・・迷路だね。。まじ・・
今のところ・・・下記の3つ?
Firebase Authentication
Cloud Firestore
Firebase Hosting
外部サーバー用のPHPを調べてみる・・
ぐぐってみると・・どれも古い・・動かない・・
おいらが悪いのか・・・
で、いろいろやって、動いたのが・・ここ・・
https://github.com/kreait/firebase-php
https://firebase-php.readthedocs.io/en/7.15.0/
上記も、firebase-php 3.0とか4.0の情報ばかりで・・
自分が。。下記したら、5.6で実行すると・・エラーの嵐・・・・
phpはあんま情報ないのね・
・
composer require kreait/firebase-php
とりえず・・下記でカスタムトークンが取得できた・・・
var_dumpでダンプしながら。デバックモードで見ながら・・・
var_dumpでみるとlocalIdがUIDぽいからもう少し・・改造できるかも・・
createFirestoreで接続する場合。gRPC PHP extensionの設定等が必要です。
gRPCを使わない・・ライブラリもあるようなので、kreait製じゃないものも選択も・・
あと、Authentication系はadmin、データベース操作系は、Clientといった表記・・
<?php
require __DIR__.'/vendor/autoload.php';
ini_set('display_errors', "On");
use Kreait\Firebase\Factory;
use Kreait\Firebase\ServiceAccount;
use Kreait\Firebase\Firestore;
use Kreait\Firebase\Auth;
//Firebase アプリケーションを初期化
$factory = (new Factory)
->withServiceAccount(__DIR__.'ダウンロードした秘密鍵のjson')
->withDatabaseUri('自分のDatabaseUriを記述');
var_dump($factory);
$auth = $factory->createAuth();
$uid = "コピペしたUID";
$customToken = $auth->createCustomToken($uid);
var_dump($customToken);
$token = $customToken->toString();
echo $token."<br>";
echo "---------------";
exit;
上記PHPでは、アクセスするたびに、新規でトークンが生成されます。
このライブラリーでは、1時間後にトークンが削除されました。
ダンプでは、refreshToken項目で、データは確認できるので・・削除も可能かと・・
システムで、リードだけであれば、現在のままでもよいかな・・
Authenticationするアカウントをリード専用にして、対応するか・・
書き込み用は、書き込み後、すぐに、refreshTokenを削除するとか・・・とか・・
リードとライトアカウント準備して権限設定して、継続か、処理後トークン削除か・・・・
あと、ダウンロードした秘密鍵のjsonをサーバーにアップする場合、自サーバー以外ダウンロードできないようにするか オリジンとか・・HTTP_X_REQUESTED_WITHとか・・
PHP内部に取り込んでしまうか・・・
HTMLとjavascript
上記で。ウェブアプリケーションのscriptタグでコピペしたもので
下記のHTMLでは、トークン固定しているので、該当トークンの有効性が確認できるかと・・
ここで、記載する、apiKey、projectId等は、認証するアカウント等とか必ずログイン機構で認証する前提であれば、
公開されていても問題ないそうです。今回の場合、PHP側にアカウントを埋め込みしているので、とりあえず、隠蔽できているのかね・・
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-firestore.js"></script>
<script>
var config = {
apiKey: "",
authDomain: "xxxxxxxxfirebaseapp.com",
databaseURL: "https://xxxxxxxxxx.firebaseio.com",
projectId: "xxxxxxxx",
storageBucket: "xxxxxxxx.appspot.com",
messagingSenderId: "",
appId: ":web"
};
//////////////////////////////////////////////////////////////
firebase.initializeApp(config);
</script>
現在は、直書きですが、ここをajax等でphpを呼び出してトークン取得とか・・・
phpは、リファラー等をみて、自サーバーのみ呼び出し可能にするなど・・クロスオリジン確認などなど。
リファラーは、セキュリティソフトなどで禁止されてたりするので工夫は必要・・・
<script>
var token = 'phpで取得したカスタムトークン';
firebase.auth().signInWithCustomToken(token)
.then(function (result) {
db = firebase.firestore();
db.settings({ timestampsInSnapshots: true });
})
.catch(function (error) {
});
db.collection("自分のコレクション名").get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
alert(doc.id);
});
});
</script>
次は・・sumみたいのを・・・・
ああああああ。。。。はまった・・・
どうも、javascript APIもバージョンがあって、V8系とV9では、記述がちがうと・・・・・・
こっちは、下記とはみて・・・・・・
うーむ、途中からはじめると。。。経緯わかんないから・・・・はまるね・・・・
https://firebase.google.com/docs/web/learn-more?hl=ja
どうも、名前空間方式(scriptで呼び出し,import compat)とモジュラー(import compatなしで呼び出し)で新しい機能は・・モジュラー方式でないと
使用ができないということらしい・・・・汗・・
下記のように・・compatがついたものはV8の記述ができるらしい・・あくまでも臨時でなくなる可能性あり・・って・・
importってことは、ES6かな、iphone7,ios10も数パーセントになってきてるから・・・ってこと?!
<script src="https://www.gstatic.com/firebasejs/10.13.1/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.13.1/firebase-firestore-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.13.1/firebase-auth-compat.js"></script>
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.14.0/firebase-app.js";
import { getFirestore, collection, doc, addDoc , getDoc,getAggregateFromServer, average, count, query ,where,orderBy} from "https://www.gstatic.com/firebasejs/10.14.0/firebase-firestore.js";
import { getAuth, signInWithCustomToken, signInAnonymously } from "https://www.gstatic.com/firebasejs/10.14.0/firebase-auth.js";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "",
authDomain: "xxxxxxxxfirebaseapp.com",
databaseURL: "https://xxxxxxxxxx.firebaseio.com",
projectId: "xxxxxxxx",
storageBucket: "xxxxxxxx.appspot.com",
messagingSenderId: "",
appId: ":web"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
// Create a reference to the user document
const Ref = doc(db, "コレクション名", "ドキュメントID");
// Fetch the document
const Snap = await getDoc(Ref);
if (Snap.exists()) {
console.log("tel:", Snap.data().tel);
console.log("email:", Snap.data().email);
console.log("data all:", Snap.data());
} else {
console.log("No such document!");
}
//追加
const addRef = collection(db, "rateing");
// 追加 ID自動生成
const newRef = await addDoc(addRef, {
id: id,
rateing: 3.5,
createdAt: new Date().toISOString()
});
//平均値
const coll = collection(db, 'rateing');
const snapshot = await getAggregateFromServer(coll, {
averagePopulation: average('rateing')
});
console.log('average: ', snapshot.data().averagePopulation);
</script>
Whereではまった。。
単一キーと複合キーとあるらしいが・・単一キーは自動で作成されると記載があったので
そのまま、下記のように書くと・・
エラーになる・・ブラウザーの開発環境でエラー内容にURLがあるので、そちらをそのまま、作成
するとエラーは治る・・どうも、whereするものと計算するものを両方いるのかな・・
ただ、whereを使用した場合、下記の場合平均値は、averagePopulationではなく。totalPopulationにはいってくるので
whereを付けた場合、ない場合で戻り値の確認が必要。。。同時にエラーと起きると混乱する・・・
const coll = collection(db, 'rateing');
const q = query(coll, where("id", "==", id));
const snapshot = await getAggregateFromServer(q, {
averagePopulation: average('rateing')
});
console.log('numberOfSales: ', snapshot.data().numberOfSales);
console.log('average: ', snapshot.data().averagePopulation);
console.log('totalPopulation: ', snapshot.data().totalPopulation);
const rt = snapshot.data().totalPopulation;
ここまでで、気づいたことは、名前空間方式とモジュラー方式では、モジュラー方式の方が、快適に動作する・・・
トークンの処理は・・・ログアウトしておくとか。。
トークン有効時間を、数分にするとか・・
const auth = getAuth();
signInWithCustomToken(auth, token).then((userCredential) => {
//コレクション処理して・・・
signOut(auth).then(() => {
// Sign-out successful.
}).catch((error) => {
// An error happened.
});
})
.catch((error) => {
});
jwt版
あとphp側だけど・・やってて気が付いてたけど、jwtでいいような気がしてたのでテストしてみた。
Kreait\Firebaseだと、不要なファイルおおいので・・
composerする
composer require firebase/php-jwt
<?php
require __DIR__.'/vendor/autoload.php';
#ini_set('display_errors', "On");
use Firebase\JWT\JWT;
# if(!(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')) {
# error_msg("許可されていません");
# return;
# }
$uid = "コピペしたUID";
$jsonInfo = json_decode(file_get_contents(__DIR__.'ダウンロードした秘密鍵のjson'), true);
$private_key = $jsonInfo['private_key'];
$service_account_email = $jsonInfo['client_email'];
$custom_token = create_custom_token($uid, true);
echo $custom_token;
function create_custom_token($uid, $is_premium_account) {
global $service_account_email, $private_key;
$now_seconds = time();
$payload = array(
"iss" => $service_account_email,
"sub" => $service_account_email,
"aud" => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iat" => $now_seconds,
"exp" => $now_seconds+(60*60), // Maximum expiration time is one hour
"uid" => $uid,
"claims" => array(
"premium_account" => $is_premium_account
)
);
return JWT::encode($payload, $private_key, "RS256");
}
上記で、signInWithCustomTokenでサイインできました。OKぽいですね。
テストがおわったら・・・
下記でテストモードを選択したので下記書き換えを行う・・
設定時は、下記2024, 10, 20まで、読み書きできるみたいな設定です。
テスト環境を伸ばす場合は、単純に期限を変更で問題なさそうですね。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// This rule allows anyone with your Firestore database reference to view, edit,
// and delete all data in your Firestore database. It is useful for getting
// started, but it is configured to expire after 30 days because it
// leaves your app open to attackers. At that time, all client
// requests to your Firestore database will be denied.
//
// Make sure to write security rules for your app before that time, or else
// all client requests to your Firestore database will be denied until you Update
// your rules
match /{document=**} {
allow read, write: if request.time < timestamp.date(2024, 10, 20);
}
}
}
https://firebase.google.com/docs/firestore/security/rules-structure?hl=ja
上記をみると・・下記がベースで
service cloud.firestore {
match /databases/{database}/documents {
//ここに各コレクションごとに制限をかく・・
}
}
こんな感じでコレクションごとに条件書いていく感じですかね・・・
match /ターゲットのコレクション名/{document} {
//条件を書く・・・
}
下記とか
allow read, write: if request.auth != null
とか?
allow read, write: if request.auth.uid != null
何となく・・・ってことで。。ここまでで・・・
ここはもう少し。。深堀しないとまずそう・・・・
さいごに
やりながら書いてたので・・下の方が・・、参考になる?!・・
Ver8からVer 10の記述になっていますので注意してね。
途中から、やりはじめると。。知ってて・・当たり前の部分がわからない・・・
あと、セキュリティ周りに気しないと・・・はまる予感がします。