Goal
ログイン後における、リンクとレイアウトの変更を実装する。
登録と同時にログイン状態にする。
サインアウト機能を実装する。
登録と同時にログイン状態にする。
サインアウト機能を実装する。
Dev-Environment
OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4
Phoenix Framework: v0.13.1
PostgreSQL: postgres (PostgreSQL) 9.4.4
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4
Phoenix Framework: v0.13.1
PostgreSQL: postgres (PostgreSQL) 9.4.4
Wait a minute
アカウント設定の保存カラム項目を追加(マイグレーション)Base64によるトークン生成(SecureRandom)トークンの暗号化(SHA1)before_insert機能による登録時のトークン追加ログイン時にトークンの更新をするトークンから現在のユーザを参照できるようにするセッションハイジャックについて
具体的な内容は以下の項目です。
- レイアウトの変更
- リンクの変更(Sign-inからSign-outへリンクと表示の変更、プロファイルページへのリンクを表示)
- 登録と同時にサインイン
- サインアウト
Index
- Change links and layout
- Sign-out
- After registration, sign-in
- Extra
1. Change links and layout
ログイン後、リンクとレイアウトを変更しましょう!
レイアウトのヘッダーに他のページへのリンクを表示しています。
しかし、ログインしたのにLoginまたはSign-inのリンクが表示されているのは変です。
また、現在はログインユーザ自身の情報を表示するページへのリンクがありません。
ここでは、上記二つの実装を行います。
しかし、ログインしたのにLoginまたはSign-inのリンクが表示されているのは変です。
また、現在はログインユーザ自身の情報を表示するページへのリンクがありません。
ここでは、上記二つの実装を行います。
ログインしているか否かの判断はどうしましょうか?
前回、現在のログインユーザを取得する機能を実装したのを覚えていますか?
前回、現在のログインユーザを取得する機能を実装したのを覚えていますか?
def current_user(conn) do
conn.assigns[:current_user]
end
ログインしていればユーザを返す。していなければnilが返ってきます。
Elixirでは、falseとnilは真偽値で偽と判断されます。
ここまで言えば分かりますね?
Elixirでは、falseとnilは真偽値で偽と判断されます。
ここまで言えば分かりますね?
そうです!
EExテンプレートでif文を利用して処理を分ければいいんです。
つまりこんな形に記述したいわけです。
EExテンプレートでif文を利用して処理を分ければいいんです。
つまりこんな形に記述したいわけです。
<%= if current_user(@conn) do %>
ログインしている時の処理...
<% else %>
ログインしていない時の処理...
<% end %>
ファイル: web/templates/layout/header.html.eex
<header class="navbar navbar-inverse">
<div class="navbar-inner">
<div class="container">
<a class="logo" href="<%= page_path(@conn, :index) %>"></a>
<ul class="nav nav-pills pull-right">
<li><%= link "Home", to: static_pages_path(@conn, :home) %></li>
<li><%= link "Help", to: static_pages_path(@conn, :help) %></li>
<li><%= link "About", to: static_pages_path(@conn, :about) %></li>
<li><%= link "Contact", to: static_pages_path(@conn, :contact) %></li>
<%= if current_user(@conn) do %>
<li><%= link "Sign out", to: session_path(@conn, :delete), method: :delete %></li>
<li><%= link "Profile", to: user_path(@conn, :show, current_user(@conn).id) %><li>
<% else %>
<li><%= link "Sign-in", to: session_path(@conn, :new) %></li>
<% end %>
<li><a href="http://www.phoenixframework.org/docs">Phoenix Get Started</a></li>
</ul>
</div> <!-- container -->
</div> <!-- navbar-inner -->
</header>
<h2>
<p class="alert alert-info" role="alert" style="text-align: center;"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert" style="text-align: center;"><%= get_flash(@conn, :error) %></p>
</h2>
Sign-outリンクが浮いてデザインが崩れてる・・・
修正は後に行います。
今は早々にログアウトを実装してしまいましょう。
修正は後に行います。
今は早々にログアウトを実装してしまいましょう。
2. Sign-out
ログアウト機能を実装します。
当初セッションのルーティングを作成する際に、
deleteアクションをログアウトに利用することを想定していましたね。
セッションコントローラのdeleteアクションにログアウトを実装します。
deleteアクションをログアウトに利用することを想定していましたね。
セッションコントローラのdeleteアクションにログアウトを実装します。
ファイル: web/controllers/session_controller.ex
def delete(conn, _params) do
conn
|> put_flash(:info, "Logout now! See you again!!")
|> delete_session(:user_id)
|> redirect(to: static_pages_path(conn, :home))
end
特に難しい部分はありませんね。
それぞれ行っていることは・・・
それぞれ行っていることは・・・
- ログアウトをしたメッセージの表示
- セッションのデータを削除
- リダイレクトで/homeのページを表示
上記を行っているだけです。
3. After registration, sign-in
今回の記事で最後の機能、ユーザ登録後のログイン処理を実装します。
ファイル: web/controllers/user_controller.ex
createアクションを以下のように編集して下さい。
createアクションを以下のように編集して下さい。
def create(conn, %{"user" => user_params}) do
changeset = SampleApp.User.changeset(%SampleApp.User{}, user_params)
if changeset.valid? do
Repo.insert(changeset)
user = Ecto.Changeset.get_field(changeset, :email)
|> SampleApp.User.find_user_from_email
conn
|> put_flash(:info, "User registration is success!!")
|> put_session(:user_id, user.id)
|> redirect(to: static_pages_path(conn, :home))
else
render(conn, "new.html", changeset: changeset)
end
end
Description:
少々冗長な処理をしていますが・・・
changesetからemailの値を取得し、
そのemailを利用して先ほど挿入したユーザのデータを取得しています。
少々冗長な処理をしていますが・・・
changesetからemailの値を取得し、
そのemailを利用して先ほど挿入したユーザのデータを取得しています。
何故こんな面倒な手順を踏んでいるのかと言いますと。
一度、DBにデータを挿入しないとidの値が生成されないからです。
簡単に順番を記述します。
一度、DBにデータを挿入しないとidの値が生成されないからです。
簡単に順番を記述します。
- changeset(id値なし)
- DB insert(id値生成)
- データ取得(id値あり)
そのため先ほどのような処理になっています。
4. Extra
実装途中でこんな記述をしたのですが・・・
<%= link "Sign-out", to: session_path(@conn, :delete, current_user(@conn)), method: :delete %>
エラーが出てしまいました。
(Protocol.UndefinedError) protocol Enumerable not implemented for...
参考にした記事を見てみると・・・
<%= link "Remove", to: user_path(@conn, :delete, @user), method: :delete %>
対応コントローラのアクション
def delete(conn, %{"id" => id}) do
渡し方の問題のようです。
(コントローラ側は変わりません)
(コントローラ側は変わりません)
<%= link "Sign-out", to: session_path(@conn, :delete, id: current_user(@conn)), method: :delete %>
デザインの問題はCSSではない方法で解決しました。
deleteで渡していましたが、
別にパラメータを渡すこともないのでgetで通信を行うように変更しました。
(アクション名の変更はしてません)
deleteで渡していましたが、
別にパラメータを渡すこともないのでgetで通信を行うように変更しました。
(アクション名の変更はしてません)
ファイル: web/router.ex
delete "/signout", SessionController, :delete
get "/signout", SessionController, :delete
Speaking to oneself
第八章も終わりが見えてきましたね。
分割してしまえば、そんなに難しい内容でもなかった気がします。
分割してしまえば、そんなに難しい内容でもなかった気がします。
最後に「アカウント設定の保存」を実装すれば、
第八章もまとめ記事を作成して終わりです。
第八章もまとめ記事を作成して終わりです。
アクセス制御についてなのですが、
今回の章で実施するかと思ったのですが、
第九章で実装する内容だったようです。
今回の章で実施するかと思ったのですが、
第九章で実装する内容だったようです。
それでは、次の記事も待っていてくれると嬉しいです。