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