Cet article approfondit ma compréhension en écrivant un article de commentaire du tutoriel Rails pour solidifier davantage mes connaissances Cela fait partie de mon étude. Dans de rares cas, un contenu ridicule ou un contenu incorrect peut être écrit. Notez s'il vous plaît. J'apprécierais que vous me disiez implicitement ...
La source Tutoriel Rails Chapitre 6
Puisque nous avons rendu possible la création d'un utilisateur dans le chapitre précédent, nous allons créer un mécanisme pour se connecter avec l'utilisateur créé.
Étant donné que HTTP est sans état (il n'y a pas d'état), il n'est pas possible d'utiliser les informations de requête précédentes. Il n'y a aucun moyen de conserver les données dans HTTP lorsque vous passez à une autre page. Les informations de l'utilisateur sont conservées en utilisant une "session" à la place.
Les cookies sont souvent utilisés pour implémenter cette session dans Rails Étant donné que les cookies sont des données textuelles stockées dans le navigateur et ne disparaissent pas même si la page est déplacée, les informations utilisateur peuvent être conservées.
La session est également implémentée en la reliant directement aux actions REST. En particulier
mouvement | action |
---|---|
Formulaire de connexion | new |
Processus de connexion | create |
Processus de déconnexion | destroy |
Connexion → Créer une session temporaire Déconnexion → Supprimer la session temporaire
Tout d'abord, créez un contrôleur pour la session.
rails g controller sessions new
La création d'un contrôleur avec la commande generate crée également une vue correspondante. Cette fois, la vue n'est nécessaire que pour la nouvelle action sur le formulaire de connexion Spécifiez uniquement nouveau avec la commande generate.
Mettez également à jour routes.rb. Bien que le routage vers la nouvelle action soit défini récrire.
get '/login', to:'sessions#new'
post '/login', to:'sessions#create'
delete '/logout', to:'sessions#destroy'
Modifiez le test généré automatiquement pour utiliser également la route nommée.
rails routes
Vous pouvez vérifier la liste des itinéraires en utilisant la commande.
Exercice
get login_pathreçoit un formulaire de connexion avec une requête GET.
post login_path```Envoie la valeur saisie dans le formulaire de connexion dans une demande de publication.
$ rails routes | grep sessions
login GET /login(.:format) sessions#new
POST /login(.:format) sessions#create
logout DELETE /logout(.:format) sessions#destroy
Dans le formulaire, nous avons implémenté le formulaire en utilisant form_with. Le formulaire de connexion ne crée pas de ressource Utilisateurs, il crée simplement une session L'objet modèle ne peut pas être spécifié. Dans ce cas
<%= form_with(url: login_path, scope: :session, local: true) do |f| %>
Envoyez une demande de publication à login_path par
En définissant scope :: session
Les valeurs des différents attributs seront stockées dans les paramètres [: session].
Par exemple, mot de passe
Stocké dans les paramètres [: session] [: mot de passe].
##### Exercice
1.
Il existe deux types de login_path, mais form_with est défini sur la requête POST par défaut.
Envoyez une requête POST à login_path. En d'autres termes, l'action de création est atteinte avec le POST login_path.
Dans le cas de GET, la nouvelle action est atteinte.
#### Recherche d'utilisateurs et authentification
Cette fois, créez un processus lorsque la connexion échoue.
```rb
def new
end
def create
render 'new'
end
def destroy
end
Si vous définissez le contrôleur comme ceci Lorsque vous soumettez le formulaire, il sera transféré à la nouvelle page tel quel.
Si vous essayez de soumettre le formulaire dans cet état
Vous pouvez voir que la clé de session a été ajoutée au hachage des paramètres et contient les clés de messagerie et de mot de passe.
params = { session: { password: "foobar", email: "[email protected]" }}
Il a une telle structure.
Avec ça
Recevez les informations d'identification de l'utilisateur des paramètres et recherchez l'utilisateur avec la méthode `` find_by ''
authenticate
Après avoir authentifié le mot de passe avec la méthode
Processus dans le flux de création d'une session.
Cliquez ici pour le contenu de l'action de création basée sur ceci
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
#Processus de redirection vers la page utilisateur(à partir de maintenant)
else
#Créez un message d'erreur.
render 'new'
end
end
&& est un opérateur de produit logique, et les deux ne sont vrais que lorsqu'ils sont vrais. La condition est que l'utilisateur existe et que son mot de passe soit authentifié.
>> user = nil
=> nil
>> user && user.authenticate("foobar")
=> nil
>> !!(user && user.authenticate("foobar"))
=> false
>> user = User.first
(1.4ms) SELECT sqlite_version(*)
User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "take", email: "[email protected]", created_at: "2020-06-14 02:57:10", updated_at: "2020-06-14 02:57:10", password_digest: [FILTERED]>
>> !!(user && user.authenticate("foobar"))
=> true
Créez un message flash lorsque la connexion échoue.
danger] = "Invalid email/password combination"
Si cela est laissé tel quel, le message restera même lorsque la page est remplacée par une autre page.
Parce que je réaffiche le formulaire avec le rendu.
Le rendu n'étant pas une redirection mais un rendu de la vue, le flash ne disparaît pas.
Je vais résoudre ce problème à partir de maintenant.
↓ Le flash est affiché sur les autres pages ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617753/4bbfd1e0-1e51-1de0-0b09-44394f9d9723.png)
#### Test flash
Créez un test d'intégration autour de la connexion. Cette fois, il y a un problème d'affichage du flash sur d'autres pages, donc cette erreur
Créez le test pour détecter d'abord et écrivez le code pour que le test réussisse.
Cliquez ici pour ce code
```rb
test "login with invalid information" do
get login_path
assert_template 'sessions/new'
post login_path , params:{ session: { email:"",password:"" }}
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
end
Les deux dernières lignes accèdent au chemin racine et vérifient que le flash est désactivé.
Bien sûr, une erreur se produira. Utilisez `` flash.now '' pour résoudre ce problème.
flash.disparaît maintenant lorsque vous passez à l'action suivante.
↑ Site de référence (https://qiita.com/taraontara/items/2db82e6a0b528b06b949)
J'ai été confus pendant un moment que tout devrait être flash.now, mais dans le cas de flash.now, lorsque redirect_to est utilisé, flash n'est jamais affiché en premier lieu
C'est fini. (Pour aller lire la prochaine action avec redirect_to)
Remplacez immédiatement `` flash '' par `` flash.now ''.
Ce changement passera le test.
##### Exercice
1.
Erreur d'authentification
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617753/341a1295-c6d4-e19b-1947-88e9044042f4.png)
Il disparaît lorsque vous passez à une autre page
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617753/09cc611d-ce89-3f2c-0015-a7018fba1f01.png)
### S'identifier
Ensuite, implémentez le traitement lorsque les informations de connexion sont valides.
Le flux de traitement est
S'authentifier, créer une session temporaire à l'aide de cookies si l'utilisateur est correct, terminer le processus de connexion,
Transit vers la page utilisateur correspondante.
Les sessions sont traitées séparément selon que vous êtes connecté ou non, même sur des pages autres que la page de connexion.
Incluez SessionsHelper dans ApplicationController afin qu'il puisse être utilisé en commun.
En faisant cela, il peut être utilisé en commun par tous les contrôleurs.
#### méthode log_in
Implémentez le processus de connexion en utilisant la méthode `` session '' définie dans Rails.
Si vous utilisez la méthode de session, la valeur associée à la clé est enregistrée dans des cookies temporaires.
Ces cookies temporaires expirent au moment où le navigateur est fermé, vous devrez donc vous connecter à chaque fois que vous le fermez.
La connexion étant censée être utilisée à divers endroits, définissez-la comme une méthode dans SessionsHelper.
#### **`sessions_helper.rb`**
```rb
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
end
Après cela, ajoutez-le à l'action de création et redirigez-le pour le terminer!
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in(user)
redirect_to user
else
flash.now[:danger] = "Invalid email/password combination"
render 'new'
end
end
Dans le cas de Chrome, la liste peut être affichée dans Cookies sur l'onglet Application des outils de développement. ↑ L'ID utilisateur chiffré est stocké (_sample_app_session)
Expire → L'heure d'expiration est écrite. Dans le cas d'une session temporaire, il s'agit d'une session, et vous pouvez voir qu'elle expire lorsque le navigateur est fermé.
Définissez la méthode `` utilisateur_actuel '' pour que l'ID utilisateur puisse être récupéré et que les informations utilisateur puissent être utilisées sur une autre page.
La recherche d'un objet utilisateur à partir de l'ID stocké dans la session, mais l'utilisation de find provoquera une erreur si l'utilisateur n'existe pas. Utilisez la méthode find_by.
def current_user
if session[:user_id]
User.find_by(id: session[:user_id])
end
end
De plus, en enregistrant ce résultat (objet utilisateur) dans la variable d'instance La référence à la base de données dans une requête ne peut être effectuée qu'une seule fois, ce qui entraîne une vitesse plus élevée.
def current_user
if session[:user_id]
if @current_user.nil?
@current_user = User.find_by(id: session[:user_id])
else
@current_user
end
end
end
Et||Vous pouvez écrire cette instruction if sur une seule ligne en utilisant l'opérateur.
@current_user = @current_user || User.find_by(id: session[:user_id])
De plus, écrire cette ligne sous forme abrégée donnera le code correct.
#### **`@current_user ||= User.find_by(id: session[:user_id])`**
>> User.find_by(id:"123")
(0.4ms) SELECT sqlite_version(*)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 123], ["LIMIT", 1]]
=> nil
(1) Si nil est donné, la méthode find_by renvoie nil et @current_user devient également nil.
② Lorsque
@ current_user``` est vide, lisez à partir de la base de données, mais lorsqu'il y a du contenu, remplacez la confiance (ne rien faire)
>> session = {}
=> {}
>> session[:user_id] = nil
=> nil
>> @current_user ||= User.find_by(id:session[:user_id])
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT ? [["LIMIT", 1]]
=> nil
>> session[:user_id] = User.first.id
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> 1
>> @current_user ||= User.find_by(id: session[:user_id])
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "take", email: "[email protected]", created_at: "2020-06-14 02:57:10", updated_at: "2020-06-14 02:57:10", password_digest: [FILTERED]>
>> @current_user ||= User.find_by(id: session[:user_id])
=> #<User id: 1, name: "take", email: "[email protected]", created_at: "2020-06-14 02:57:10", updated_at: "2020-06-14 02:57:10", password_digest: [FILTERED]>
>>
Modifiez dynamiquement la page affichée selon que vous êtes connecté ou non. Si vous vous branchez dans ERB selon que vous êtes connecté ou non, modifiez le lien. Définissez une méthode `` log_in? '' Qui renvoie une valeur logique à ceci.
def logged_in?
!current_user.nil?
end
Je veux m'assurer que current_user n'est pas vide, donc j'inverse la valeur logique avec!
Ensuite, modifiez la disposition de l'en-tête.
erb:_header.html.erb
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="container">
<%= link_to "sample app",root_path, id: "logo" %>
<nav>
<ul class="nav navbar-nav navbar-right">
<li><%= link_to "Home", root_path %></li>
<li><%= link_to "Help", help_path %></li>
<% if logged_in? %>
<li><%= link_to "Users", '#' %></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Account <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li>
<li><%= link_to "Settings", '#'%></li>
<li class="divider"></li>
<li>
<%= link_to "Log out", logout_path, method: :delete%>
</li>
</ul>
</li>
<% else %>
<li><%= link_to "Log in", login_path %></li>
<% end %>
</ul>
</nav>
</div>
</header>
J'ajoute un menu déroulant en utilisant une classe de bootstrap (telle que la liste déroulante).
Aussi
<%= link_to "profile", current_user %>
Dans la ligne
Le lien est spécifié sous forme abrégée
Le lien sera vers la page du spectacle. C'est une fonction qui convertit un objet utilisateur en un lien vers la page d'affichage.
Le menu déroulant doit charger jQuery dans bootstrap, donc Chargez via application.js.
Tout d'abord, chargez les packages jQuery et bootstrap avec yarn.
yarn add [email protected] [email protected]
Ensuite, ajoutez les paramètres jQuery au fichier d'environnement Webpack.
#### **`config/webpack/environment.js`**
```js
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
module.exports = environment
Enfin, exigez jQuery dans application.js et importez Bootstrap.
À ce stade, le menu déroulant est activé Puisque vous pouvez vous connecter en tant qu'utilisateur valide Vous pouvez tester efficacement votre code existant.
De plus, lorsque vous fermez le navigateur, la session temporaire est supprimée comme prévu et vous devez vous reconnecter.
Écran de suppression des cookies Chrome Après la suppression, le lien de connexion s'affiche.
Omis. Vous serez invité à vous connecter lorsque vous redémarrerez votre navigateur. (La session temporaire disparaît.)
Nous testerons le comportement des liens d'en-tête à changement dynamique avec des tests d'intégration. L'opération est
Vous devez d'abord vous connecter en tant qu'utilisateur enregistré pour tester ces L'utilisateur doit être enregistré dans la base de données de test. Dans Rails, vous pouvez créer des données de test à l'aide de fixture.
Puisque les données de l'utilisateur sont enregistrées mais que le mot de passe est haché et enregistré sous password_digest Les données de l'appareil doivent également stocker le mot de passe haché dans password_digest
Définit une méthode de résumé qui renvoie une chaîne hachée de mot de passe Puisqu'il est utilisé pour les appareils, il n'est pas utilisé pour les objets utilisateur. Par conséquent, il est défini comme une méthode de classe.
En outre, pour réduire le poids, minimiser le coût du hachage pendant les tests et augmenter le coût de la sécurité dans un environnement de production. Cliquez ici pour la méthode définie
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine::cost
BCrypt::Password.create(string, cost: cost)
end
Maintenant que la méthode de résumé est prête, créez un appareil.
users.yml
michael:
name: Michael Example
email: [email protected]
password_digest: <%= User.digest('password') %>
Puisque erb peut être utilisé dans le fixture, la chaîne de caractères hachée avec "password" est affectée à password_digest en utilisant la méthode digest. Vous pouvez maintenant faire référence à l'utilisateur de l'appareil.
Je vais créer un test à la fois.
test "login with valid information" do
get login_path
post login_path , params:{session:{email: @user.email,
password: 'password'}}
assert_redirected_to @user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
end
Dans ce test
assert_redirected_to
La destination de la redirection est@user
Je vérifie si c'est le cas.
follow_redirect!
En fait, déplacez-vous vers la destination de la redirection.
Après cela, la page de présentation (vue) est-elle affichée avec assert_template
?
assert_select
Il n'y a pas de lien de connexion dans.
Il existe un lien de déconnexion. Test des liens de page d’affichage.
post login_path , params:{ session: { email: @user.email,password:"" }}
Vous pouvez maintenant tester le cas où votre adresse e-mail est valide et votre mot de passe n'est pas valide.
2.```user && user.authenticate(params[:session][:password])```
↓
#### **`user&.authenticate(params[:session][:password])`**
Ces deux sont équivalents
Si vous ne vous connectez pas après vous être enregistré en tant qu'utilisateur, cela créera de la confusion et de la confusion pour les utilisateurs, alors connectez-vous en même temps que vous vous inscrivez.
Le processus de connexion étant défini par la méthode log_in '', la méthode
log_in '' est ajoutée à l'action Créer de UsersController.
Ajoutez-le simplement.
def create
@user = User.new(user_params)
if @user.save
log_in @user
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
render 'new'
end
end
Pour déterminer si vous êtes connecté lorsque vous créez l'utilisateur que vous avez ajouté ici
Je veux utiliser la méthode log_in? '' Ajoutée à la méthode d'assistance Comme une méthode d'aide pour les tests ne peut pas être appelée dans le test,
test_helper.rb``` Inscrivez-vous nouveau. Ceci est défini comme la méthode ```is_logged_in?
`Pour éviter les noms confus.
def is_logged_in?
!session[:user_id].nil?
end
Nous allons implémenter la fonction de déconnexion. Puisqu'il y a un lien, nous définirons l'action. Le contenu du processus est l'inverse de la méthode `` log_in '' et la session peut être supprimée.
def log_out
session.delete(:user_id)
@current_user = nil
end
Créez également une action de destruction en utilisant la méthode définie `` log_out ''.
def destroy
log_out
redirect_to root_url
end
Spécifications pour se déconnecter et accéder à l'URL racine lors de l'accès.
Mettez à jour le test ici également.
user_login_test.rb
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "login with valid email/invalid information" do
get login_path
assert_template 'sessions/new'
post login_path , params:{ session: { email: @user.email,password:"" }}
assert_not is_logged_in?
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
end
test "login with valid information" do
get login_path
post login_path , params:{session:{email: @user.email,
password: 'password'}}
assert_redirected_to @user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
delete logout_path
assert_not is_logged_in?
assert_redirected_to root_url
follow_redirect!
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path ,count: 0
assert_select "a[href=?]", user_path(@user), count: 0
end
end
Ici, si le processus de déconnexion est en cours, si la redirection est réussie, si le lien affiché est correct, etc. Je teste.
De plus, le contenu de "login avec email valide / mot de passe invalide" est réécrit en utilisant la méthode ```is_logged_in? `` `.
La session est supprimée et passe à l'URL racine. Les liens sur la page seront également remplacés par ceux avant la connexion. Nous avons confirmé l'opération, elle est donc omise.
Il ne disparaît pas lors de l'utilisation de Chrome, mais le processus de déconnexion est terminé.
debugger
Vous pouvez également le vérifier directement en utilisant comme.
[12, 21] in /home/ubuntu/environment/sample_app/app/controllers/sessions_controller.rb
12: render 'new'
13: end
14: end
15:
16: def destroy
17: log_out
18: redirect_to root_url
19: debugger
=> 20: end
21: end
(byebug) logged_in?
false
Recommended Posts