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.session.ExpiredSessionException;
22 import org.apache.shiro.session.InvalidSessionException;
23 import org.apache.shiro.session.StoppedSessionException;
24 import org.apache.shiro.util.CollectionUtils;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 import java.io.IOException;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.io.Serializable;
32 import java.text.DateFormat;
33 import java.util.*;
34
35
36
37
38
39
40
41
42 public class SimpleSession implements ValidatingSession, Serializable {
43
44
45
46
47
48
49 private static final long serialVersionUID = -7125642695178165650L;
50
51
52 private transient static final Logger log = LoggerFactory.getLogger(SimpleSession.class);
53
54 protected static final long MILLIS_PER_SECOND = 1000;
55 protected static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
56 protected static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
57
58
59 static int bitIndexCounter = 0;
60 private static final int ID_BIT_MASK = 1 << bitIndexCounter++;
61 private static final int START_TIMESTAMP_BIT_MASK = 1 << bitIndexCounter++;
62 private static final int STOP_TIMESTAMP_BIT_MASK = 1 << bitIndexCounter++;
63 private static final int LAST_ACCESS_TIME_BIT_MASK = 1 << bitIndexCounter++;
64 private static final int TIMEOUT_BIT_MASK = 1 << bitIndexCounter++;
65 private static final int EXPIRED_BIT_MASK = 1 << bitIndexCounter++;
66 private static final int HOST_BIT_MASK = 1 << bitIndexCounter++;
67 private static final int ATTRIBUTES_BIT_MASK = 1 << bitIndexCounter++;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 private transient Serializable id;
87 private transient Date startTimestamp;
88 private transient Date stopTimestamp;
89 private transient Date lastAccessTime;
90 private transient long timeout;
91 private transient boolean expired;
92 private transient String host;
93 private transient Map<Object, Object> attributes;
94
95 public SimpleSession() {
96 this.timeout = DefaultSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT;
97 this.startTimestamp = new Date();
98 this.lastAccessTime = this.startTimestamp;
99 }
100
101 public SimpleSession(String host) {
102 this();
103 this.host = host;
104 }
105
106 public Serializable getId() {
107 return this.id;
108 }
109
110 public void setId(Serializable id) {
111 this.id = id;
112 }
113
114 public Date getStartTimestamp() {
115 return startTimestamp;
116 }
117
118 public void setStartTimestamp(Date startTimestamp) {
119 this.startTimestamp = startTimestamp;
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139 public Date getStopTimestamp() {
140 return stopTimestamp;
141 }
142
143 public void setStopTimestamp(Date stopTimestamp) {
144 this.stopTimestamp = stopTimestamp;
145 }
146
147 public Date getLastAccessTime() {
148 return lastAccessTime;
149 }
150
151 public void setLastAccessTime(Date lastAccessTime) {
152 this.lastAccessTime = lastAccessTime;
153 }
154
155
156
157
158
159
160
161 public boolean isExpired() {
162 return expired;
163 }
164
165 public void setExpired(boolean expired) {
166 this.expired = expired;
167 }
168
169 public long getTimeout() {
170 return timeout;
171 }
172
173 public void setTimeout(long timeout) {
174 this.timeout = timeout;
175 }
176
177 public String getHost() {
178 return host;
179 }
180
181 public void setHost(String host) {
182 this.host = host;
183 }
184
185 public Map<Object, Object> getAttributes() {
186 return attributes;
187 }
188
189 public void setAttributes(Map<Object, Object> attributes) {
190 this.attributes = attributes;
191 }
192
193 public void touch() {
194 this.lastAccessTime = new Date();
195 }
196
197 public void stop() {
198 if (this.stopTimestamp == null) {
199 this.stopTimestamp = new Date();
200 }
201 }
202
203 protected boolean isStopped() {
204 return getStopTimestamp() != null;
205 }
206
207 protected void expire() {
208 stop();
209 this.expired = true;
210 }
211
212
213
214
215 public boolean isValid() {
216 return !isStopped() && !isExpired();
217 }
218
219
220
221
222
223
224 protected boolean isTimedOut() {
225
226 if (isExpired()) {
227 return true;
228 }
229
230 long timeout = getTimeout();
231
232 if (timeout >= 0l) {
233
234 Date lastAccessTime = getLastAccessTime();
235
236 if (lastAccessTime == null) {
237 String msg = "session.lastAccessTime for session with id [" +
238 getId() + "] is null. This value must be set at " +
239 "least once, preferably at least upon instantiation. Please check the " +
240 getClass().getName() + " implementation and ensure " +
241 "this value will be set (perhaps in the constructor?)";
242 throw new IllegalStateException(msg);
243 }
244
245
246
247
248
249
250 long expireTimeMillis = System.currentTimeMillis() - timeout;
251 Date expireTime = new Date(expireTimeMillis);
252 return lastAccessTime.before(expireTime);
253 } else {
254 if (log.isTraceEnabled()) {
255 log.trace("No timeout for session with id [" + getId() +
256 "]. Session is not considered expired.");
257 }
258 }
259
260 return false;
261 }
262
263 public void validate() throws InvalidSessionException {
264
265 if (isStopped()) {
266
267 String msg = "Session with id [" + getId() + "] has been " +
268 "explicitly stopped. No further interaction under this session is " +
269 "allowed.";
270 throw new StoppedSessionException(msg);
271 }
272
273
274 if (isTimedOut()) {
275 expire();
276
277
278 Date lastAccessTime = getLastAccessTime();
279 long timeout = getTimeout();
280
281 Serializable sessionId = getId();
282
283 DateFormat df = DateFormat.getInstance();
284 String msg = "Session with id [" + sessionId + "] has expired. " +
285 "Last access time: " + df.format(lastAccessTime) +
286 ". Current time: " + df.format(new Date()) +
287 ". Session timeout is set to " + timeout / MILLIS_PER_SECOND + " seconds (" +
288 timeout / MILLIS_PER_MINUTE + " minutes)";
289 if (log.isTraceEnabled()) {
290 log.trace(msg);
291 }
292 throw new ExpiredSessionException(msg);
293 }
294 }
295
296 private Map<Object, Object> getAttributesLazy() {
297 Map<Object, Object> attributes = getAttributes();
298 if (attributes == null) {
299 attributes = new HashMap<Object, Object>();
300 setAttributes(attributes);
301 }
302 return attributes;
303 }
304
305 public Collection<Object> getAttributeKeys() throws InvalidSessionException {
306 Map<Object, Object> attributes = getAttributes();
307 if (attributes == null) {
308 return Collections.emptySet();
309 }
310 return attributes.keySet();
311 }
312
313 public Object getAttribute(Object key) {
314 Map<Object, Object> attributes = getAttributes();
315 if (attributes == null) {
316 return null;
317 }
318 return attributes.get(key);
319 }
320
321 public void setAttribute(Object key, Object value) {
322 if (value == null) {
323 removeAttribute(key);
324 } else {
325 getAttributesLazy().put(key, value);
326 }
327 }
328
329 public Object removeAttribute(Object key) {
330 Map<Object, Object> attributes = getAttributes();
331 if (attributes == null) {
332 return null;
333 } else {
334 return attributes.remove(key);
335 }
336 }
337
338
339
340
341
342
343
344
345
346
347
348
349
350 @Override
351 public boolean equals(Object obj) {
352 if (this == obj) {
353 return true;
354 }
355 if (obj instanceof SimpleSession) {
356 SimpleSession../../../org/apache/shiro/session/mgt/SimpleSession.html#SimpleSession">SimpleSession other = (SimpleSession) obj;
357 Serializable thisId = getId();
358 Serializable otherId = other.getId();
359 if (thisId != null && otherId != null) {
360 return thisId.equals(otherId);
361 } else {
362
363 return onEquals(other);
364 }
365 }
366 return false;
367 }
368
369
370
371
372
373
374
375
376
377 protected boolean onEquals(SimpleSession ss) {
378 return (getStartTimestamp() != null ? getStartTimestamp().equals(ss.getStartTimestamp()) : ss.getStartTimestamp() == null) &&
379 (getStopTimestamp() != null ? getStopTimestamp().equals(ss.getStopTimestamp()) : ss.getStopTimestamp() == null) &&
380 (getLastAccessTime() != null ? getLastAccessTime().equals(ss.getLastAccessTime()) : ss.getLastAccessTime() == null) &&
381 (getTimeout() == ss.getTimeout()) &&
382 (isExpired() == ss.isExpired()) &&
383 (getHost() != null ? getHost().equals(ss.getHost()) : ss.getHost() == null) &&
384 (getAttributes() != null ? getAttributes().equals(ss.getAttributes()) : ss.getAttributes() == null);
385 }
386
387
388
389
390
391
392
393
394
395
396
397 @Override
398 public int hashCode() {
399 Serializable id = getId();
400 if (id != null) {
401 return id.hashCode();
402 }
403 int hashCode = getStartTimestamp() != null ? getStartTimestamp().hashCode() : 0;
404 hashCode = 31 * hashCode + (getStopTimestamp() != null ? getStopTimestamp().hashCode() : 0);
405 hashCode = 31 * hashCode + (getLastAccessTime() != null ? getLastAccessTime().hashCode() : 0);
406 hashCode = 31 * hashCode + Long.valueOf(Math.max(getTimeout(), 0)).hashCode();
407 hashCode = 31 * hashCode + Boolean.valueOf(isExpired()).hashCode();
408 hashCode = 31 * hashCode + (getHost() != null ? getHost().hashCode() : 0);
409 hashCode = 31 * hashCode + (getAttributes() != null ? getAttributes().hashCode() : 0);
410 return hashCode;
411 }
412
413
414
415
416
417
418
419
420
421 @Override
422 public String toString() {
423 StringBuilder sb = new StringBuilder();
424 sb.append(getClass().getName()).append(",id=").append(getId());
425 return sb.toString();
426 }
427
428
429
430
431
432
433
434
435 private void writeObject(ObjectOutputStream out) throws IOException {
436 out.defaultWriteObject();
437 short alteredFieldsBitMask = getAlteredFieldsBitMask();
438 out.writeShort(alteredFieldsBitMask);
439 if (id != null) {
440 out.writeObject(id);
441 }
442 if (startTimestamp != null) {
443 out.writeObject(startTimestamp);
444 }
445 if (stopTimestamp != null) {
446 out.writeObject(stopTimestamp);
447 }
448 if (lastAccessTime != null) {
449 out.writeObject(lastAccessTime);
450 }
451 if (timeout != 0l) {
452 out.writeLong(timeout);
453 }
454 if (expired) {
455 out.writeBoolean(expired);
456 }
457 if (host != null) {
458 out.writeUTF(host);
459 }
460 if (!CollectionUtils.isEmpty(attributes)) {
461 out.writeObject(attributes);
462 }
463 }
464
465
466
467
468
469
470
471
472
473 @SuppressWarnings({"unchecked"})
474 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
475 in.defaultReadObject();
476 short bitMask = in.readShort();
477
478 if (isFieldPresent(bitMask, ID_BIT_MASK)) {
479 this.id = (Serializable) in.readObject();
480 }
481 if (isFieldPresent(bitMask, START_TIMESTAMP_BIT_MASK)) {
482 this.startTimestamp = (Date) in.readObject();
483 }
484 if (isFieldPresent(bitMask, STOP_TIMESTAMP_BIT_MASK)) {
485 this.stopTimestamp = (Date) in.readObject();
486 }
487 if (isFieldPresent(bitMask, LAST_ACCESS_TIME_BIT_MASK)) {
488 this.lastAccessTime = (Date) in.readObject();
489 }
490 if (isFieldPresent(bitMask, TIMEOUT_BIT_MASK)) {
491 this.timeout = in.readLong();
492 }
493 if (isFieldPresent(bitMask, EXPIRED_BIT_MASK)) {
494 this.expired = in.readBoolean();
495 }
496 if (isFieldPresent(bitMask, HOST_BIT_MASK)) {
497 this.host = in.readUTF();
498 }
499 if (isFieldPresent(bitMask, ATTRIBUTES_BIT_MASK)) {
500 this.attributes = (Map<Object, Object>) in.readObject();
501 }
502 }
503
504
505
506
507
508
509
510
511
512 private short getAlteredFieldsBitMask() {
513 int bitMask = 0;
514 bitMask = id != null ? bitMask | ID_BIT_MASK : bitMask;
515 bitMask = startTimestamp != null ? bitMask | START_TIMESTAMP_BIT_MASK : bitMask;
516 bitMask = stopTimestamp != null ? bitMask | STOP_TIMESTAMP_BIT_MASK : bitMask;
517 bitMask = lastAccessTime != null ? bitMask | LAST_ACCESS_TIME_BIT_MASK : bitMask;
518 bitMask = timeout != 0l ? bitMask | TIMEOUT_BIT_MASK : bitMask;
519 bitMask = expired ? bitMask | EXPIRED_BIT_MASK : bitMask;
520 bitMask = host != null ? bitMask | HOST_BIT_MASK : bitMask;
521 bitMask = !CollectionUtils.isEmpty(attributes) ? bitMask | ATTRIBUTES_BIT_MASK : bitMask;
522 return (short) bitMask;
523 }
524
525
526
527
528
529
530
531
532
533
534
535
536
537 private static boolean isFieldPresent(short bitMask, int fieldBitMask) {
538 return (bitMask & fieldBitMask) != 0;
539 }
540
541 }