Wenn Sie GraphQL verwenden, möchten Sie es möglicherweise in verschiedenen Prozessen autorisieren.
Anfangs hatte ich wenig Kenntnisse über graphql-ruby, daher habe ich den Prozess zum Autorisieren im Erfassungs- und Aktualisierungsprozess aufgerufen. Wenn ich das graphql-ruby-Dokument jedoch erneut lese, wird die Autorisierungsmethode (autorisiert?) Ich fand heraus, dass es etwas gab, und schrieb einen Artikel, um den Vorgang zu überprüfen.
Es ist ein Juwel, das die Verwendung von GraphQL in Ruby (Rails) vereinfacht. https://github.com/rmosolgo/graphql-ruby
Die Details sind oft erst bekannt, wenn Sie sie tatsächlich ausprobieren, aber die Dokumentation ist großartig. https://graphql-ruby.org/guides
Zum Zeitpunkt des Schreibens dieses Artikels verwende ich graphql: 1.11.1
.
Bitte beachten Sie, dass der Betrieb von Gem noch aktualisiert wird. Wenn sich die Version unterscheidet, hat sich der Betrieb möglicherweise erheblich geändert.
Hier sind einige Beispiele für die Implementierung der ersten vier Muster.
Es wird davon ausgegangen, dass die für die Autorisierung erforderlichen Login-Benutzerinformationen im Kontext gespeichert sind. Die Erklärung der Authentifizierung wird weggelassen, da sie vom Hauptpunkt dieses Artikels abweicht.
app/controllers/graphql_controller.rb
#Login-Benutzerinformationen sind Kontext[:current_user]Speichern in
#Null, wenn nicht angemeldet
context = { current_user: current_user }
Hier wird "eine Abfrage implementiert, die review_id angibt und den entsprechenden ReviewType zurückgibt".
Implementieren Sie eine Abfrage, die einen ReviewType erhält, bevor Sie die Autorisierung implementieren.
app/graphql/types/query_type.rb
module Types
class QueryType < Types::BaseObject
field :review, resolver: Resolvers::ReviewResolver
end
end
app/graphql/resolvers/review_resolver.rb
module Resolvers
class ReviewResolver < BaseResolver
type Types::ReviewType, null: true
argument :review_id, Int, required: true
def resolve(review_id:)
Review.find_by(id: review_id)
end
end
end
app/graphql/types/review_type.rb
module Types
class ReviewType < BaseObject
field :id, ID, null: false
field :title, String, null: true
field :body, String, null: true
field :secret, String, null: true
field :user, Types::UserType, null: false
end
end
app/graphql/types/user_type.rb
module Types
class UserType < BaseObject
field :id, ID, null: false
field :name, String, null: false
field :email, String, null: false
end
end
In GraphiQL sieht es folgendermaßen aus:
Fügen wir nun die Einschränkung hinzu, die "nur der angemeldete Benutzer ausführen kann", zu dem zuvor implementierten Prozess.
Früher hatte ich eine Implementierung, die die Anmeldung überprüft, bevor ich eine Überprüfung mit der Auflösungsmethode erhalte.
Implementieren Sie zunächst eine Anmeldeprüfmethode in BaseResolver, damit sie von verschiedenen Resolvern verwendet werden kann.
Wenn der Kontext [: current_user] nicht enthalten ist, tritt ein Fehler auf.
Übrigens, wenn Sie GraphQL :: ExecutionError
verwenden, wird die Antwort nur durch Auslösen in das GraphQL-Fehlerformat konvertiert.
app/graphql/resolvers/base_resolver.rb
module Resolvers
class BaseResolver < GraphQL::Schema::Resolver
def login_required!
#Erhöhen Sie, wenn Sie nicht angemeldet sind
raise GraphQL::ExecutionError, 'login required!!' unless context[:current_user]
end
end
end
Rufen Sie dann zu Beginn des Vorgangs die BaseResolver-Anmeldeprüfung auf.
app/graphql/resolvers/review_resolver.rb
def resolve(review_id:)
+ #Führen Sie zu Beginn des Vorgangs eine Anmeldeprüfung durch
+ login_required!
Review.find_by(id: review_id)
end
Wenn Sie es mit GraphiQL ausführen, ohne sich anzumelden, sieht es wie folgt aus.
Ich habe mit dieser Methode erreicht, was ich tun möchte, aber Resolver, die eine Anmeldung erfordern, müssen zu Beginn des Prozesses immer "login_required!" Schreiben. Ich habe nach einer Möglichkeit gesucht, diesen Prozess automatisch zu autorisieren, bevor er aufgerufen wird, wie z. B. before_action im Controller.
Als ich die Anleitung von graphql-ruby noch einmal las, bemerkte ich, dass es eine Methode namens autorisiert gibt? Es scheint, dass Sie dies verwenden können, um die Autorisierung vor der Auflösungsmethode durchzuführen und zu steuern, ob sie ausgeführt werden kann oder nicht. Unten finden Sie eine Anleitung zum Hinzufügen zur Mutation, aber Sie können sie auch zu Resolver hinzufügen. https://graphql-ruby.org/mutations/mutation_authorization.html
Da Resolver, für den eine Anmeldung erforderlich ist, für allgemeine Zwecke verwendet werden kann, habe ich login_required_resolver erstellt, den Resolver, für den eine Anmeldung erforderlich ist, erbt. Die autorisierten Parameter (args) enthalten dieselben Parameter wie die Auflösung.
app/graphql/resolvers/login_required_resolver.rb
module Resolvers
class LoginRequiredResolver < BaseResolver
def authorized?(args)
context[:current_user].present?
end
end
end
Ändern Sie review_resolver, um login_required_resolver zu erben. Andere Implementierungen sind dieselben wie vor dem Hinzufügen der Autorisierung.
app/graphql/resolvers/review_resolver.rb
- class ReviewResolver < BaseResolver
+ class ReviewResolver < LoginRequiredResolver
Wenn Sie es mit GraphiQL ausführen, ohne sich anzumelden, sieht es wie folgt aus.
Wenn das Ergebnis von autorisiert? Falsch ist, gibt es keine Fehlerinformationen und nur "data: null" wird zurückgegeben. Wie im Handbuch beschrieben, scheint das Standardverhalten darin zu bestehen, nur "data: null" zurückzugeben, wenn dies autorisiert ist. Ist false. Wenn es kein Problem mit der Angabe der Rückgabe von null gibt, können Sie es so lassen, wie es ist. Wenn es jedoch nicht autorisiert ist, versuchen Sie es zu ändern, damit auch Fehlerinformationen zurückgegeben werden.
Das Hinzufügen von Fehlerinformationen ist einfach und kann durch Auslösen von GraphQL :: ExecutionError in autorisiertem? Übrigens, wenn Sie erfolgreich sind, müssen Sie vorsichtig sein, da dies nicht als Erfolg erkannt wird, es sei denn, Sie geben ausdrücklich true zurück.
app/graphql/resolvers/login_required_resolver.rb
module Resolvers
class LoginRequiredResolver < BaseResolver
def authorized?(args)
#GraphQL, wenn nicht autorisiert::ExecutionError auslösen
raise GraphQL::ExecutionError, 'login required!!' unless context[:current_user]
true
end
end
end
Wenn Sie es mit GraphiQL ausführen, ohne sich anzumelden, sieht es wie folgt aus. Jetzt können Sie die Fehlerinformationen zurückgeben, auch wenn Sie autorisierte? Verwenden.
Wenn Sie autorisiert? Verwenden, können Sie es einfach schreiben, weil Sie den Autorisierungsprozess nicht in der Auflösungsmethode schreiben müssen. (Dieses Beispiel ist eine ziemlich einfache Implementierung, daher gibt es keinen großen Unterschied ...)
Hier wird "Mutation implementiert, die den Titel und den Text der entsprechenden Überprüfung durch Angabe von review_id aktualisiert".
Implementieren Sie Mutation, um Review zu aktualisieren, bevor Sie die Autorisierung implementieren. Klassen, die so verwendet werden, wie sie sind, wie z. B. ReviewType, die im vorherigen Beispiel verwendet wurden, werden weggelassen.
app/graphql/types/mutation_type.rb
module Types
class MutationType < Types::BaseObject
field :update_review, mutation: Mutations::UpdateReview
end
end
app/graphql/mutations/update_review.rb
module Mutations
class UpdateReview < BaseMutation
argument :review_id, Int, required: true
argument :title, String, required: false
argument :body, String, required: false
type Types::ReviewType
def resolve(review_id:, title: nil, body: nil)
review = Review.find review_id
review.title = title if title
review.body = body if body
review.save!
review
end
end
end
Bei der Ausführung in GraphiQL werden die Überprüfungsdaten wie folgt aktualisiert.
Sie können autorisierte? In Mutation wie im vorherigen Beispiel verwenden. Es ist in der folgenden Anleitung aufgeführt. https://graphql-ruby.org/mutations/mutation_authorization.html
Erstellen Sie eine übergeordnete Klasse, die Mutation erbt, die nur Administratoren zur Verfügung steht, und erben Sie sie.
app/graphql/mutations/base_admin_mutation.rb
module Mutations
class BaseAdminMutation < BaseMutation
def authorized?(args)
raise GraphQL::ExecutionError, 'login required!!' unless context[:current_user]
raise GraphQL::ExecutionError, 'permission denied!!' unless context[:current_user].admin?
super
end
end
end
app/graphql/mutations/update_review.rb
- class UpdateReview < BaseMutation
+ class UpdateReview < BaseAdminMutation
Wenn Mutation autorisiert ist? Gibt auch nur false zurück, Fehlerinformationen werden nicht zurückgegeben, Daten sind null und die Aktualisierungsverarbeitung wird nicht ausgeführt. Resolver sieht immer noch gut aus, aber Mutation versteht es nur, wenn es Fehlerinformationen zurückgibt. Deshalb habe ich es implementiert, um auch GraphQL :: ExecutionError auszulösen. Übrigens, wenn Sie die Anleitung lesen, scheint es eine Möglichkeit zu geben, Fehlerinformationen zurückzugeben, indem Sie Fehler als Rückgabewert zurückgeben, wie unten gezeigt. Ich habe es versucht, aber die folgende Methode hat die fehlerhaften Speicherorte und Pfade nicht zurückgegeben, aber ich konnte die Fehlermeldungen zurückgeben. Wenn Sie die Nachricht nur zurückgeben müssen, können Sie sie mit beiden Methoden implementieren.
def authorized?(employee:)
if context[:current_user]&.admin?
true
else
return false, { errors: ["permission denied!!"] }
end
end
Wenn es von einem Benutzer ausgeführt wird, der keine Administratorrechte in GraphiQL hat, sieht es wie folgt aus. Im Fehlerfall wird der Update-Vorgang natürlich nicht ausgeführt.
Hier ändern wir es basierend auf der ersten "Abfrage, die review_id angibt und den entsprechenden ReviewType zurückgibt". Das erste, was ich gemacht habe, war, nur den Anmeldestatus zu überprüfen, aber diesmal ist Meine Eigenschaft überprüfen? Fügen Sie einen Scheck für hinzu.
Es wäre schön, wenn ich dem gleichen autorisierten einen Scheck hinzufügen könnte? Wie der Login-Check, aber dieser Check kann erst durchgeführt werden, nachdem ich Revew erhalten habe. Selbst mit autorisiertem? Wird review_id als Argument empfangen, sodass es möglich ist, eine Überprüfung zu erhalten, dies verdeckt jedoch die Rolle der Auflösung. Ich werde es tatsächlich implementieren.
app/graphql/resolvers/login_required_resolver.rb
def authorized?(args)
raise GraphQL::ExecutionError, 'login required!!' if context[:current_user].blank?
+ #Sie müssen an dieser Stelle eine Bewertung erhalten
+ review = Review.find_by(id: args[:review_id])
+ return false unless review
+ raise GraphQL::ExecutionError, 'permission denied!!' if context[:current_user].id != review.user_id
true
end
Sie benötigen eine Überprüfung mit autorisierten ?. Es wird auch durch die Auflösungsmethode erfasst, so dass es ineffizient erscheint, es auch hier zu erfassen. Wie wäre es also mit einer Überprüfung auf der Auflösungsseite?
app/graphql/resolvers/review_resolver.rb
def resolve(review_id:)
- Review.find_by(id: review_id)
+ review = Review.find_by(id: review_id)
+ raise GraphQL::ExecutionError, 'permission denied!!' if context[:current_user].id != review.user_id
+ review
end
Dies scheint effizienter zu sein als die Implementierung mit autorisiertem ", aber durch Ausschneiden des Prüfprozesses in" autorisiert "wurde der Prüfprozess zur Lösung hinzugefügt, der nur den Datenerfassungsprozess beschreibt.
Zuerst dachte ich, dass das einzige, was erst nach der Datenerfassung überprüft werden kann, die Überprüfung mit Entschlossenheit ist, aber ich habe erfahren, dass autorisiert? Kann auch in ReviewType definiert werden, also werde ich es in ReviewType definieren.
Was bedeutet es, mit ReviewType zu überprüfen? Ich werde es tatsächlich implementieren.
Ich möchte ReviewType für jedermann verfügbar machen, daher erstelle ich einen ReviewType mit dem Namen MyReviewType mit Einschränkungen, die nur ich anzeigen kann.
app/graphql/types/my_review_type.rb
module Types
class MyReviewType < ReviewType
def self.authorized?(object, context)
raise GraphQL::ExecutionError, 'permission denied!!' if context[:current_user].id != object.user_id
true
end
end
end
Wie im Handbuch erwähnt, verwendet autorisiert? Verwendet in Typ Objekt und Kontext als Argumente. Da es sich um eine Klassenmethode handelt, müssen Sie vorsichtig sein. https://graphql-ruby.org/authorization/authorization.html
Sie müssen lediglich den Antworttyp auf MyReviewType setzen. Es sind keine weiteren Änderungen erforderlich.
app/graphql/resolvers/review_resolver.rb
- type Types::ReviewType, null: true
+ type Types::MyReviewType, null: true
Wenn Sie in GraphiQL eine andere als Ihre eigene Überprüfung angeben, lautet diese wie folgt.
Jetzt, da Sie den Autorisierungsprozess nicht mehr in der Auflösungsmethode schreiben müssen, können Sie ihn einfach schreiben. Wenn Sie die Antwort auf MyReviewType festlegen, wird durch einfaches Lesen der Schemadefinition deutlich, dass diese Abfrage MyReviewType = "Nur Sie können sie anzeigen" zurückgibt.
Im vorherigen Beispiel habe ich MyReviewType so definiert, dass ich nur die gesamte Antwort für meine Daten sehen kann. Es kann jedoch vorkommen, dass Sie nur bestimmte Felder ausblenden möchten, nicht alle.
Ich werde den Überprüfungstyp erneut veröffentlichen. Hier soll die geheime Spalte nur meine Daten sehen.
app/graphql/types/review_type.rb
module Types
class ReviewType < BaseObject
field :id, ID, null: false
field :title, String, null: true
field :body, String, null: true
field :secret, String, null: true # <-Machen Sie dies nur für Sie sichtbar
field :user, Types::UserType, null: false
end
end
Es scheint autorisiert zu sein. Kann auch vor Ort implementiert werden, aber es scheint schwierig zu sein, nur ein Feld anzupassen. Deshalb habe ich beschlossen, es ohne Verwendung von autorisiert zu implementieren. Hier. https://graphql-ruby.org/authorization/authorization.html Klicken Sie hier für eine Feldanleitung https://graphql-ruby.org/fields/introduction.html#field-parameter-default-values
Wenn Sie dieselbe Methode wie den unten gezeigten Feldnamen definieren, wird diese Methode aufgerufen. Ich habe die Autorisierung innerhalb dieser Methode implementiert.
app/graphql/types/review_type.rb
module Types
class ReviewType < BaseObject
field :id, ID, null: false
field :title, String, null: true
field :body, String, null: true
field :secret, String, null: true
field :user, Types::UserType, null: false
#Wird beim Definieren einer Methode mit Feldname aufgerufen
def secret
#Wenn der angemeldete Benutzer und der Benutzer, der die Bewertung verfasst hat, unterschiedlich sind, geben Sie nil zurück
return if object.user_id != context[:current_user].id
object.secret
end
end
end
Wenn Sie in GraphiQL eine andere als Ihre eigene Überprüfung angeben, lautet diese wie folgt. Das Geheimnis wurde null zurückgegeben.
Wenn Sie diese Prüfung in Resolver implementieren, müssen alle Resolver, die ReviewType verwenden, geheim sein. Wenn Sie sie jedoch in ReviewType implementieren, müssen einzelne Resolver nicht an die Zugriffskontrolle von secret denken.
Ich wollte die Anleitung durchlesen, bevor ich anfing, graphql-ruby zu verwenden, aber ich habe die Existenz von autorisierten? ... Es scheint, dass es andere nützliche Funktionen als autorisierte gibt, die Sie noch nicht bemerkt haben. Auch wenn es jetzt nicht existiert, wurde die Version aktualisiert und es besteht eine hohe Wahrscheinlichkeit, dass in Zukunft neue Funktionen hinzugefügt werden. Daher möchte ich weiterhin die Trends von graphql-ruby überprüfen.
Recommended Posts