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