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.SecurityUtils;
022import org.apache.shiro.session.Session;
023import org.apache.shiro.subject.Subject;
024import org.apache.shiro.subject.support.DisabledSessionException;
025import org.apache.shiro.web.util.WebUtils;
026
027import javax.servlet.ServletContext;
028import javax.servlet.http.HttpServletRequest;
029import javax.servlet.http.HttpServletRequestWrapper;
030import javax.servlet.http.HttpSession;
031import java.security.Principal;
032
033
034/**
035 * A {@code ShiroHttpServletRequest} wraps the Servlet container's original {@code ServletRequest} instance, but ensures
036 * that all {@link HttpServletRequest} invocations that require Shiro's support ({@link #getRemoteUser getRemoteUser},
037 * {@link #getSession getSession}, etc) can be executed first by Shiro as necessary before allowing the underlying
038 * Servlet container instance's method to be invoked.
039 *
040 * @since 0.2
041 */
042public class ShiroHttpServletRequest extends HttpServletRequestWrapper {
043
044    //TODO - complete JavaDoc
045
046    //The following 7 constants support the Shiro's implementation of the Servlet Specification
047    public static final String COOKIE_SESSION_ID_SOURCE = "cookie";
048    public static final String URL_SESSION_ID_SOURCE = "url";
049    public static final String REFERENCED_SESSION_ID = ShiroHttpServletRequest.class.getName() + "_REQUESTED_SESSION_ID";
050    public static final String REFERENCED_SESSION_ID_IS_VALID = ShiroHttpServletRequest.class.getName() + "_REQUESTED_SESSION_ID_VALID";
051    public static final String REFERENCED_SESSION_IS_NEW = ShiroHttpServletRequest.class.getName() + "_REFERENCED_SESSION_IS_NEW";
052    public static final String REFERENCED_SESSION_ID_SOURCE = ShiroHttpServletRequest.class.getName() + "REFERENCED_SESSION_ID_SOURCE";
053    public static final String IDENTITY_REMOVED_KEY = ShiroHttpServletRequest.class.getName() + "_IDENTITY_REMOVED_KEY";
054
055    protected ServletContext servletContext = null;
056
057    protected HttpSession session = null;
058    protected boolean httpSessions = true;
059
060    public ShiroHttpServletRequest(HttpServletRequest wrapped, ServletContext servletContext, boolean httpSessions) {
061        super(wrapped);
062        this.servletContext = servletContext;
063        this.httpSessions = httpSessions;
064    }
065
066    public boolean isHttpSessions() {
067        return httpSessions;
068    }
069
070    public String getRemoteUser() {
071        String remoteUser;
072        Object scPrincipal = getSubjectPrincipal();
073        if (scPrincipal != null) {
074            if (scPrincipal instanceof String) {
075                return (String) scPrincipal;
076            } else if (scPrincipal instanceof Principal) {
077                remoteUser = ((Principal) scPrincipal).getName();
078            } else {
079                remoteUser = scPrincipal.toString();
080            }
081        } else {
082            remoteUser = super.getRemoteUser();
083        }
084        return remoteUser;
085    }
086
087    protected Subject getSubject() {
088        return SecurityUtils.getSubject();
089    }
090
091    protected Object getSubjectPrincipal() {
092        Object userPrincipal = null;
093        Subject subject = getSubject();
094        if (subject != null) {
095            userPrincipal = subject.getPrincipal();
096        }
097        return userPrincipal;
098    }
099
100    public boolean isUserInRole(String s) {
101        Subject subject = getSubject();
102        boolean inRole = (subject != null && subject.hasRole(s));
103        if (!inRole) {
104            inRole = super.isUserInRole(s);
105        }
106        return inRole;
107    }
108
109    public Principal getUserPrincipal() {
110        Principal userPrincipal;
111        Object scPrincipal = getSubjectPrincipal();
112        if (scPrincipal != null) {
113            if (scPrincipal instanceof Principal) {
114                userPrincipal = (Principal) scPrincipal;
115            } else {
116                userPrincipal = new ObjectPrincipal(scPrincipal);
117            }
118        } else {
119            userPrincipal = super.getUserPrincipal();
120        }
121        return userPrincipal;
122    }
123
124    public String getRequestedSessionId() {
125        String requestedSessionId = null;
126        if (isHttpSessions()) {
127            requestedSessionId = super.getRequestedSessionId();
128        } else {
129            Object sessionId = getAttribute(REFERENCED_SESSION_ID);
130            if (sessionId != null) {
131                requestedSessionId = sessionId.toString();
132            }
133        }
134
135        return requestedSessionId;
136    }
137
138    public HttpSession getSession(boolean create) {
139
140        HttpSession httpSession;
141
142        if (isHttpSessions()) {
143            httpSession = super.getSession(false);
144            if (httpSession == null && create) {
145                //Shiro 1.2: assert that creation is enabled (SHIRO-266):
146                if (WebUtils._isSessionCreationEnabled(this)) {
147                    httpSession = super.getSession(create);
148                } else {
149                    throw newNoSessionCreationException();
150                }
151            }
152        } else {
153            if (this.session == null) {
154
155                boolean existing = getSubject().getSession(false) != null;
156
157                Session shiroSession = getSubject().getSession(create);
158                if (shiroSession != null) {
159                    this.session = new ShiroHttpSession(shiroSession, this, this.servletContext);
160                    if (!existing) {
161                        setAttribute(REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
162                    }
163                }
164            }
165            httpSession = this.session;
166        }
167
168        return httpSession;
169    }
170
171    /**
172     * Constructs and returns a {@link DisabledSessionException} with an appropriate message explaining why
173     * session creation has been disabled.
174     *
175     * @return a new DisabledSessionException with appropriate no creation message
176     * @since 1.2
177     */
178    private DisabledSessionException newNoSessionCreationException() {
179        String msg = "Session creation has been disabled for the current request.  This exception indicates " +
180                "that there is either a programming error (using a session when it should never be " +
181                "used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +
182                "for the current request.  See the " + DisabledSessionException.class.getName() + " JavaDoc " +
183                "for more.";
184        return new DisabledSessionException(msg);
185    }
186
187    public HttpSession getSession() {
188        return getSession(true);
189    }
190
191    public boolean isRequestedSessionIdValid() {
192        if (isHttpSessions()) {
193            return super.isRequestedSessionIdValid();
194        } else {
195            Boolean value = (Boolean) getAttribute(REFERENCED_SESSION_ID_IS_VALID);
196            return (value != null && value.equals(Boolean.TRUE));
197        }
198    }
199
200    public boolean isRequestedSessionIdFromCookie() {
201        if (isHttpSessions()) {
202            return super.isRequestedSessionIdFromCookie();
203        } else {
204            String value = (String) getAttribute(REFERENCED_SESSION_ID_SOURCE);
205            return value != null && value.equals(COOKIE_SESSION_ID_SOURCE);
206        }
207    }
208
209    public boolean isRequestedSessionIdFromURL() {
210        if (isHttpSessions()) {
211            return super.isRequestedSessionIdFromURL();
212        } else {
213            String value = (String) getAttribute(REFERENCED_SESSION_ID_SOURCE);
214            return value != null && value.equals(URL_SESSION_ID_SOURCE);
215        }
216    }
217
218    public boolean isRequestedSessionIdFromUrl() {
219        return isRequestedSessionIdFromURL();
220    }
221
222    private class ObjectPrincipal implements java.security.Principal {
223        private Object object = null;
224
225        public ObjectPrincipal(Object object) {
226            this.object = object;
227        }
228
229        public Object getObject() {
230            return object;
231        }
232
233        public String getName() {
234            return getObject().toString();
235        }
236
237        public int hashCode() {
238            return object.hashCode();
239        }
240
241        public boolean equals(Object o) {
242            if (o instanceof ObjectPrincipal) {
243                ObjectPrincipal op = (ObjectPrincipal) o;
244                return getObject().equals(op.getObject());
245            }
246            return false;
247        }
248
249        public String toString() {
250            return object.toString();
251        }
252    }
253}