1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.shiro.session.mgt;
20
21 import org.apache.shiro.authz.AuthorizationException;
22 import org.apache.shiro.session.ExpiredSessionException;
23 import org.apache.shiro.session.InvalidSessionException;
24 import org.apache.shiro.session.Session;
25 import org.apache.shiro.session.UnknownSessionException;
26 import org.apache.shiro.lang.util.Destroyable;
27 import org.apache.shiro.lang.util.LifecycleUtils;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import java.util.Collection;
32
33
34
35
36
37
38
39 public abstract class AbstractValidatingSessionManager extends AbstractNativeSessionManager
40 implements ValidatingSessionManager, Destroyable {
41
42
43
44
45
46 public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = MILLIS_PER_HOUR;
47
48 private static final Logger LOGGER = LoggerFactory.getLogger(AbstractValidatingSessionManager.class);
49
50 protected boolean sessionValidationSchedulerEnabled;
51
52
53
54
55 protected SessionValidationScheduler sessionValidationScheduler;
56
57 protected long sessionValidationInterval;
58
59 public AbstractValidatingSessionManager() {
60 this.sessionValidationSchedulerEnabled = true;
61 this.sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
62 }
63
64 public boolean isSessionValidationSchedulerEnabled() {
65 return sessionValidationSchedulerEnabled;
66 }
67
68 @SuppressWarnings({"UnusedDeclaration"})
69 public void setSessionValidationSchedulerEnabled(boolean sessionValidationSchedulerEnabled) {
70 this.sessionValidationSchedulerEnabled = sessionValidationSchedulerEnabled;
71 }
72
73 public void setSessionValidationScheduler(SessionValidationScheduler sessionValidationScheduler) {
74 this.sessionValidationScheduler = sessionValidationScheduler;
75 }
76
77 public SessionValidationScheduler getSessionValidationScheduler() {
78 return sessionValidationScheduler;
79 }
80
81 private void enableSessionValidationIfNecessary() {
82 SessionValidationScheduler scheduler = getSessionValidationScheduler();
83 if (isSessionValidationSchedulerEnabled() && (scheduler == null || !scheduler.isEnabled())) {
84 enableSessionValidation();
85 }
86 }
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 public void setSessionValidationInterval(long sessionValidationInterval) {
103 this.sessionValidationInterval = sessionValidationInterval;
104 }
105
106 public long getSessionValidationInterval() {
107 return sessionValidationInterval;
108 }
109
110 @Override
111 protected final Session doGetSession(final SessionKey key) throws InvalidSessionException {
112 enableSessionValidationIfNecessary();
113
114 LOGGER.trace("Attempting to retrieve session with key {}", key);
115
116 Session s = retrieveSession(key);
117 if (s != null) {
118 validate(s, key);
119 }
120 return s;
121 }
122
123
124
125
126
127
128
129
130 protected abstract Session retrieveSession(SessionKey key) throws UnknownSessionException;
131
132 protected Session createSession(SessionContext context) throws AuthorizationException {
133 enableSessionValidationIfNecessary();
134 return doCreateSession(context);
135 }
136
137 protected abstract Session doCreateSession(SessionContext initData) throws AuthorizationException;
138
139 protected void validate(Session session, SessionKey key) throws InvalidSessionException {
140 try {
141 doValidate(session);
142 } catch (ExpiredSessionException ese) {
143 onExpiration(session, ese, key);
144 throw ese;
145 } catch (InvalidSessionException ise) {
146 onInvalidation(session, ise, key);
147 throw ise;
148 }
149 }
150
151 protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
152 LOGGER.trace("Session with id [{}] has expired.", s.getId());
153 try {
154 onExpiration(s);
155 notifyExpiration(s);
156 } finally {
157 afterExpired(s);
158 }
159 }
160
161 protected void onExpiration(Session session) {
162 onChange(session);
163 }
164
165 protected void afterExpired(Session session) {
166 }
167
168 protected void onInvalidation(Session s, InvalidSessionException ise, SessionKey key) {
169 if (ise instanceof ExpiredSessionException) {
170 onExpiration(s, (ExpiredSessionException) ise, key);
171 return;
172 }
173 LOGGER.trace("Session with id [{}] is invalid.", s.getId());
174 try {
175 onStop(s);
176 notifyStop(s);
177 } finally {
178 afterStopped(s);
179 }
180 }
181
182 protected void doValidate(Session session) throws InvalidSessionException {
183 if (session instanceof ValidatingSession) {
184 ((ValidatingSession) session).validate();
185 } else {
186 String msg = "The " + getClass().getName() + " implementation only supports validating "
187 + "Session implementations of the " + ValidatingSession.class.getName() + " interface. "
188 + "Please either implement this interface in your session implementation or override the "
189 + AbstractValidatingSessionManager.class.getName() + ".doValidate(Session) method to perform validation.";
190 throw new IllegalStateException(msg);
191 }
192 }
193
194
195
196
197
198
199
200
201
202
203 protected long getTimeout(Session session) {
204 return session.getTimeout();
205 }
206
207 protected SessionValidationScheduler createSessionValidationScheduler() {
208 ExecutorServiceSessionValidationScheduler scheduler;
209
210 if (LOGGER.isDebugEnabled()) {
211 LOGGER.debug("No sessionValidationScheduler set. Attempting to create default instance.");
212 }
213 scheduler = new ExecutorServiceSessionValidationScheduler(this);
214 scheduler.setSessionValidationInterval(getSessionValidationInterval());
215 if (LOGGER.isTraceEnabled()) {
216 LOGGER.trace("Created default SessionValidationScheduler instance of type [" + scheduler.getClass().getName() + "].");
217 }
218 return scheduler;
219 }
220
221 protected synchronized void enableSessionValidation() {
222 SessionValidationScheduler scheduler = getSessionValidationScheduler();
223 if (scheduler == null) {
224 scheduler = createSessionValidationScheduler();
225 setSessionValidationScheduler(scheduler);
226 }
227
228
229 if (!scheduler.isEnabled()) {
230 if (LOGGER.isInfoEnabled()) {
231 LOGGER.info("Enabling session validation scheduler...");
232 }
233 scheduler.enableSessionValidation();
234 afterSessionValidationEnabled();
235 }
236 }
237
238 protected void afterSessionValidationEnabled() {
239 }
240
241 protected synchronized void disableSessionValidation() {
242 beforeSessionValidationDisabled();
243 SessionValidationScheduler scheduler = getSessionValidationScheduler();
244 if (scheduler != null) {
245 try {
246 scheduler.disableSessionValidation();
247 if (LOGGER.isInfoEnabled()) {
248 LOGGER.info("Disabled session validation scheduler.");
249 }
250 } catch (Exception e) {
251 if (LOGGER.isDebugEnabled()) {
252 String msg = "Unable to disable SessionValidationScheduler. Ignoring (shutting down)...";
253 LOGGER.debug(msg, e);
254 }
255 }
256 LifecycleUtils.destroy(scheduler);
257 setSessionValidationScheduler(null);
258 }
259 }
260
261 protected void beforeSessionValidationDisabled() {
262 }
263
264 public void destroy() {
265 disableSessionValidation();
266 }
267
268
269
270
271 public void validateSessions() {
272 if (LOGGER.isInfoEnabled()) {
273 LOGGER.info("Validating all active sessions...");
274 }
275
276 int invalidCount = 0;
277
278 Collection<Session> activeSessions = getActiveSessions();
279
280 if (activeSessions != null && !activeSessions.isEmpty()) {
281 for (Session s : activeSessions) {
282 try {
283
284
285 SessionKey key = new DefaultSessionKey(s.getId());
286 validate(s, key);
287 } catch (InvalidSessionException e) {
288 if (LOGGER.isDebugEnabled()) {
289 boolean expired = (e instanceof ExpiredSessionException);
290 String msg = "Invalidated session with id [" + s.getId() + "]"
291 + (expired ? " (expired)" : " (stopped)");
292 LOGGER.debug(msg);
293 }
294 invalidCount++;
295 }
296 }
297 }
298
299 if (LOGGER.isInfoEnabled()) {
300 String msg = "Finished session validation.";
301 if (invalidCount > 0) {
302 msg += " [" + invalidCount + "] sessions were stopped.";
303 } else {
304 msg += " No sessions were stopped.";
305 }
306 LOGGER.info(msg);
307 }
308 }
309
310 protected abstract Collection<Session> getActiveSessions();
311 }