Dieser Artikel vertieft mein Verständnis, indem er einen Kommentarartikel zum Rails-Tutorial schreibt, um mein Wissen weiter zu festigen Es ist Teil meines Studiums. In seltenen Fällen kann es lächerliche oder falsche Inhalte enthalten. Bitte beachten Sie. Ich würde es begrüßen, wenn Sie mir implizit sagen könnten ...
Quelle Rails Tutorial Kapitel 6
Da wir im vorherigen Kapitel die Erstellung eines Benutzers ermöglicht haben, erstellen wir einen Mechanismus zum Anmelden mit dem erstellten Benutzer.
Da HTTP zustandslos ist (es gibt keinen Status), können frühere Anforderungsinformationen nicht verwendet werden. Es gibt keine Möglichkeit, Daten in HTTP beizubehalten, wenn Sie auf eine andere Seite wechseln. Die Benutzerinformationen werden beibehalten, indem stattdessen eine "Sitzung" verwendet wird.
Cookies werden häufig verwendet, um diese Sitzung in Rails zu implementieren Da Cookies Textdaten sind, die im Browser gespeichert sind und auch beim Verschieben der Seite nicht verschwinden, können Benutzerinformationen beibehalten werden.
Die Sitzung wird auch implementiert, indem sie direkt mit REST-Aktionen verknüpft wird. Speziell
Bewegung | Aktion |
---|---|
Login Formular | new |
Anmeldevorgang | create |
Abmeldevorgang | destroy |
Anmelden → Temporäre Sitzung erstellen Abmelden → Temporäre Sitzung löschen
Erstellen Sie zunächst einen Controller für die Sitzung.
rails g controller sessions new
Durch das Erstellen eines Controllers mit dem Befehl generate wird auch eine entsprechende Ansicht erstellt. Dieses Mal wird die Ansicht nur für die neue Aktion im Anmeldeformular benötigt Geben Sie mit dem Befehl generate nur new an.
Aktualisieren Sie auch route.rb. Obwohl das Routing zur neuen Aktion definiert ist umschreiben.
get '/login', to:'sessions#new'
post '/login', to:'sessions#create'
delete '/logout', to:'sessions#destroy'
Ändern Sie den automatisch generierten Test so, dass auch die angegebene Route verwendet wird.
rails routes
Sie können die Liste der Routen mit dem Befehl überprüfen.
Übung
`` `get login_pathempfängt ein Anmeldeformular mit einer GET-Anfrage.
post login_path```Sendet den im Anmeldeformular eingegebenen Wert in einer Post-Anfrage.
$ rails routes | grep sessions
login GET /login(.:format) sessions#new
POST /login(.:format) sessions#create
logout DELETE /logout(.:format) sessions#destroy
Im Formular haben wir das Formular mit form_with implementiert. Das Anmeldeformular erstellt keine Benutzerressource, sondern lediglich eine Sitzung Modellobjekt kann nicht angegeben werden. In einem solchen Fall
<%= form_with(url: login_path, scope: :session, local: true) do |f| %>
Senden Sie eine Post-Anfrage an login_path von
Durch Festlegen von scope :: session
Die Werte verschiedener Attribute werden in params [: session] gespeichert.
Zum Beispiel Passwort
Gespeichert in params [: session] [: password].
##### Übung
1.
Es gibt zwei Arten von Anmeldepfaden, aber form_with ist standardmäßig auf POST-Anforderung eingestellt.
Stellen Sie eine POST-Anforderung an login_path. Mit anderen Worten, die Erstellungsaktion wird mit dem POST-Anmeldepfad erreicht.
Im Fall von GET ist die neue Aktion erreicht.
#### Benutzersuche und Authentifizierung
Dieses Mal erstellen wir einen Prozess, wenn die Anmeldung fehlschlägt.
```rb
def new
end
def create
render 'new'
end
def destroy
end
Wenn Sie den Controller so definieren Wenn Sie das Formular abschicken, wird es unverändert an die neue Seite weitergeleitet.
Wenn Sie versuchen, das Formular in diesem Zustand zu senden
Sie können sehen, dass der Sitzungsschlüssel zum Parameter-Hash hinzugefügt wurde und die E-Mail- und Kennwortschlüssel enthält.
params = { session: { password: "foobar", email: "[email protected]" }}
Es hat eine solche Struktur.
Mit diesem
Empfangen Sie Benutzeranmeldeinformationen von Parametern und suchen Sie mit der Methode `` `find_bynach dem Benutzer
authenticate```Nach der Authentifizierung des Passworts mit der Methode
Prozess beim Erstellen einer Sitzung.
Klicken Sie hier, um den Inhalt der darauf basierenden Erstellungsaktion anzuzeigen
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
#Prozess zum Umleiten zur Benutzerseite(von jetzt an)
else
#Erstellen Sie eine Fehlermeldung.
render 'new'
end
end
&& ist ein logischer Produktoperator, und beide sind nur dann wahr, wenn sie wahr sind. Voraussetzung ist, dass der Benutzer existiert und das Kennwort des Benutzers authentifiziert ist.
>> 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
Erstellen Sie eine Flash-Nachricht, wenn die Anmeldung fehlschlägt.
danger] = "Invalid email/password combination"
Wenn dies unverändert bleibt, bleibt die Nachricht auch dann erhalten, wenn die Seite auf eine andere Seite geändert wird.
Weil ich das Formular mit Rendern erneut anzeige.
Da das Rendern keine Umleitung, sondern ein erneutes Rendern der Ansicht ist, verschwindet Flash nicht.
Ich werde dieses Problem von nun an beheben.
↓ Der Blitz wird auf anderen Seiten angezeigt ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617753/4bbfd1e0-1e51-1de0-0b09-44394f9d9723.png)
#### Flash-Test
Erstellen Sie einen Integrationstest für die Anmeldung. Dieses Mal gibt es ein Problem, dass der Blitz auch auf anderen Seiten angezeigt wird, daher dieser Fehler
Erstellen Sie den Test, um ihn zuerst zu erkennen, und schreiben Sie den Code so, dass der Test erfolgreich ist.
Klicken Sie hier für diesen 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
Die letzten beiden Zeilen greifen auf den Root-Pfad zu und testen, ob der Flash ausgeschaltet ist.
Natürlich wird ein Fehler auftreten. Verwenden Sie `` `flash.now```, um dieses Problem zu lösen.
flash.verschwindet jetzt, wenn Sie zur nächsten Aktion übergehen.
↑ Referenzseite (https://qiita.com/taraontara/items/2db82e6a0b528b06b949)
Ich war für einen Moment verwirrt, dass alles flash.now sein sollte, aber im Fall von flash.now, wenn redirect_to verwendet wird, wird flash überhaupt nicht angezeigt
Es ist aus. (Um die nächste Aktion mit redirect_to zu lesen)
Ändern Sie sofort `` `flash``` in`` flash.now```.
Diese Änderung besteht den Test.
##### Übung
1.
Anmeldungsfehler
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617753/341a1295-c6d4-e19b-1947-88e9044042f4.png)
Es verschwindet, wenn Sie zu einer anderen Seite wechseln
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617753/09cc611d-ce89-3f2c-0015-a7018fba1f01.png)
### Einloggen
Implementieren Sie als Nächstes die Verarbeitung, wenn die Anmeldeinformationen gültig sind.
Der Verarbeitungsablauf ist
Authentifizieren Sie sich, erstellen Sie eine temporäre Sitzung mit Cookies, wenn der Benutzer korrekt ist. Schließen Sie den Anmeldevorgang ab.
Zur entsprechenden Benutzerseite wechseln.
Sitzungen werden separat verarbeitet, je nachdem, ob Sie angemeldet sind oder nicht, auch auf anderen Seiten als der Anmeldeseite.
Nehmen Sie SessionsHelper in ApplicationController auf, damit es gemeinsam verwendet werden kann.
Auf diese Weise kann es von allen Controllern gemeinsam verwendet werden.
#### log_in Methode
Implementieren Sie den Anmeldevorgang mit der in Rails definierten Methode "Sitzung".
Wenn Sie die Sitzungsmethode verwenden, wird der dem Schlüssel zugeordnete Wert in temporären Cookies gespeichert.
Diese temporären Cookies verfallen, sobald der Browser geschlossen wird. Sie müssen sich daher jedes Mal anmelden, wenn Sie ihn schließen.
Da die Anmeldung voraussichtlich an verschiedenen Stellen verwendet wird, definieren Sie sie in SessionsHelper als Methode.
#### **`sessions_helper.rb`**
```rb
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
end
Fügen Sie es danach der Erstellungsaktion hinzu und leiten Sie es um, um es abzuschließen!
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
Im Fall von Chrome kann die Liste in Cookies auf der Registerkarte Anwendung der Entwicklertools angezeigt werden. ↑ Die verschlüsselte Benutzer-ID wird gespeichert (_sample_app_session)
Läuft ab → Die Zeit bis zum Ablauf wird geschrieben. Bei einer temporären Sitzung handelt es sich um eine Sitzung, und Sie können sehen, dass sie abläuft, wenn der Browser geschlossen wird.
Definieren Sie die Methode `` `current_user```, damit die Benutzer-ID abgerufen und die Benutzerinformationen auf einer anderen Seite verwendet werden können.
Die Suche nach einem Benutzerobjekt anhand der in der Sitzung gespeicherten ID, die Verwendung von find führt jedoch zu einem Fehler, wenn der Benutzer nicht vorhanden ist. Verwenden Sie die find_by-Methode.
def current_user
if session[:user_id]
User.find_by(id: session[:user_id])
end
end
Durch Speichern dieses Ergebnisses (Benutzerobjekt) in der Instanzvariablen Die Datenbankreferenz innerhalb einer Anforderung kann nur einmal durchgeführt werden, was zu einer Beschleunigung führt.
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
Und||Sie können diese if-Anweisung mit dem Operator in eine Zeile schreiben.
@current_user = @current_user || User.find_by(id: session[:user_id])
Wenn Sie diese Zeile in Kurzform schreiben, erhalten Sie außerdem den richtigen Code.
#### **`@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) Wenn nil angegeben wird, gibt die find_by-Methode nil zurück und @current_user wird ebenfalls nil. ② Wenn `` `@ current_user``` leer ist, aus der Datenbank lesen, aber wenn es Inhalt gibt, ersetzen Sie das Vertrauen (nichts tun)
>> 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]>
>>
Ändern Sie die angezeigte Seite dynamisch, je nachdem, ob Sie angemeldet sind oder nicht.
Wenn Sie in ERB verzweigen, je nachdem, ob Sie angemeldet sind oder nicht, ändern Sie den Link.
Definieren Sie eine `logged_in?`
Methode, die einen logischen Wert zurückgibt.
def logged_in?
!current_user.nil?
end
Ich möchte sicherstellen, dass current_user nicht leer ist, also kehre ich den logischen Wert mit um!
Ändern Sie als Nächstes das Layout der Kopfzeile.
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>
Ich füge ein Dropdown-Menü mithilfe einer Bootstrap-Klasse hinzu (z. B. Dropdown).
Ebenfalls
<%= link_to "profile", current_user %>
In der Schlange
Der Link wird in Kurzform angegeben
Der Link führt zur Show-Seite. Es ist eine Funktion, die ein Benutzerobjekt in einen Link zur Show-Seite konvertiert.
Das Dropdown-Menü muss also jQuery in Bootstrap laden Laden Sie über application.js.
Laden Sie zuerst die Pakete jQuery und Bootstrap mit Garn.
yarn add [email protected] [email protected]
Fügen Sie als Nächstes die jQuery-Einstellungen zur Webpack-Umgebungsdatei hinzu.
#### **`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
Schließlich benötigen Sie jQuery in application.js und importieren Sie Bootstrap.
Zu diesem Zeitpunkt ist das Dropdown-Menü aktiviert Da ist es möglich, sich als gültiger Benutzer anzumelden Sie können Ihren vorhandenen Code effizient testen.
Wenn Sie den Browser schließen, wird die temporäre Sitzung wie erwartet gelöscht und Sie müssen sich erneut anmelden.
Bildschirm zum Löschen von Chrome-Cookies Nach dem Löschen wird der Login-Link angezeigt.
Ausgelassen. Sie werden aufgefordert, sich anzumelden, wenn Sie Ihren Browser neu starten. (Temporäre Sitzung verschwindet.)
Wir werden das Verhalten von sich dynamisch ändernden Header-Links mit Integrationstests testen. Die Operation ist
Sie müssen sich zuerst als registrierter Benutzer anmelden, um diese zu testen Der Benutzer muss in der Testdatenbank registriert sein. In Rails können Sie Testdaten mithilfe von Fixture erstellen.
Da die Benutzerdaten registriert sind, wird das Passwort jedoch gehasht und als password_digest gespeichert Die Fixture-Daten müssen auch das Hash-Passwort in password_digest speichern
Definiert eine Digest-Methode, die eine gehashte Kennwortzeichenfolge zurückgibt Da es für Fixtures verwendet wird, wird es nicht für Benutzerobjekte verwendet. Daher wird es als Klassenmethode definiert.
Um das Gewicht zu reduzieren, minimieren Sie die Kosten für das Hashing während des Testens und erhöhen Sie die Kosten für die Sicherheit in einer Produktionsumgebung. Klicken Sie hier für die definierte Methode
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine::cost
BCrypt::Password.create(string, cost: cost)
end
Nachdem die Digest-Methode fertig ist, erstellen Sie ein Fixture.
users.yml
michael:
name: Michael Example
email: [email protected]
password_digest: <%= User.digest('password') %>
Da erb im Fixture verwendet werden kann, wird die mit "password" gehashte Zeichenfolge mit der Digest-Methode password_digest zugewiesen. Sie können jetzt vom Gerät aus auf den Benutzer verweisen.
Ich werde sofort einen Test erstellen.
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
In diesem Test
assert_redirected_to
Das Umleitungsziel ist@user
Ich überprüfe, ob es ist.
follow_redirect!
Bewegen Sie sich tatsächlich zum Umleitungsziel.
Wird danach die Show-Seite (Ansicht) mit `` `assert_templateangezeigt?
assert_select```Es gibt keinen Login-Link.
Es gibt einen Abmeldelink. Testen auf Showpage-Links.
post login_path , params:{ session: { email: @user.email,password:"" }}
Jetzt können Sie den Fall testen, in dem Ihre E-Mail-Adresse gültig und Ihr Passwort ungültig ist.
2.```user && user.authenticate(params[:session][:password])```
↓
#### **`user&.authenticate(params[:session][:password])`**
Diese beiden sind gleichwertig
Wenn Sie sich nach der Registrierung als Benutzer nicht anmelden, ist dies verwirrend und führt zu Verwirrung bei den Benutzern. Melden Sie sich daher gleichzeitig mit der Registrierung an.
Da der Anmeldevorgang durch die Methode `log_in``` definiert wird, wird die Methode`
log_in``` zur Aktion Create von UsersController hinzugefügt.
Fügen Sie es einfach hinzu.
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
Um festzustellen, ob Sie beim Erstellen des hier hinzugefügten Benutzers angemeldet sind
Ich möchte die Methode `logged_in?`
Verwenden, die der Hilfsmethode hinzugefügt wurde
Da eine Hilfsmethode zum Testen im Test nicht aufgerufen werden kann, wird `test_helper.rb``` Neu registrieren. Dies wird als
is_logged_in?
`` Methode definiert, um verwirrende Namen zu vermeiden.
def is_logged_in?
!session[:user_id].nil?
end
is_logged_in?
`Angemeldet bin.Wir werden die Abmeldefunktion implementieren. Da es einen Link gibt, werden wir die Aktion definieren. Der Inhalt des Prozesses ist die Umkehrung der Methode `` `log_in```, und die Sitzung kann gelöscht werden.
def log_out
session.delete(:user_id)
@current_user = nil
end
Erstellen Sie auch eine Zerstörungsaktion mit der definierten Methode `` `log_out```.
def destroy
log_out
redirect_to root_url
end
Spezifikationen zum Abmelden und Springen zur Stamm-URL beim Zugriff.
Aktualisieren Sie den Test auch hier.
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
Hier, ob der Abmeldevorgang ausgeführt wird, ob die Umleitung erfolgreich ist, ob der angezeigte Link korrekt ist usw. Ich teste.
Außerdem wird der Inhalt von "Anmelden mit gültiger E-Mail-Adresse / ungültigem Kennwort" mithilfe der Methode "is_logged_in" umgeschrieben.
Die Sitzung wird gelöscht und springt zur Stamm-URL. Die Links auf der Seite ändern sich auch zu denen vor der Anmeldung. Wir haben den Vorgang bestätigt, daher wird er weggelassen.
Es verschwindet nicht, wenn Sie Chrome verwenden, aber der Abmeldevorgang ist abgeschlossen.
debugger
Sie können es auch direkt mit z.
[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