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 */ 019package org.apache.shiro.web.servlet; 020 021import org.apache.shiro.session.InvalidSessionException; 022import org.apache.shiro.session.Session; 023import org.apache.shiro.web.session.HttpServletSession; 024 025import javax.servlet.ServletContext; 026import javax.servlet.http.HttpServletRequest; 027import javax.servlet.http.HttpSession; 028import javax.servlet.http.HttpSessionBindingEvent; 029import javax.servlet.http.HttpSessionBindingListener; 030import 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 */ 041public 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 * 1000L); 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}