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.Collection; 031import java.util.Enumeration; 032import java.util.HashSet; 033import java.util.Iterator; 034import java.util.Set; 035 036/** 037 * Wrapper class that uses a Shiro {@link Session Session} under the hood for all session operations instead of the 038 * Servlet Container's session mechanism. This is required in heterogeneous client environments where the Session 039 * is used on both the business tier as well as in multiple client technologies (web, swing, flash, etc.) since 040 * Servlet container sessions alone cannot support this feature. 041 * 042 * @since 0.2 043 */ 044@SuppressWarnings("checkstyle:MagicNumber") 045public class ShiroHttpSession implements HttpSession { 046 047 /** 048 * default session id name. 049 */ 050 public static final String DEFAULT_SESSION_ID_NAME = "JSESSIONID"; 051 052 private static final Enumeration<String> EMPTY_ENUMERATION = new Enumeration<>() { 053 public boolean hasMoreElements() { 054 return false; 055 } 056 057 public String nextElement() { 058 return null; 059 } 060 }; 061 062 @SuppressWarnings({"deprecation"}) 063 private static final javax.servlet.http.HttpSessionContext HTTP_SESSION_CONTEXT = 064 new javax.servlet.http.HttpSessionContext() { 065 public HttpSession getSession(String s) { 066 return null; 067 } 068 069 public Enumeration<String> getIds() { 070 return EMPTY_ENUMERATION; 071 } 072 }; 073 074 protected ServletContext servletContext; 075 protected HttpServletRequest currentRequest; 076 //'real' Shiro Session 077 protected Session session; 078 079 public ShiroHttpSession(Session session, HttpServletRequest currentRequest, ServletContext servletContext) { 080 if (session instanceof HttpServletSession) { 081 String msg = "Session constructor argument cannot be an instance of HttpServletSession. This is enforced to " 082 + "prevent circular dependencies and infinite loops."; 083 throw new IllegalArgumentException(msg); 084 } 085 this.session = session; 086 this.currentRequest = currentRequest; 087 this.servletContext = servletContext; 088 } 089 090 public Session getSession() { 091 return this.session; 092 } 093 094 public long getCreationTime() { 095 try { 096 return getSession().getStartTimestamp().getTime(); 097 } catch (Exception e) { 098 throw new IllegalStateException(e); 099 } 100 } 101 102 public String getId() { 103 return getSession().getId().toString(); 104 } 105 106 public long getLastAccessedTime() { 107 return getSession().getLastAccessTime().getTime(); 108 } 109 110 public ServletContext getServletContext() { 111 return this.servletContext; 112 } 113 114 public void setMaxInactiveInterval(int i) { 115 try { 116 getSession().setTimeout(i * 1000L); 117 } catch (InvalidSessionException e) { 118 throw new IllegalStateException(e); 119 } 120 } 121 122 public int getMaxInactiveInterval() { 123 try { 124 return (Long.valueOf(getSession().getTimeout() / 1000)).intValue(); 125 } catch (InvalidSessionException e) { 126 throw new IllegalStateException(e); 127 } 128 } 129 130 @SuppressWarnings({"deprecation"}) 131 public javax.servlet.http.HttpSessionContext getSessionContext() { 132 return HTTP_SESSION_CONTEXT; 133 } 134 135 public Object getAttribute(String s) { 136 try { 137 return getSession().getAttribute(s); 138 } catch (InvalidSessionException e) { 139 throw new IllegalStateException(e); 140 } 141 } 142 143 @Deprecated 144 @Override 145 public Object getValue(String s) { 146 return getAttribute(s); 147 } 148 149 protected Set<String> getKeyNames() { 150 Collection<Object> keySet; 151 try { 152 keySet = getSession().getAttributeKeys(); 153 } catch (InvalidSessionException e) { 154 throw new IllegalStateException(e); 155 } 156 Set<String> keyNames; 157 if (keySet != null && !keySet.isEmpty()) { 158 keyNames = new HashSet<String>(keySet.size()); 159 for (Object o : keySet) { 160 keyNames.add(o.toString()); 161 } 162 } else { 163 keyNames = Set.of(); 164 } 165 return keyNames; 166 } 167 168 @Override 169 public Enumeration<String> getAttributeNames() { 170 Set<String> keyNames = getKeyNames(); 171 final Iterator<String> iterator = keyNames.iterator(); 172 return new Enumeration<>() { 173 public boolean hasMoreElements() { 174 return iterator.hasNext(); 175 } 176 177 public String nextElement() { 178 return iterator.next(); 179 } 180 }; 181 } 182 183 @Deprecated 184 public String[] getValueNames() { 185 Set<String> keyNames = getKeyNames(); 186 String[] array = new String[keyNames.size()]; 187 if (keyNames.size() > 0) { 188 array = keyNames.toArray(array); 189 } 190 return array; 191 } 192 193 protected void afterBound(String s, Object o) { 194 if (o instanceof HttpSessionBindingListener) { 195 HttpSessionBindingListener listener = (HttpSessionBindingListener) o; 196 HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, s, o); 197 listener.valueBound(event); 198 } 199 } 200 201 protected void afterUnbound(String s, Object o) { 202 if (o instanceof HttpSessionBindingListener) { 203 HttpSessionBindingListener listener = (HttpSessionBindingListener) o; 204 HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, s, o); 205 listener.valueUnbound(event); 206 } 207 } 208 209 public void setAttribute(String s, Object o) { 210 try { 211 getSession().setAttribute(s, o); 212 afterBound(s, o); 213 } catch (InvalidSessionException e) { 214 //noinspection finally 215 try { 216 afterUnbound(s, o); 217 } finally { 218 //noinspection ThrowFromFinallyBlock 219 throw new IllegalStateException(e); 220 } 221 } 222 } 223 224 @Deprecated 225 public void putValue(String s, Object o) { 226 setAttribute(s, o); 227 } 228 229 public void removeAttribute(String s) { 230 try { 231 Object attribute = getSession().removeAttribute(s); 232 afterUnbound(s, attribute); 233 } catch (InvalidSessionException e) { 234 throw new IllegalStateException(e); 235 } 236 } 237 238 @Deprecated 239 public void removeValue(String s) { 240 removeAttribute(s); 241 } 242 243 public void invalidate() { 244 try { 245 getSession().stop(); 246 } catch (InvalidSessionException e) { 247 throw new IllegalStateException(e); 248 } 249 } 250 251 public boolean isNew() { 252 Boolean value = (Boolean) currentRequest.getAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW); 253 return value != null && value.equals(Boolean.TRUE); 254 } 255}