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