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     */
019    package org.apache.shiro.web.mgt;
020    
021    import org.apache.shiro.mgt.DefaultSecurityManager;
022    import org.apache.shiro.mgt.DefaultSubjectDAO;
023    import org.apache.shiro.mgt.SessionStorageEvaluator;
024    import org.apache.shiro.mgt.SubjectDAO;
025    import org.apache.shiro.realm.Realm;
026    import org.apache.shiro.session.mgt.SessionContext;
027    import org.apache.shiro.session.mgt.SessionKey;
028    import org.apache.shiro.session.mgt.SessionManager;
029    import org.apache.shiro.subject.Subject;
030    import org.apache.shiro.subject.SubjectContext;
031    import org.apache.shiro.util.LifecycleUtils;
032    import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
033    import org.apache.shiro.web.session.mgt.*;
034    import org.apache.shiro.web.subject.WebSubject;
035    import org.apache.shiro.web.subject.WebSubjectContext;
036    import org.apache.shiro.web.subject.support.DefaultWebSubjectContext;
037    import org.apache.shiro.web.util.WebUtils;
038    import org.slf4j.Logger;
039    import org.slf4j.LoggerFactory;
040    
041    import javax.servlet.ServletRequest;
042    import javax.servlet.ServletResponse;
043    import java.io.Serializable;
044    import java.util.Collection;
045    
046    
047    /**
048     * Default {@link WebSecurityManager WebSecurityManager} implementation used in web-based applications or any
049     * application that requires HTTP connectivity (SOAP, http remoting, etc).
050     *
051     * @since 0.2
052     */
053    public class DefaultWebSecurityManager extends DefaultSecurityManager implements WebSecurityManager {
054    
055        //TODO - complete JavaDoc
056    
057        private static final Logger log = LoggerFactory.getLogger(DefaultWebSecurityManager.class);
058    
059        @Deprecated
060        public static final String HTTP_SESSION_MODE = "http";
061        @Deprecated
062        public static final String NATIVE_SESSION_MODE = "native";
063    
064        /**
065         * @deprecated as of 1.2.  This should NOT be used for anything other than determining if the sessionMode has changed.
066         */
067        @Deprecated
068        private String sessionMode;
069    
070        public DefaultWebSecurityManager() {
071            super();
072            ((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
073            this.sessionMode = HTTP_SESSION_MODE;
074            setSubjectFactory(new DefaultWebSubjectFactory());
075            setRememberMeManager(new CookieRememberMeManager());
076            setSessionManager(new ServletContainerSessionManager());
077        }
078    
079        @SuppressWarnings({"UnusedDeclaration"})
080        public DefaultWebSecurityManager(Realm singleRealm) {
081            this();
082            setRealm(singleRealm);
083        }
084    
085        @SuppressWarnings({"UnusedDeclaration"})
086        public DefaultWebSecurityManager(Collection<Realm> realms) {
087            this();
088            setRealms(realms);
089        }
090    
091        @Override
092        protected SubjectContext createSubjectContext() {
093            return new DefaultWebSubjectContext();
094        }
095    
096        @Override
097        //since 1.2.1 for fixing SHIRO-350
098        public void setSubjectDAO(SubjectDAO subjectDAO) {
099            super.setSubjectDAO(subjectDAO);
100            applySessionManagerToSessionStorageEvaluatorIfPossible();
101        }
102    
103        //since 1.2.1 for fixing SHIRO-350
104        @Override
105        protected void afterSessionManagerSet() {
106            super.afterSessionManagerSet();
107            applySessionManagerToSessionStorageEvaluatorIfPossible();
108        }
109    
110        //since 1.2.1 for fixing SHIRO-350:
111        private void applySessionManagerToSessionStorageEvaluatorIfPossible() {
112            SubjectDAO subjectDAO = getSubjectDAO();
113            if (subjectDAO instanceof DefaultSubjectDAO) {
114                SessionStorageEvaluator evaluator = ((DefaultSubjectDAO)subjectDAO).getSessionStorageEvaluator();
115                if (evaluator instanceof DefaultWebSessionStorageEvaluator) {
116                    ((DefaultWebSessionStorageEvaluator)evaluator).setSessionManager(getSessionManager());
117                }
118            }
119        }
120    
121        @Override
122        protected SubjectContext copy(SubjectContext subjectContext) {
123            if (subjectContext instanceof WebSubjectContext) {
124                return new DefaultWebSubjectContext((WebSubjectContext) subjectContext);
125            }
126            return super.copy(subjectContext);
127        }
128    
129        @SuppressWarnings({"UnusedDeclaration"})
130        @Deprecated
131        public String getSessionMode() {
132            return sessionMode;
133        }
134    
135        /**
136         * @param sessionMode
137         * @deprecated since 1.2
138         */
139        @Deprecated
140        public void setSessionMode(String sessionMode) {
141            log.warn("The 'sessionMode' property has been deprecated.  Please configure an appropriate WebSessionManager " +
142                    "instance instead of using this property.  This property/method will be removed in a later version.");
143            String mode = sessionMode;
144            if (mode == null) {
145                throw new IllegalArgumentException("sessionMode argument cannot be null.");
146            }
147            mode = sessionMode.toLowerCase();
148            if (!HTTP_SESSION_MODE.equals(mode) && !NATIVE_SESSION_MODE.equals(mode)) {
149                String msg = "Invalid sessionMode [" + sessionMode + "].  Allowed values are " +
150                        "public static final String constants in the " + getClass().getName() + " class: '"
151                        + HTTP_SESSION_MODE + "' or '" + NATIVE_SESSION_MODE + "', with '" +
152                        HTTP_SESSION_MODE + "' being the default.";
153                throw new IllegalArgumentException(msg);
154            }
155            boolean recreate = this.sessionMode == null || !this.sessionMode.equals(mode);
156            this.sessionMode = mode;
157            if (recreate) {
158                LifecycleUtils.destroy(getSessionManager());
159                SessionManager sessionManager = createSessionManager(mode);
160                this.setInternalSessionManager(sessionManager);
161            }
162        }
163    
164        @Override
165        public void setSessionManager(SessionManager sessionManager) {
166            this.sessionMode = null;
167            if (sessionManager != null && !(sessionManager instanceof WebSessionManager)) {
168                if (log.isWarnEnabled()) {
169                    String msg = "The " + getClass().getName() + " implementation expects SessionManager instances " +
170                            "that implement the " + WebSessionManager.class.getName() + " interface.  The " +
171                            "configured instance is of type [" + sessionManager.getClass().getName() + "] which does not " +
172                            "implement this interface..  This may cause unexpected behavior.";
173                    log.warn(msg);
174                }
175            }
176            setInternalSessionManager(sessionManager);
177        }
178    
179        /**
180         * @param sessionManager
181         * @since 1.2
182         */
183        private void setInternalSessionManager(SessionManager sessionManager) {
184            super.setSessionManager(sessionManager);
185        }
186    
187        /**
188         * @since 1.0
189         */
190        public boolean isHttpSessionMode() {
191            SessionManager sessionManager = getSessionManager();
192            return sessionManager instanceof WebSessionManager && ((WebSessionManager)sessionManager).isServletContainerSessions();
193        }
194    
195        protected SessionManager createSessionManager(String sessionMode) {
196            if (sessionMode == null || !sessionMode.equalsIgnoreCase(NATIVE_SESSION_MODE)) {
197                log.info("{} mode - enabling ServletContainerSessionManager (HTTP-only Sessions)", HTTP_SESSION_MODE);
198                return new ServletContainerSessionManager();
199            } else {
200                log.info("{} mode - enabling DefaultWebSessionManager (non-HTTP and HTTP Sessions)", NATIVE_SESSION_MODE);
201                return new DefaultWebSessionManager();
202            }
203        }
204    
205        @Override
206        protected SessionContext createSessionContext(SubjectContext subjectContext) {
207            SessionContext sessionContext = super.createSessionContext(subjectContext);
208            if (subjectContext instanceof WebSubjectContext) {
209                WebSubjectContext wsc = (WebSubjectContext) subjectContext;
210                ServletRequest request = wsc.resolveServletRequest();
211                ServletResponse response = wsc.resolveServletResponse();
212                DefaultWebSessionContext webSessionContext = new DefaultWebSessionContext(sessionContext);
213                if (request != null) {
214                    webSessionContext.setServletRequest(request);
215                }
216                if (response != null) {
217                    webSessionContext.setServletResponse(response);
218                }
219    
220                sessionContext = webSessionContext;
221            }
222            return sessionContext;
223        }
224    
225        @Override
226        protected SessionKey getSessionKey(SubjectContext context) {
227            if (WebUtils.isWeb(context)) {
228                Serializable sessionId = context.getSessionId();
229                ServletRequest request = WebUtils.getRequest(context);
230                ServletResponse response = WebUtils.getResponse(context);
231                return new WebSessionKey(sessionId, request, response);
232            } else {
233                return super.getSessionKey(context);
234    
235            }
236        }
237    
238        @Override
239        protected void beforeLogout(Subject subject) {
240            super.beforeLogout(subject);
241            removeRequestIdentity(subject);
242        }
243    
244        protected void removeRequestIdentity(Subject subject) {
245            if (subject instanceof WebSubject) {
246                WebSubject webSubject = (WebSubject) subject;
247                ServletRequest request = webSubject.getServletRequest();
248                if (request != null) {
249                    request.setAttribute(ShiroHttpServletRequest.IDENTITY_REMOVED_KEY, Boolean.TRUE);
250                }
251            }
252        }
253    }