Je génère des jetons aléatoires pour la raison comme mentionné dans ce Question placée dans un Après avoir traversé une recherche Google, j'ai décidé de supprimer tous les éléments (jetons) toutes les heures contenues par cette liste Je pouvais penser à utiliser le Quartz API, mais cela ne semble pas possible de manipuler une session d'utilisateur. Ce que j'ai essayé avec l'API de quartz (1,8,6, 2.x est incompatible avec le printemps 3.2 que j'utilise) au printemps peut être vu ci-dessous. P> Java .util.list code> et cette liste
code> est conservée dans une portée de session.
code> en session. p>
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.sessionTokenCleanerService': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:343)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:184)
at $Proxy79.unsetAllTokens(Unknown Source)
at token.PreventDuplicateSubmission.clearTokens(PreventDuplicateSubmission.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:90)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:329)
... 19 more
7 Réponses :
Je ne sais pas grand chose à propos de quartz, mais c'est ce que je ferais dans votre situation: Printemps 3.2, non? Dans votre contexte de candidature:
class SessionCleaner{ @Autowired private MySessionDataBean sessionData; @Scheduled(fixedRate=3600000) public void clearSession(){ sessionData.getList().clear();//something like that } }
Félicitations à l'aide des jetons pour empêcher les soubmits ("introduire la synchronisation de jeton de synchronisation" à partir des motifs J2EE du livre J2EE). : ^) p>
Quartz est précieux pour une planification compliquée ou précise. Mais vos besoins n'ont pas besoin de quartz. Pourrait être plus utile d'apprendre CDI, Java.Util.Timer, planchedexecutor et / ou EJB minuteries. P>
Si vous utilisez un planificateur, comme vous le mentionnez, il est bon d'avoir un planificateur Singleton partagé par tous les utilisateurs, plutôt que d'avoir une instance de planificateur et de thread par session utilisateur. P>
Soyez prudent si vous stockez déjà des références à httpsession ou de le transmettre en tant que paramètre. Stockage des références empêche la collecte des ordures lorsque la session est complète - provoquant une fuite de mémoire (gros?). Les tentatives de nettoyage des références à l'aide de HttpsPessionsListener et d'autres astuces, peuvent ne pas travailler et sont désordonnés. Passer httppsession à des méthodes sous forme de paramètre qui les rend artificiellement dépendants artificiellement du récipient de servlet et de tester difficiles à un appareil, car vous devez faire une simulation d'objets httpsession complexes. Il est plus propre d'envelopper toutes les données de session dans un objet Singular ObjectSetSetState - Conservez des références à ce em> dans vos variables d'instance de session et d'objet. Ceci est également connu sous le nom de modèle d'objet contexteur - c'est-à-dire que vous stockez toutes vos données scopées dans un / quelques objets contextuels Pojo, indépendant des classes de protocole HTTP. P> Je suggère deux solutions alternatives à votre problème: p> Utilisez un Vous pouvez remplacer Ceci est disponible avec le JVM. Il ne nécessite aucune configuration JAR et aucune configuration.
Vous appelez Vous codez le Je vous suggère de créer deux singletons globaux: A Créer une personnalisation HttpSspessionListener - de SessionCreated, mettez une nouvelle instance utilisateurState dans la session et appelez Timerask.Ajoutersession. De SéanceDestroyed, appelez timerask.removeUsersession. P>
Appeler le Singleton Global-Scoped Singleton Utilisez la liste de jetons avec la taille plafonnée (pas de nettoyage) p>
Ne nettoyez pas les jetons basés sur le temps écoulé. Au lieu de restreindre la taille de la liste (par exemple 25 jetons) et stockez les jetons les plus récemment générés. P>
C'est probablement la solution la plus simple. Lorsque vous ajoutez un élément à la liste, vérifiez si vous avez dépassé la taille maximale, le cas échéant et insérez à partir de l'indice le plus ancien de la liste: P>
Réponse H2>
Java.Util.Timer Code> Singleton Instance P>
java.util.timer code> (introduit dans Java SE 1.3) avec
planchedexecuteur code> (introduit dans Java SE 5) pour une solution presque identique. p>
timertask.schedule code>, en passant dans un
TIMERTASK code> instance. Lorsque la planification est due,
timertask.run code> est appelé. Vous supprimez les tâches planifiées via
timertask.cancel code> et
timertask.purge code> qui libère la mémoire utilisée. p>
TIMERTASK code>, y compris son constructeur et vous créez une nouvelle instance et transmettez-la à la méthode de planification, ce qui signifie que vous pouvez stocker des références de données ou d'objet dont vous avez besoin dans le
TIMERTASK ; Vous pouvez conserver une référence à elle et appelez les méthodes de réglage à tout moment plus tard. p>
Minuterie code> instance et a
TIMERTASK code> instance. Dans votre instance Custom
TIMERTASK CODE>, conservez une liste de toutes les sessions utilisateur (sous forme POJO comme
usersessionState CODE> ou Spring / CDI Bean, non sous la forme de
httpsession code >). Ajoutez deux méthodes à cette classe:
addsessionObject code> et
reploisessionObject code>, avec un paramètre de
usersessionState code> ou similaire. Dans la méthode
TIMERTASK.RUN CODE>, itérale via l'ensemble de
usersessionState code> et effacer les données. P>
Timer.Schedule CODE> Pour planifier l'instance TIMERTASK pour effacer le contenu de la référence Session-Scoped. P> LI>
if (++putIndex > maxSize) {
putIndex = 0;
}
list.put(putIndex, newElement);
Mon idée serait quelque chose comme:
Version courte: strong> p> Votre tâche planifiée s'exécutera chaque minute appelera la méthode code> non enregistrée code> mise en œuvre par mais afin de l'invoquer dans chaque session Scoped La mise en œuvre serait une chose comme ceci: p> interface: p> implémentation: p> ralligère de jeton de session: p> interface: p> implémentation: p> Dans ce cas, vous utiliserez la fonction de tâche entraînée de l'annotation Du printemps: p> de mon point de vue serait une solution simple et bonne. p> avec ceci a PPROCH Vous êtes plus ou moins enregistrant le
désastablez code> méthode sur
sessionTokenservice code> qui sera invoqué chaque minute mais ne nettoie pas la liste des jetons s'il est vraiment nécessaire (dans ce cas, la décision est effectuée en fonction de le temps). li>
ol>
sessionToken code>. La mise en œuvre de la méthode vérifiera la dernière liste de jetons de jeton de la dernière fois était propre et invoquer
désattalltokens code> s'il était il y a une heure. P>
SessionTokenService Code> Vous aurez besoin de la liste des
SessionTokenService existant CODE> S, qui pourrait être réalisé en l'enregistrant au moment de la création au service qui le nettoiera (vous utiliserez ici un
FaibleHashMap < / Code> Pour éviter les références difficiles, cela éviterait les objets à collecter par le collecteur des ordures). P>
sessionToken code> au fichier code> lorsque le ressort se termine la configuration du haricot et le retirez lorsque le ressort détruit le haricot, cela se fait lorsque la session est terminée. p> p>
Bonjour, cela fonctionne comme il s'agit que des objets contenus par faiblesHashmap code> restent après la déconnexion d'un utilisateur. Cela pourrait être une longue liste au fil du temps. Est-il approprié de les laisser au collectionneur des ordures pour être récupéré? En outre, une petite chose que je ne sais pas, à cette ligne -
flusherservice.register (ceci); code> à l'intérieur du constructeur de la classe
sessionToken code> classe, un avertissement apparaît - < code> fuite de ceci dans le constructeur code>. Peut-il être supprimé d'une manière ou d'une autre?
Oui, donc j'ai changé un peu l'échantillon, laissez sessionToken code> implémenter
jetatablebean code> et
initializizingbean code>, cela enregistrera le haricon juste après la configuration du haricot de finition de printemps ( Évitez la fuite de cela dans le message du constructeur) et la désenregistrement lorsque la session est terminée, j'espère que cela aide.
Bonjour monsieur, le problème concret a été résolu (en référence au commentaire ci-dessous la question). Je cherchais cependant toujours une autre condition à l'intérieur du non enregistré () code> méthode. Ce qui se passe, c'est que cette condition
if (lastcleanup.gettime ()> nouvelle date () . De plus, je recevais une exception indiquant
faiblesHashmap code> n'est pas sérialisable. Cette réponse est également utile à l'avenir aussi, s'il vous plaît ne le supprimez pas.
Je viens de le modifier, j'ai demandé parce que je suis curieux si c'était ce dont vous aviez besoin, s'il couvre vos besoins ...
@Javamentor: Séance Scoped Beans nécessitent la mise en œuvre de l'interface java.io.serializalbe code>. N'est-ce pas? Concernant le Pop's Commentaire A> - " je reçois une exception indiquant
faiblesHashmap code> n'est pas sérialisable i>".
@Lion Le service FLUSHER n'est pas session SPOPED (il s'agit d'une sorte de service planifié qui fonctionne chaque minute et il suffit d'effacer les cartes de session stockées dans un SessionTokenService CODE> et celui-ci est SESSION SPOPED).
Je pense que cela devrait être beaucoup plus simple. P>
Les jetons du motif de jeton de synchroniseur ne sont pas destinés à être réutilisables. Un jeton est considéré comme valide que pour une soumission. Pas plus pas moins. P> li>
À tout moment donné, vous devez économiser un seul jeton pour une session. P> Li>
Lorsqu'un formulaire est montré à l'utilisateur, le jeton doit être inclus dans le formulaire sous forme d'élément de formulaire caché. p> li>
sur soumission, tout ce que vous avez à faire est de vérifier si le jeton sous la forme et la session correspondent. S'ils le font, autorisez la soumission de formulaire et réinitialisez la valeur du jeton dans la session. P> li>
MAINTENANT Si l'utilisateur re-soumet le même formulaire (avec les jetons anciens) ne correspond pas et que vous pouvez détecter une double soumission ou une soumission fade p> li>
D'autre part, si l'utilisateur recharge le formulaire lui-même, le jeton mis à jour sera désormais présent dans l'élément de formulaire masqué. P> li>
ol>
Conclusion - Il n'est pas nécessaire de sauvegarder une liste de jetons pour un utilisateur. Un jeton par session est juste ce qui est nécessaire. Ce modèle est largement utilisé comme mesure de sécurité pour empêcher les attaques du CSRF. Lorsque chaque lien de la page ne peut être invoqué que une fois. Même cela peut être fait avec un seul jeton par session. Pour référence, vous pouvez voir comment le CSRF Guard v3 fonctionne https: //www.owasp. org / index.php / Catégorie: owasp_csrfguard_project # Source_code P>
sur les sessions strong> p>
Un objet de session n'a de sens que tant que le fil de l'exécution est attaché à une paire de requêtes / interventions HTTP. Ou simplement mettre, tant que l'utilisateur navigue sur votre site. P> li>
Une fois que l'utilisateur est parti, la session (de la JVM). Donc, vous ne pouvez pas utiliser une minuterie pour le réinitialiser p> li>
Les sessions sont sérialisées par des serveurs pour vous assurer qu'ils peuvent être écartés à la vie lorsqu'un utilisateur revient à votre site (JSessionID est utilisé pour identifier quelle session doit être désérialisée pour laquelle la session de navigateur) p> li>
Il existe un délai d'attente associé à une session, si le délai d'expiration expire, le serveur démarre une nouvelle session lorsque l'utilisateur revisite. p> li>
ol>
Conclusion - Aucune raison plausible de devoir chasser périodiquement des sessions des utilisateurs - et aucun moyen de le faire. p>
laissez-moi savoir si j'ai mal compris quelque chose, et j'espère que cela vous aidera. P>
Si l'utilisateur dispose de plus d'un onglet et qu'ils soumettent les deux formulaires, qui doivent être traités comme une double soumission!
Si vous souhaitez conserver plus d'un jeton par session, maintenez une liste de taille finale comme 10 jetons. Lorsqu'il y a un besoin pour le 11ème jeton, retirez le premier. En tout cas, vous ne pouvez pas et ne devriez pas penser à des sessions de rinçage périodiquement par le code. Ma réponse est toujours en attente. Vous ne pourrez pas courir d'emploi pour supprimer des sessions.
@Akshaysinghal: L'OP vous demande de supprimer simplement un attribut de session périodiquement et de ne pas rincer toute la session de l'utilisateur comme vous semblez avoir supposé qu'il voulait dire qu'il voulait dire.
Je n'ai pas supposé avoir quitté toute la session, je suppose que mon commentaire aurait pu donner cette impression. Merci de l'avoir amené, j'apprécie que vous essayiez de vous assurer que j'ai bien compris la question correctement. Quoi qu'il en soit, essayez de mettre à jour une session de navigateur après que l'utilisateur ait disparu depuis longtemps n'est pas possible. Donc, ma réponse à la question reste toujours la même.
Je ne sais pas grand chose à propos de quartz .mais, voyant votre exception ce que je peux deviner est La planification fait partie de la communication asynchrone. Donc, le modèle de multithreading et d'événement a été utilisé pour y parvenir. Donc, dans votre cas, vous essayez de citer un haricot préventifduplicamentaserservice. Le conteneur crée un haricot comme une partie de l'objet.Mais lorsque vous y accédez, le fil créé par quartz dans le cadre de la planification asynchrone tente d'obtenir un service. Mais , pour ce fil, le haricon n'est pas instancié.May ÊTRE VOUS UTILISEZ SCRAPHIQUE = "Demande"> .SO Lorsque la portée est la demande de chaque requête HTTP Son objet est créée.Dans votre cas lorsque vous essayez d'accéder à la bean, puis du haricon avec demande La portée n'est pas créée car l'accès au haricot est effectué en mode non HTTP, donc lorsque le thread Essayez d'attirer un service, l'exception est également transmise du même problème lorsque j'essayais d'accéder à un haricot à l'aide d'un bean à la mainIMPL.in Mon cas, le haricot pour demande n'a pas été créé car il est créé pour chaque mode de requête HTTP. J'ai mis en œuvre la solutine donnée ici et cela fonctionne pour moi. accéder aux haricots proxy cachés dans des fils de P>
Il semble que le travail périodique du remodelage de jeton que vous avez configuré n'a pas été exécuté dans le contexte d'une session utilisateur particulière - ni toutes les sessions utilisateur. p>
Je ne suis pas sûr de savoir s'il y a un mécanisme pour vous de scanner toutes les sessions actives et de le modifier, mais l'alternative que je propose est la suivante: P>
Stockez une paire de jeton uniques avec un horodatage du côté serveur. Envoyez uniquement le jeton à l'utilisateur (jamais l'horodatage) lorsque vous présentez le formulaire. Lorsque le formulaire est soumis, recherche lorsque ce jeton est généré - s'il est passé la valeur du délai d'attente, rejetez-le. P>
De cette façon, vous n'avez même pas besoin d'une minuterie pour supprimer tout le jeton. L'utilisation d'une minuterie supprimerait également un jeton nouvellement créé p>
Dans la méthode de la question originale (qui fonctionnait correctement selon la flotte), utilisez le code ci-dessous ...
package quartz; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; public final class RemoveTokens extends QuartzJobBean implements javax.sevlet.http.HttpSessionListener { static java.util.Map<String, HttpSession> httpSessionMap = new HashMap<String, HttpSession>(); void sessionCreated(HttpSessionEvent se) { HttpSession ss = se.getSession(); httpSessionMap.put(ss.getId(), ss); } void sessionDestroyed(HttpSessionEvent se) { HttpSession ss = se.getSession(); httpSessionMap.remove(ss.getId()); } @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { Set<String> keys = httpSessionMap.keySet(); for (String key : keys) { HttpSession ss = httpSessionMap.get(key); Long date = (Long) ss.getAttribute("time"); if (date == null) { date = ss.getCreationTime(); } long curenttime = System.currentTimeMillis(); long difference = curenttime - date; if (difference > (60 * 60 * 1000)) { /*Greater than 1 hour*/ List l = ss.getAttribute("YourList"); l.removeAll(l); ss.setAttribute("time", curenttime); } } } }
Essayé de définir le haricot code> préventive code> comme une session SPOPED Bean avec code> mais il n'a même pas invocé La méthode annotée avec
@scheduler code> dans le même haricot.
Comment chargez-vous le contexte de votre application? Essayez de le charger avec contextloaderListener -> MKKYONG.COM/SPRING3 / Spring-3-MVC-Hello-World-Exemple (5. Intégrez l'application Web avec le printemps)
C'est semblable similaire. Mon fichier
web.xml code> est disponible ici .
Pourriez-vous résoudre le problème?