Ich erstelle Zufallstokens aus dem in dieser Frage genannten Grund, die in einem java.util.List
und Dieser List
wird in einem Sitzungsumfang aufbewahrt.
Nachdem ich eine Google-Suche durchgeführt habe, habe ich entschieden, alle Elemente (Tokens), die jede Stunde von dieser List
in der Sitzung enthalten sind, zu entfernen.
Ich könnte daran denken, die Quartz API zu verwenden, aber es scheint nicht möglich zu sein, einen Benutzer zu manipulieren Session. Was ich im Frühjahr mit der Quartz-API (1.8.6, 2.x ist nicht kompatibel mit Spring 3.2, die ich verwende) ausprobiert habe, kann man unten sehen.
%Vor% Und es wurde in der Datei application-context.xml
wie folgt konfiguriert.
Die überschriebene Methode in der Klasse RemoveTokens
wird jede Stunde mit dem anfänglichen Intervall von 10 Sekunden ausgeführt, wie in XML konfiguriert, aber es ist nicht möglich, eine Methode einer Klasse auszuführen, um die in List
which verfügbaren Token zu entfernen wird in einer Benutzersitzung gespeichert. Ist es möglich?
Was ist der faire Weg, um dieses List
, das in einem Sitzungsumfang gespeichert ist, mit einem definierten Zeitintervall (jede Stunde) zu entfernen? Es wäre viel besser, wenn es mit dieser Quartz-API möglich wäre.
BEARBEITEN:
Nach der Antwort unten habe ich folgendes versucht, aber leider hat es keinen Unterschied gemacht.
In der Datei application-context.xml
Dies erfordert die folgenden zusätzlichen Namespaces,
%Vor%Die folgende Bean wurde als Session-Bean registriert.
%Vor%Und Schnittstelle, die es implementiert,
%Vor% Und diese Bean wurde in der application-context.xml
-Datei wie folgt konfiguriert:
Jetzt gebe ich diesen Dienst in die folgende Klasse ein.
%Vor% Und in der Datei application-context.xml
,
Beide obigen Beans sind mit dem @Service
versehen und sie sollten Teil von context:component-scan
in der dispatacher-servelt.xml
-Datei sein (oder wie auch immer der Name lautet).
Die mit der Annotation @Scheduled
im vorangehenden Code-Snippet annotierte Methode wird regelmäßig mit der angegebenen Rate aufgerufen, verursacht jedoch die folgende offensichtliche Ausnahme.
Um Daten zu löschen, die in einer Benutzersitzung gespeichert wurden, sollte die Methode, die diese Aufgabe ausführt, regelmäßig in einem definierten Zeitintervall von jeder Benutzersitzung aufgerufen werden, was bei diesem Versuch nicht der Fall ist. Wie ist der Weg? Die Frage könnte einfach lauten: Wie kann man von jeder Sitzung eines Benutzers ein regelmäßiges Zeitintervall auslösen? Unterstützt die Spring- oder Servlet-API etwas, um dies zu erreichen?
Herzlichen Glückwunsch zur Verwendung von Token, um Wiederholungen zu verhindern ("Synchronization Token einführen" Refactoring aus dem Buch Core J2EE Patterns). : ^)
Quarz ist wertvoll für komplizierte oder präzise Planung. Aber Ihre Anforderungen brauchen nicht Quarz. Könnte nützlicher sein, um CDI, java.util.Timer, ScheduledExecutor und / oder EJB-Timer zu lernen.
Wenn Sie einen Scheduler verwenden, wie Sie es nennen, ist es gut, einen Singleton-Scheduler von allen Benutzern gemeinsam nutzen zu lassen, anstatt einen Scheduler & amp; Thread-Instanz pro Benutzersitzung.
Seien Sie vorsichtig, wenn Sie Referenzen auf HttpSession speichern oder als Parameter übergeben. Das Speichern von Verweisen verhindert die Garbage-Collection, wenn die Sitzung abgeschlossen ist. Dies führt zu einem (großen?) Speicherleck. Versuche, Referenzen mithilfe von HttpSessionListener & amp; andere Tricks, funktioniert möglicherweise nicht & amp; sind unordentlich. Das Übergeben von HttpSession an Methoden als Parameter macht sie künstlich abhängig vom Servlet-Container und schwierig zu Unit-Test, da Sie komplexe HttpSession-Objekte vortäuschen müssen. Es ist sauberer, alle Sitzungsdaten in ein einzelnes Objekt zu packen. UserSessionState - speichert Referenzen auf diese in Ihrer Session & amp; Objektinstanzvariablen. Dies wird auch als Context Object Pattern bezeichnet - d. H. Sie speichern alle Ihre Bereichsdaten in einem / wenigen POJO-Kontextobjekten, unabhängig von den HTTP-Protokollklassen.
Ich schlage zwei alternative Lösungen für Ihr Problem vor:
Verwenden Sie eine java.util.Timer
Singleton-Instanz
Sie könnten java.util.Timer
(eingeführt in Java SE 1.3) durch ScheduledExecutor
(eingeführt in Java SE 5) für eine nahezu identische Lösung ersetzen.
Dies ist mit der JVM verfügbar. Es erfordert kein JAR-Setup und keine Konfiguration.
Sie rufen timerTask.schedule
auf und übergeben eine TimerTask
-Instanz. Wenn der Zeitplan fällig ist, wird timerTask.run
aufgerufen. Sie entfernen geplante Aufgaben über timerTask.cancel
und timerTask.purge
, wodurch belegter Speicher freigegeben wird.
Sie codieren die TimerTask
, einschließlich ihres Konstruktors, und Sie erstellen eine neue Instanz und übergeben sie an die Schedule-Methode, was bedeutet, dass Sie alle benötigten Daten oder Objektreferenzen in TimerTask
; Sie können jederzeit darauf verweisen und Setter-Methoden aufrufen.
Ich schlage vor, Sie erstellen zwei globale Singletons: eine Timer
Instanz und eine TimerTask
Instanz. Speichern Sie in Ihrer benutzerdefinierten TimerTask
-Instanz eine Liste aller Benutzersitzungen (im POJO-Format wie UserSessionState
oder Spring / CDI-Bean, nicht in Form von HttpSession
). Fügen Sie dieser Klasse zwei Methoden hinzu: addSessionObject
und removeSessionObject
, mit einem Parameter von UserSessionState
oder ähnlich. In der TimerTask.run
-Methode, iterieren Sie durch den Satz von UserSessionState
und löschen Sie die Daten.
Erstellen Sie einen benutzerdefinierten HttpSessionListener - aus sessionCreated, fügen Sie eine neue UserSessionState-Instanz in die Sitzung ein und rufen Sie TimerTask.addUserSession auf; Rufen Sie von sessionDestroyed TimerTask.removeUserSession auf.
Rufen Sie den Global-Scoped-Singleton timer.schedule
auf, um die TimerTask-Instanz so zu planen, dass der Inhalt der sitzungsbezogenen Referenz gelöscht wird.
Verwenden Sie eine Token-Liste mit begrenzter Größe (keine Bereinigung)
Bereinige keine Tokens basierend auf der verstrichenen Zeit. Beschränken Sie stattdessen die Listengröße (z. B. 25 Token) und speichern Sie die zuletzt generierten Tokens.
Dies ist wahrscheinlich die einfachste Lösung. Wenn Sie ein Element zur Liste hinzufügen, prüfen Sie, ob Sie die maximale Größe überschritten haben, wenn dies der Fall ist, und fügen Sie sie vom frühesten Index in die Liste ein:
%Vor%Hier ist kein Scheduler erforderlich, und es ist nicht notwendig, & amp; eine Gruppe aller Benutzersitzungen verwalten.
Ich denke, das sollte viel einfacher sein.
Informationen zur Synchronisierungstokenimplementierung
Die Token im Synchronizer-Token-Muster sind NICHT dazu gedacht, wiederverwendet zu werden. Ein Token gilt nur für eine Einreichung als gültig. Nicht mehr und nicht weniger.
Zu jedem Zeitpunkt sollten Sie nur ein Token für eine Sitzung speichern.
Wenn ein Formular dem Benutzer angezeigt wird, sollte das Token als verstecktes Formularelement in das Formular eingefügt werden.
Bei der Übermittlung müssen Sie lediglich prüfen, ob das Token im Formular und in der Sitzung übereinstimmt. Ist dies der Fall, erlauben Sie die Formularübergabe und setzen Sie den Wert des Tokens in der Sitzung zurück.
Wenn der Benutzer nun dasselbe Formular erneut einreicht, werden Token (mit dem alten Token) nicht übereinstimmen und Sie können eine doppelte Einreichung oder eine veraltete Einreichung erkennen
Wenn der Benutzer andererseits das Formular selbst neu lädt, ist das aktualisierte Token jetzt im ausgeblendeten Formularelement vorhanden.
Fazit - Es ist nicht notwendig, eine Liste von Tokens für einen Benutzer zu speichern. Ein Token pro Sitzung ist genau das, was benötigt wird. Dieses Muster wird häufig als Sicherheitsmaßnahme zur Verhinderung von CSRF-Angriffen verwendet. Jeder Link auf der Seite kann nur einmal aufgerufen werden. Selbst dies kann mit nur einem Token pro Sitzung erfolgen. Als Referenz können Sie sehen, wie der CSRF Guard V3 Ссылка
funktioniertÜber Sitzungen
Ein Sitzungsobjekt hat nur dann eine Bedeutung, wenn der Ausführungsthread irgendwie mit einem HTTP-Anfrage / Antwort-Paar verbunden ist. Oder einfach gesagt, solange der Benutzer Ihre Website durchsucht.
Sobald der Benutzer weg ist, wird auch die Sitzung (von der JVM) beendet. Sie können also keinen Timer verwenden, um es zurückzusetzen
Sitzungen werden serverseitig serialisiert, um sicherzustellen, dass sie bei einem erneuten Besuch Ihrer Site zum Leben erweckt werden können (jsessionid verwendet ID, welche Sitzung für welche Browsersitzung deserialisiert werden sollte)
Mit einer Sitzung ist ein Zeitlimit verbunden. Wenn das Zeitlimit abgelaufen ist, startet der Server beim erneuten Besuch des Benutzers eine neue Sitzung.
Fazit - kein plausibler Grund, um die Sitzungen der Benutzer regelmäßig zu löschen - Und keine Möglichkeit, dies zu tun.
Lass mich wissen, wenn ich etwas missverstanden habe, und ich hoffe, das hilft.
Meine Idee wäre etwas wie:
Kurzversion:
unsetAllTokensIfNeeded
method zu SessionTokenService
hinzu, das jede Minute aufgerufen wird, aber löscht nur die Token-Liste, wenn sie wirklich benötigt wird (in diesem Fall wird die Entscheidung basierend auf der Zeit getroffen). Ausführliche Version:
Ihre geplante Aufgabe wird jede Minute ausgeführt, wobei die Methode unsetAllTokensIfNeeded
aufgerufen wird, die von SessionToken
implementiert wurde. Die Methodenimplementierung überprüft, wann die Tokenliste das letzte Mal sauber war, und ruft unsetAllTokens
auf, wenn es vor einer Stunde war.
Aber um es in jeder einzelnen Sitzung aufzurufen% cocode% benötigen Sie die Liste der vorhandenen SessionTokenService
s, die Sie erreichen können, indem Sie sie zum Zeitpunkt der Erstellung beim Dienst registrieren, der sie bereinigt Verwenden Sie SessionTokenService
, um harte Referenzen zu vermeiden. Dies würde verhindern, dass die Objekte vom Garbage Collector erfasst werden.
Die Implementierung wäre etwa so:
Sitzungstoken:
Schnittstelle:
%Vor%Implementierung:
%Vor%Session Token Flusher:
Schnittstelle:
%Vor%Implementierung:
%Vor%In diesem Fall sollten Sie die Annotation-gesteuerte Aufgabe von Spring verwenden:
%Vor%Aus meiner Sicht wäre das eine einfache und gute Lösung.
BEARBEITEN
Bei dieser Methode registrieren Sie mehr oder weniger WeakHashMap
auf SessionToken
, wenn Spring die Bean-Konfiguration beendet und sie löscht, wenn Spring die Bean zerstört. Dies geschieht, wenn die Sitzung beendet wird.
Ich weiß nicht viel über Quarz, aber das würde ich in Ihrer Situation tun: Frühling 3.2, oder? In Ihrem Anwendungskontext:
%Vor%Sie benötigen einen Task-Namespace ...
In einer Klasse (SessionCleaner.java zum Beispiel):
%Vor%Was ich tun würde ist, die Sitzungsdaten als von Spring verwaltete Session-Bean zu verwenden:
%Vor%und injiziere es dort, wo ich es brauche. Dann wird der Clan so aussehen.
%Vor%Ich weiß nicht viel über Quarz. Aber, sehen Sie Ihre Ausnahme, was ich erraten kann Scheduling ist ein Teil der asynchronen Kommunikation. Daher werden Multithreading- und ereignisgesteuerte Modelle verwendet, um dies zu erreichen. In diesem Fall versuchen Sie also, eine Bean zu kitten preventDuplicateSubmissionService. Der Container erstellt Bean als Teil von object.Ab wenn Sie darauf zugreifen, versucht der Thread, der von Quartz als Teil der asynchronen Planung erstellt wird, einen Service zu bekommen Für diesen Thread wird die Bean nicht instanziiert. Möglicherweise verwenden Sie scope="request" & gt; .So wenn scope ist Anforderung für jede HTTP-Anfrage sein Objekt erstellt.In Ihrem Fall, wenn Sie versuchen, auf Bean zugreifen, dann die Bohne mit Request-Bereich nicht erstellt, da der Zugriff auf Bean in nicht http-Modus gemacht wird, wenn Thread versuchen, zu aceess a service die ausnahme wird ausgelöst.Ich hatte auch das selbe Problem, als ich versuchte, auf eine Bean zuzugreifen, die Multithreading in serviceImpl verwendete. In meinem Fall wurde die Bean für den Anforderungsbereich nicht erstellt, weil sie für jeden HTTP-Anforderungsmodus erstellt wurde. Ich habe hier eine Lösung implementiert und es funktioniert für mich. Zugriff auf Bereichs-Proxy-Beans innerhalb von Threads von
Es scheint, als ob der periodische Auftrag des Token-Entferners, den Sie eingerichtet haben, nicht im Kontext einer bestimmten Benutzersitzung ausgeführt wurde - noch für alle Benutzersitzungen.
Ich bin mir nicht sicher, ob es einen Mechanismus gibt, mit dem Sie alle aktiven Sitzungen durchsuchen und ändern können, aber die von mir vorgeschlagene Alternative ist:
Speichern Sie ein Paar eindeutiger Token mit einem Zeitstempel auf der Serverseite. Senden Sie das Token nur an den Benutzer (nie den Zeitstempel), wenn Sie das Formular präsentieren. Wenn das Formular gesendet wird, suchen Sie nach dem Zeitpunkt, zu dem dieses Token generiert wird. Wenn es den Zeitüberschreitungswert überschritten hat, lehnen Sie es ab.
Auf diese Weise brauchen Sie nicht einmal einen Timer, um alle Token zu löschen. Mit einem Timer würde auch neu erstellte Token
löschenIn der ursprünglichen Question-Methode (die korrekt gemäß shcedule ausgeführt wurde) verwenden Sie den folgenden Code ...
%Vor%Tags und Links java session spring jsp quartz-scheduler