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