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