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