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