1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server.session;
20
21 import static java.lang.Math.round;
22
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Enumeration;
26 import java.util.EventListener;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.concurrent.CopyOnWriteArrayList;
32
33 import javax.servlet.SessionCookieConfig;
34 import javax.servlet.SessionTrackingMode;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpSession;
37 import javax.servlet.http.HttpSessionAttributeListener;
38 import javax.servlet.http.HttpSessionBindingEvent;
39 import javax.servlet.http.HttpSessionContext;
40 import javax.servlet.http.HttpSessionEvent;
41 import javax.servlet.http.HttpSessionListener;
42
43 import org.eclipse.jetty.http.HttpCookie;
44 import org.eclipse.jetty.server.Server;
45 import org.eclipse.jetty.server.SessionIdManager;
46 import org.eclipse.jetty.server.SessionManager;
47 import org.eclipse.jetty.server.handler.ContextHandler;
48 import org.eclipse.jetty.util.annotation.ManagedAttribute;
49 import org.eclipse.jetty.util.annotation.ManagedObject;
50 import org.eclipse.jetty.util.annotation.ManagedOperation;
51 import org.eclipse.jetty.util.component.AbstractLifeCycle;
52 import org.eclipse.jetty.util.log.Logger;
53 import org.eclipse.jetty.util.statistic.CounterStatistic;
54 import org.eclipse.jetty.util.statistic.SampleStatistic;
55
56
57
58
59
60
61
62
63
64
65
66 @SuppressWarnings("deprecation")
67 @ManagedObject("Abstract Session Manager")
68 public abstract class AbstractSessionManager extends AbstractLifeCycle implements SessionManager
69 {
70 final static Logger __log = SessionHandler.LOG;
71
72 public Set<SessionTrackingMode> __defaultSessionTrackingModes =
73 Collections.unmodifiableSet(
74 new HashSet<SessionTrackingMode>(
75 Arrays.asList(new SessionTrackingMode[]{SessionTrackingMode.COOKIE,SessionTrackingMode.URL})));
76
77
78
79
80 public final static int __distantFuture=60*60*24*7*52*20;
81
82 static final HttpSessionContext __nullSessionContext=new HttpSessionContext()
83 {
84 public HttpSession getSession(String sessionId)
85 {
86 return null;
87 }
88
89 @SuppressWarnings({ "rawtypes", "unchecked" })
90 public Enumeration getIds()
91 {
92 return Collections.enumeration(Collections.EMPTY_LIST);
93 }
94 };
95
96 private boolean _usingCookies=true;
97
98
99
100
101 protected int _dftMaxIdleSecs=-1;
102 protected SessionHandler _sessionHandler;
103 protected boolean _httpOnly=false;
104 protected SessionIdManager _sessionIdManager;
105 protected boolean _secureCookies=false;
106 protected boolean _secureRequestOnly=true;
107
108 protected final List<HttpSessionAttributeListener> _sessionAttributeListeners = new CopyOnWriteArrayList<HttpSessionAttributeListener>();
109 protected final List<HttpSessionListener> _sessionListeners= new CopyOnWriteArrayList<HttpSessionListener>();
110
111 protected ClassLoader _loader;
112 protected ContextHandler.Context _context;
113 protected String _sessionCookie=__DefaultSessionCookie;
114 protected String _sessionIdPathParameterName = __DefaultSessionIdPathParameterName;
115 protected String _sessionIdPathParameterNamePrefix =";"+ _sessionIdPathParameterName +"=";
116 protected String _sessionDomain;
117 protected String _sessionPath;
118 protected int _maxCookieAge=-1;
119 protected int _refreshCookieAge;
120 protected boolean _nodeIdInSessionId;
121 protected boolean _checkingRemoteSessionIdEncoding;
122 protected String _sessionComment;
123
124 public Set<SessionTrackingMode> _sessionTrackingModes;
125
126 private boolean _usingURLs;
127
128 protected final CounterStatistic _sessionsStats = new CounterStatistic();
129 protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
130
131
132
133 public AbstractSessionManager()
134 {
135 setSessionTrackingModes(__defaultSessionTrackingModes);
136 }
137
138
139 public ContextHandler.Context getContext()
140 {
141 return _context;
142 }
143
144
145 public ContextHandler getContextHandler()
146 {
147 return _context.getContextHandler();
148 }
149
150 @ManagedAttribute("path of the session cookie, or null for default")
151 public String getSessionPath()
152 {
153 return _sessionPath;
154 }
155
156 @ManagedAttribute("if greater the zero, the time in seconds a session cookie will last for")
157 public int getMaxCookieAge()
158 {
159 return _maxCookieAge;
160 }
161
162
163 public HttpCookie access(HttpSession session,boolean secure)
164 {
165 long now=System.currentTimeMillis();
166
167 AbstractSession s = ((SessionIf)session).getSession();
168
169 if (s.access(now))
170 {
171
172 if (isUsingCookies() &&
173 (s.isIdChanged() ||
174 (getSessionCookieConfig().getMaxAge()>0 && getRefreshCookieAge()>0 && ((now-s.getCookieSetTime())/1000>getRefreshCookieAge()))
175 )
176 )
177 {
178 HttpCookie cookie=getSessionCookie(session,_context==null?"/":(_context.getContextPath()),secure);
179 s.cookieSet();
180 s.setIdChanged(false);
181 return cookie;
182 }
183 }
184 return null;
185 }
186
187
188 public void addEventListener(EventListener listener)
189 {
190 if (listener instanceof HttpSessionAttributeListener)
191 _sessionAttributeListeners.add((HttpSessionAttributeListener)listener);
192 if (listener instanceof HttpSessionListener)
193 _sessionListeners.add((HttpSessionListener)listener);
194 }
195
196
197 public void clearEventListeners()
198 {
199 _sessionAttributeListeners.clear();
200 _sessionListeners.clear();
201 }
202
203
204 public void complete(HttpSession session)
205 {
206 AbstractSession s = ((SessionIf)session).getSession();
207 s.complete();
208 }
209
210
211 @Override
212 public void doStart() throws Exception
213 {
214 _context=ContextHandler.getCurrentContext();
215 _loader=Thread.currentThread().getContextClassLoader();
216
217 if (_sessionIdManager==null)
218 {
219 final Server server=getSessionHandler().getServer();
220 synchronized (server)
221 {
222 _sessionIdManager=server.getSessionIdManager();
223 if (_sessionIdManager==null)
224 {
225 _sessionIdManager=new HashSessionIdManager();
226 server.setSessionIdManager(_sessionIdManager);
227 }
228 }
229 }
230 if (!_sessionIdManager.isStarted())
231 _sessionIdManager.start();
232
233
234 if (_context!=null)
235 {
236 String tmp=_context.getInitParameter(SessionManager.__SessionCookieProperty);
237 if (tmp!=null)
238 _sessionCookie=tmp;
239
240 tmp=_context.getInitParameter(SessionManager.__SessionIdPathParameterNameProperty);
241 if (tmp!=null)
242 setSessionIdPathParameterName(tmp);
243
244
245 if (_maxCookieAge==-1)
246 {
247 tmp=_context.getInitParameter(SessionManager.__MaxAgeProperty);
248 if (tmp!=null)
249 _maxCookieAge=Integer.parseInt(tmp.trim());
250 }
251
252
253 if (_sessionDomain==null)
254 _sessionDomain=_context.getInitParameter(SessionManager.__SessionDomainProperty);
255
256
257 if (_sessionPath==null)
258 _sessionPath=_context.getInitParameter(SessionManager.__SessionPathProperty);
259
260 tmp=_context.getInitParameter(SessionManager.__CheckRemoteSessionEncoding);
261 if (tmp!=null)
262 _checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
263 }
264
265 super.doStart();
266 }
267
268
269 @Override
270 public void doStop() throws Exception
271 {
272 super.doStop();
273
274 invalidateSessions();
275
276 _loader=null;
277 }
278
279
280
281
282
283 @ManagedAttribute("true if cookies use the http only flag")
284 public boolean getHttpOnly()
285 {
286 return _httpOnly;
287 }
288
289
290 public HttpSession getHttpSession(String nodeId)
291 {
292 String cluster_id = getSessionIdManager().getClusterId(nodeId);
293
294 AbstractSession session = getSession(cluster_id);
295 if (session!=null && !session.getNodeId().equals(nodeId))
296 session.setIdChanged(true);
297 return session;
298 }
299
300
301
302
303
304
305 public SessionIdManager getIdManager()
306 {
307 return getSessionIdManager();
308 }
309
310
311
312
313
314 @ManagedAttribute("Session ID Manager")
315 public SessionIdManager getSessionIdManager()
316 {
317 return _sessionIdManager;
318 }
319
320
321
322
323
324
325 @Override
326 @ManagedAttribute("defailt maximum time a session may be idle for (in s)")
327 public int getMaxInactiveInterval()
328 {
329 return _dftMaxIdleSecs;
330 }
331
332
333
334
335
336 @Deprecated
337 public int getMaxSessions()
338 {
339 return getSessionsMax();
340 }
341
342
343
344
345
346 @ManagedAttribute("maximum number of simultaneous sessions")
347 public int getSessionsMax()
348 {
349 return (int)_sessionsStats.getMax();
350 }
351
352
353
354
355
356 @ManagedAttribute("total number of sessions")
357 public int getSessionsTotal()
358 {
359 return (int)_sessionsStats.getTotal();
360 }
361
362
363
364
365
366 @Deprecated
367 public SessionIdManager getMetaManager()
368 {
369 return getSessionIdManager();
370 }
371
372
373
374
375
376 @Deprecated
377 public int getMinSessions()
378 {
379 return 0;
380 }
381
382
383 @ManagedAttribute("time before a session cookie is re-set (in s)")
384 public int getRefreshCookieAge()
385 {
386 return _refreshCookieAge;
387 }
388
389
390
391
392
393
394
395
396 @ManagedAttribute("if true, secure cookie flag is set on session cookies")
397 public boolean getSecureCookies()
398 {
399 return _secureCookies;
400 }
401
402
403
404
405
406 public boolean isSecureRequestOnly()
407 {
408 return _secureRequestOnly;
409 }
410
411
412
413
414
415
416
417
418 public void setSecureRequestOnly(boolean secureRequestOnly)
419 {
420 _secureRequestOnly = secureRequestOnly;
421 }
422
423
424
425
426 @ManagedAttribute("the set session cookie")
427 public String getSessionCookie()
428 {
429 return _sessionCookie;
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458 public HttpCookie getSessionCookie(HttpSession session, String contextPath, boolean requestIsSecure)
459 {
460 if (isUsingCookies())
461 {
462 String sessionPath = (_cookieConfig.getPath()==null) ? contextPath : _cookieConfig.getPath();
463 sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
464 String id = getNodeId(session);
465 HttpCookie cookie = null;
466 if (_sessionComment == null)
467 {
468 cookie = new HttpCookie(
469 _cookieConfig.getName(),
470 id,
471 _cookieConfig.getDomain(),
472 sessionPath,
473 _cookieConfig.getMaxAge(),
474 _cookieConfig.isHttpOnly(),
475 _cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure));
476 }
477 else
478 {
479 cookie = new HttpCookie(
480 _cookieConfig.getName(),
481 id,
482 _cookieConfig.getDomain(),
483 sessionPath,
484 _cookieConfig.getMaxAge(),
485 _cookieConfig.isHttpOnly(),
486 _cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure),
487 _sessionComment,
488 1);
489 }
490
491 return cookie;
492 }
493 return null;
494 }
495
496 @ManagedAttribute("domain of the session cookie, or null for the default")
497 public String getSessionDomain()
498 {
499 return _sessionDomain;
500 }
501
502
503
504
505
506 public SessionHandler getSessionHandler()
507 {
508 return _sessionHandler;
509 }
510
511
512
513
514
515 @SuppressWarnings("rawtypes")
516 public Map getSessionMap()
517 {
518 throw new UnsupportedOperationException();
519 }
520
521
522
523
524 @ManagedAttribute("number of currently active sessions")
525 public int getSessions()
526 {
527 return (int)_sessionsStats.getCurrent();
528 }
529
530
531 @ManagedAttribute("name of use for URL session tracking")
532 public String getSessionIdPathParameterName()
533 {
534 return _sessionIdPathParameterName;
535 }
536
537
538 public String getSessionIdPathParameterNamePrefix()
539 {
540 return _sessionIdPathParameterNamePrefix;
541 }
542
543
544
545
546
547 public boolean isUsingCookies()
548 {
549 return _usingCookies;
550 }
551
552
553 public boolean isValid(HttpSession session)
554 {
555 AbstractSession s = ((SessionIf)session).getSession();
556 return s.isValid();
557 }
558
559
560 public String getClusterId(HttpSession session)
561 {
562 AbstractSession s = ((SessionIf)session).getSession();
563 return s.getClusterId();
564 }
565
566
567 public String getNodeId(HttpSession session)
568 {
569 AbstractSession s = ((SessionIf)session).getSession();
570 return s.getNodeId();
571 }
572
573
574
575
576
577 public HttpSession newHttpSession(HttpServletRequest request)
578 {
579 AbstractSession session=newSession(request);
580 session.setMaxInactiveInterval(_dftMaxIdleSecs);
581 addSession(session,true);
582 return session;
583 }
584
585
586 public void removeEventListener(EventListener listener)
587 {
588 if (listener instanceof HttpSessionAttributeListener)
589 _sessionAttributeListeners.remove(listener);
590 if (listener instanceof HttpSessionListener)
591 _sessionListeners.remove(listener);
592 }
593
594
595
596
597
598 @Deprecated
599 public void resetStats()
600 {
601 statsReset();
602 }
603
604
605
606
607
608 @ManagedOperation(value="reset statistics", impact="ACTION")
609 public void statsReset()
610 {
611 _sessionsStats.reset(getSessions());
612 _sessionTimeStats.reset();
613 }
614
615
616
617
618
619
620 public void setHttpOnly(boolean httpOnly)
621 {
622 _httpOnly=httpOnly;
623 }
624
625
626
627
628
629
630 public void setIdManager(SessionIdManager metaManager)
631 {
632 setSessionIdManager(metaManager);
633 }
634
635
636
637
638
639 public void setSessionIdManager(SessionIdManager metaManager)
640 {
641 _sessionIdManager=metaManager;
642 }
643
644
645
646
647
648
649
650 public void setMaxInactiveInterval(int seconds)
651 {
652 _dftMaxIdleSecs=seconds;
653 }
654
655
656
657 public void setRefreshCookieAge(int ageInSeconds)
658 {
659 _refreshCookieAge=ageInSeconds;
660 }
661
662
663
664 public void setSessionCookie(String cookieName)
665 {
666 _sessionCookie=cookieName;
667 }
668
669
670
671
672
673
674
675
676 public void setSessionHandler(SessionHandler sessionHandler)
677 {
678 _sessionHandler=sessionHandler;
679 }
680
681
682
683 public void setSessionIdPathParameterName(String param)
684 {
685 _sessionIdPathParameterName =(param==null||"none".equals(param))?null:param;
686 _sessionIdPathParameterNamePrefix =(param==null||"none".equals(param))?null:(";"+ _sessionIdPathParameterName +"=");
687 }
688
689
690
691
692
693 public void setUsingCookies(boolean usingCookies)
694 {
695 _usingCookies=usingCookies;
696 }
697
698
699 protected abstract void addSession(AbstractSession session);
700
701
702
703
704
705
706 protected void addSession(AbstractSession session, boolean created)
707 {
708 synchronized (_sessionIdManager)
709 {
710 _sessionIdManager.addSession(session);
711 addSession(session);
712 }
713
714 if (created)
715 {
716 _sessionsStats.increment();
717 if (_sessionListeners!=null)
718 {
719 HttpSessionEvent event=new HttpSessionEvent(session);
720 for (HttpSessionListener listener : _sessionListeners)
721 listener.sessionCreated(event);
722 }
723 }
724 }
725
726
727
728
729
730
731
732 public abstract AbstractSession getSession(String idInCluster);
733
734 protected abstract void invalidateSessions() throws Exception;
735
736
737
738
739
740
741
742
743 protected abstract AbstractSession newSession(HttpServletRequest request);
744
745
746
747
748
749
750 public boolean isNodeIdInSessionId()
751 {
752 return _nodeIdInSessionId;
753 }
754
755
756
757
758
759 public void setNodeIdInSessionId(boolean nodeIdInSessionId)
760 {
761 _nodeIdInSessionId=nodeIdInSessionId;
762 }
763
764
765
766
767
768
769
770 public void removeSession(HttpSession session, boolean invalidate)
771 {
772 AbstractSession s = ((SessionIf)session).getSession();
773 removeSession(s,invalidate);
774 }
775
776
777
778
779
780
781
782 public void removeSession(AbstractSession session, boolean invalidate)
783 {
784
785 boolean removed = removeSession(session.getClusterId());
786
787 if (removed)
788 {
789 _sessionsStats.decrement();
790 _sessionTimeStats.set(round((System.currentTimeMillis() - session.getCreationTime())/1000.0));
791
792
793 _sessionIdManager.removeSession(session);
794 if (invalidate)
795 _sessionIdManager.invalidateAll(session.getClusterId());
796
797 if (invalidate && _sessionListeners!=null)
798 {
799 HttpSessionEvent event=new HttpSessionEvent(session);
800 for (int i = _sessionListeners.size()-1; i>=0; i--)
801 {
802 _sessionListeners.get(i).sessionDestroyed(event);
803 }
804 }
805 }
806 }
807
808
809 protected abstract boolean removeSession(String idInCluster);
810
811
812
813
814
815 @ManagedAttribute("maximum amount of time sessions have remained active (in s)")
816 public long getSessionTimeMax()
817 {
818 return _sessionTimeStats.getMax();
819 }
820
821
822 public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
823 {
824 return __defaultSessionTrackingModes;
825 }
826
827
828 public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
829 {
830 return Collections.unmodifiableSet(_sessionTrackingModes);
831 }
832
833
834 @Override
835 public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
836 {
837 _sessionTrackingModes=new HashSet<SessionTrackingMode>(sessionTrackingModes);
838 _usingCookies=_sessionTrackingModes.contains(SessionTrackingMode.COOKIE);
839 _usingURLs=_sessionTrackingModes.contains(SessionTrackingMode.URL);
840 }
841
842
843 @Override
844 public boolean isUsingURLs()
845 {
846 return _usingURLs;
847 }
848
849
850
851 public SessionCookieConfig getSessionCookieConfig()
852 {
853 return _cookieConfig;
854 }
855
856
857 private SessionCookieConfig _cookieConfig =
858 new SessionCookieConfig()
859 {
860 @Override
861 public String getComment()
862 {
863 return _sessionComment;
864 }
865
866 @Override
867 public String getDomain()
868 {
869 return _sessionDomain;
870 }
871
872 @Override
873 public int getMaxAge()
874 {
875 return _maxCookieAge;
876 }
877
878 @Override
879 public String getName()
880 {
881 return _sessionCookie;
882 }
883
884 @Override
885 public String getPath()
886 {
887 return _sessionPath;
888 }
889
890 @Override
891 public boolean isHttpOnly()
892 {
893 return _httpOnly;
894 }
895
896 @Override
897 public boolean isSecure()
898 {
899 return _secureCookies;
900 }
901
902 @Override
903 public void setComment(String comment)
904 {
905 if (_context != null && _context.getContextHandler().isAvailable())
906 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
907 _sessionComment = comment;
908 }
909
910 @Override
911 public void setDomain(String domain)
912 {
913 if (_context != null && _context.getContextHandler().isAvailable())
914 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
915 _sessionDomain=domain;
916 }
917
918 @Override
919 public void setHttpOnly(boolean httpOnly)
920 {
921 if (_context != null && _context.getContextHandler().isAvailable())
922 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
923 _httpOnly=httpOnly;
924 }
925
926 @Override
927 public void setMaxAge(int maxAge)
928 {
929 if (_context != null && _context.getContextHandler().isAvailable())
930 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
931 _maxCookieAge=maxAge;
932 }
933
934 @Override
935 public void setName(String name)
936 {
937 if (_context != null && _context.getContextHandler().isAvailable())
938 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
939 _sessionCookie=name;
940 }
941
942 @Override
943 public void setPath(String path)
944 {
945 if (_context != null && _context.getContextHandler().isAvailable())
946 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
947 _sessionPath=path;
948 }
949
950 @Override
951 public void setSecure(boolean secure)
952 {
953 if (_context != null && _context.getContextHandler().isAvailable())
954 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
955 _secureCookies=secure;
956 }
957
958 };
959
960
961
962
963
964
965 @ManagedAttribute("total time sessions have remained valid")
966 public long getSessionTimeTotal()
967 {
968 return _sessionTimeStats.getTotal();
969 }
970
971
972
973
974
975 @ManagedAttribute("mean time sessions remain valid (in s)")
976 public double getSessionTimeMean()
977 {
978 return _sessionTimeStats.getMean();
979 }
980
981
982
983
984
985 @ManagedAttribute("standard deviation a session remained valid (in s)")
986 public double getSessionTimeStdDev()
987 {
988 return _sessionTimeStats.getStdDev();
989 }
990
991
992
993
994
995 @ManagedAttribute("check remote session id encoding")
996 public boolean isCheckingRemoteSessionIdEncoding()
997 {
998 return _checkingRemoteSessionIdEncoding;
999 }
1000
1001
1002
1003
1004
1005 public void setCheckingRemoteSessionIdEncoding(boolean remote)
1006 {
1007 _checkingRemoteSessionIdEncoding=remote;
1008 }
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018 public interface SessionIf extends HttpSession
1019 {
1020 public AbstractSession getSession();
1021 }
1022
1023 public void doSessionAttributeListeners(AbstractSession session, String name, Object old, Object value)
1024 {
1025 if (!_sessionAttributeListeners.isEmpty())
1026 {
1027 HttpSessionBindingEvent event=new HttpSessionBindingEvent(session,name,old==null?value:old);
1028
1029 for (HttpSessionAttributeListener l : _sessionAttributeListeners)
1030 {
1031 if (old==null)
1032 l.attributeAdded(event);
1033 else if (value==null)
1034 l.attributeRemoved(event);
1035 else
1036 l.attributeReplaced(event);
1037 }
1038 }
1039 }
1040 }