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 * 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}