001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.web.servlet;
020
021 import org.apache.shiro.session.InvalidSessionException;
022 import org.apache.shiro.session.Session;
023 import org.apache.shiro.web.session.HttpServletSession;
024
025 import javax.servlet.ServletContext;
026 import javax.servlet.http.HttpServletRequest;
027 import javax.servlet.http.HttpSession;
028 import javax.servlet.http.HttpSessionBindingEvent;
029 import javax.servlet.http.HttpSessionBindingListener;
030 import java.util.*;
031
032
033 /**
034 * Wrapper class that uses a Shiro {@link Session Session} under the hood for all session operations instead of the
035 * Servlet Container's session mechanism. This is required in heterogeneous client environments where the Session
036 * is used on both the business tier as well as in multiple client technologies (web, swing, flash, etc) since
037 * Servlet container sessions alone cannot support this feature.
038 *
039 * @since 0.2
040 */
041 public class ShiroHttpSession implements HttpSession {
042
043 //TODO - complete JavaDoc
044
045 public static final String DEFAULT_SESSION_ID_NAME = "JSESSIONID";
046
047 private static final Enumeration EMPTY_ENUMERATION = new Enumeration() {
048 public boolean hasMoreElements() {
049 return false;
050 }
051
052 public Object nextElement() {
053 return null;
054 }
055 };
056
057 @SuppressWarnings({"deprecation"})
058 private static final javax.servlet.http.HttpSessionContext HTTP_SESSION_CONTEXT =
059 new javax.servlet.http.HttpSessionContext() {
060 public HttpSession getSession(String s) {
061 return null;
062 }
063
064 public Enumeration getIds() {
065 return EMPTY_ENUMERATION;
066 }
067 };
068
069 protected ServletContext servletContext = null;
070 protected HttpServletRequest currentRequest = null;
071 protected Session session = null; //'real' Shiro Session
072
073 public ShiroHttpSession(Session session, HttpServletRequest currentRequest, ServletContext servletContext) {
074 if (session instanceof HttpServletSession) {
075 String msg = "Session constructor argument cannot be an instance of HttpServletSession. This is enforced to " +
076 "prevent circular dependencies and infinite loops.";
077 throw new IllegalArgumentException(msg);
078 }
079 this.session = session;
080 this.currentRequest = currentRequest;
081 this.servletContext = servletContext;
082 }
083
084 public Session getSession() {
085 return this.session;
086 }
087
088 public long getCreationTime() {
089 try {
090 return getSession().getStartTimestamp().getTime();
091 } catch (Exception e) {
092 throw new IllegalStateException(e);
093 }
094 }
095
096 public String getId() {
097 return getSession().getId().toString();
098 }
099
100 public long getLastAccessedTime() {
101 return getSession().getLastAccessTime().getTime();
102 }
103
104 public ServletContext getServletContext() {
105 return this.servletContext;
106 }
107
108 public void setMaxInactiveInterval(int i) {
109 try {
110 getSession().setTimeout(i * 1000);
111 } catch (InvalidSessionException e) {
112 throw new IllegalStateException(e);
113 }
114 }
115
116 public int getMaxInactiveInterval() {
117 try {
118 return (new Long(getSession().getTimeout() / 1000)).intValue();
119 } catch (InvalidSessionException e) {
120 throw new IllegalStateException(e);
121 }
122 }
123
124 @SuppressWarnings({"deprecation"})
125 public javax.servlet.http.HttpSessionContext getSessionContext() {
126 return HTTP_SESSION_CONTEXT;
127 }
128
129 public Object getAttribute(String s) {
130 try {
131 return getSession().getAttribute(s);
132 } catch (InvalidSessionException e) {
133 throw new IllegalStateException(e);
134 }
135 }
136
137 public Object getValue(String s) {
138 return getAttribute(s);
139 }
140
141 @SuppressWarnings({"unchecked"})
142 protected Set<String> getKeyNames() {
143 Collection<Object> keySet;
144 try {
145 keySet = getSession().getAttributeKeys();
146 } catch (InvalidSessionException e) {
147 throw new IllegalStateException(e);
148 }
149 Set<String> keyNames;
150 if (keySet != null && !keySet.isEmpty()) {
151 keyNames = new HashSet<String>(keySet.size());
152 for (Object o : keySet) {
153 keyNames.add(o.toString());
154 }
155 } else {
156 keyNames = Collections.EMPTY_SET;
157 }
158 return keyNames;
159 }
160
161 public Enumeration getAttributeNames() {
162 Set<String> keyNames = getKeyNames();
163 final Iterator iterator = keyNames.iterator();
164 return new Enumeration() {
165 public boolean hasMoreElements() {
166 return iterator.hasNext();
167 }
168
169 public Object nextElement() {
170 return iterator.next();
171 }
172 };
173 }
174
175 public String[] getValueNames() {
176 Set<String> keyNames = getKeyNames();
177 String[] array = new String[keyNames.size()];
178 if (keyNames.size() > 0) {
179 array = keyNames.toArray(array);
180 }
181 return array;
182 }
183
184 protected void afterBound(String s, Object o) {
185 if (o instanceof HttpSessionBindingListener) {
186 HttpSessionBindingListener listener = (HttpSessionBindingListener) o;
187 HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, s, o);
188 listener.valueBound(event);
189 }
190 }
191
192 protected void afterUnbound(String s, Object o) {
193 if (o instanceof HttpSessionBindingListener) {
194 HttpSessionBindingListener listener = (HttpSessionBindingListener) o;
195 HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, s, o);
196 listener.valueUnbound(event);
197 }
198 }
199
200 public void setAttribute(String s, Object o) {
201 try {
202 getSession().setAttribute(s, o);
203 afterBound(s, o);
204 } catch (InvalidSessionException e) {
205 //noinspection finally
206 try {
207 afterUnbound(s, o);
208 } finally {
209 //noinspection ThrowFromFinallyBlock
210 throw new IllegalStateException(e);
211 }
212 }
213 }
214
215 public void putValue(String s, Object o) {
216 setAttribute(s, o);
217 }
218
219 public void removeAttribute(String s) {
220 try {
221 Object attribute = getSession().removeAttribute(s);
222 afterUnbound(s, attribute);
223 } catch (InvalidSessionException e) {
224 throw new IllegalStateException(e);
225 }
226 }
227
228 public void removeValue(String s) {
229 removeAttribute(s);
230 }
231
232 public void invalidate() {
233 try {
234 getSession().stop();
235 } catch (InvalidSessionException e) {
236 throw new IllegalStateException(e);
237 }
238 }
239
240 public boolean isNew() {
241 Boolean value = (Boolean) currentRequest.getAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW);
242 return value != null && value.equals(Boolean.TRUE);
243 }
244 }