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.filter.authc;
020    
021    import org.apache.shiro.SecurityUtils;
022    import org.apache.shiro.session.SessionException;
023    import org.apache.shiro.subject.Subject;
024    import org.apache.shiro.web.servlet.AdviceFilter;
025    import org.apache.shiro.web.util.WebUtils;
026    import org.slf4j.Logger;
027    import org.slf4j.LoggerFactory;
028    
029    import javax.servlet.ServletRequest;
030    import javax.servlet.ServletResponse;
031    
032    /**
033     * Simple Filter that, upon receiving a request, will immediately log-out the currently executing
034     * {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) subject}
035     * and then redirect them to a configured {@link #getRedirectUrl() redirectUrl}.
036     *
037     * @since 1.2
038     */
039    public class LogoutFilter extends AdviceFilter {
040        
041        private static final Logger log = LoggerFactory.getLogger(LogoutFilter.class);
042    
043        /**
044         * The default redirect URL to where the user will be redirected after logout.  The value is {@code "/"}, Shiro's
045         * representation of the web application's context root.
046         */
047        public static final String DEFAULT_REDIRECT_URL = "/";
048    
049        /**
050         * The URL to where the user will be redirected after logout.
051         */
052        private String redirectUrl = DEFAULT_REDIRECT_URL;
053    
054        /**
055         * Acquires the currently executing {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) subject},
056         * a potentially Subject or request-specific
057         * {@link #getRedirectUrl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, org.apache.shiro.subject.Subject) redirectUrl},
058         * and redirects the end-user to that redirect url.
059         *
060         * @param request  the incoming ServletRequest
061         * @param response the outgoing ServletResponse
062         * @return {@code false} always as typically no further interaction should be done after user logout.
063         * @throws Exception if there is any error.
064         */
065        @Override
066        protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
067            Subject subject = getSubject(request, response);
068            String redirectUrl = getRedirectUrl(request, response, subject);
069            //try/catch added for SHIRO-298:
070            try {
071                subject.logout();
072            } catch (SessionException ise) {
073                log.debug("Encountered session exception during logout.  This can generally safely be ignored.", ise);
074            }
075            issueRedirect(request, response, redirectUrl);
076            return false;
077        }
078    
079        /**
080         * Returns the currently executing {@link Subject}.  This implementation merely defaults to calling
081         * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()}, but can be overridden
082         * by subclasses for different retrieval strategies.
083         *
084         * @param request  the incoming Servlet request
085         * @param response the outgoing Servlet response
086         * @return the currently executing {@link Subject}.
087         */
088        protected Subject getSubject(ServletRequest request, ServletResponse response) {
089            return SecurityUtils.getSubject();
090        }
091    
092        /**
093         * Issues an HTTP redirect to the specified URL after subject logout.  This implementation simply calls
094         * {@code WebUtils.}{@link WebUtils#issueRedirect(javax.servlet.ServletRequest, javax.servlet.ServletResponse, String) issueRedirect(request,response,redirectUrl)}.
095         *
096         * @param request  the incoming Servlet request
097         * @param response the outgoing Servlet response
098         * @param redirectUrl the URL to where the browser will be redirected immediately after Subject logout.
099         * @throws Exception if there is any error.
100         */
101        protected void issueRedirect(ServletRequest request, ServletResponse response, String redirectUrl) throws Exception {
102            WebUtils.issueRedirect(request, response, redirectUrl);
103        }
104    
105        /**
106         * Returns the redirect URL to send the user after logout.  This default implementation ignores the arguments and
107         * returns the static configured {@link #getRedirectUrl() redirectUrl} property, but this method may be overridden
108         * by subclasses to dynamically construct the URL based on the request or subject if necessary.
109         * <p/>
110         * Note: the Subject is <em>not</em> yet logged out at the time this method is invoked.  You may access the Subject's
111         * session if one is available and if necessary.
112         * <p/>
113         * Tip: if you need to access the Subject's session, consider using the
114         * {@code Subject.}{@link Subject#getSession(boolean) getSession(false)} method to ensure a new session isn't created unnecessarily.
115         * If a session would be created, it will be immediately stopped after logout, not providing any value and
116         * unnecessarily taxing session infrastructure/resources.
117         *
118         * @param request the incoming Servlet request
119         * @param response the outgoing ServletResponse
120         * @param subject the not-yet-logged-out currently executing Subject
121         * @return the redirect URL to send the user after logout.
122         */
123        protected String getRedirectUrl(ServletRequest request, ServletResponse response, Subject subject) {
124            return getRedirectUrl();
125        }
126    
127        /**
128         * Returns the URL to where the user will be redirected after logout.  Default is the web application's context
129         * root, i.e. {@code "/"}
130         *
131         * @return the URL to where the user will be redirected after logout.
132         */
133        public String getRedirectUrl() {
134            return redirectUrl;
135        }
136    
137        /**
138         * Sets the URL to where the user will be redirected after logout.  Default is the web application's context
139         * root, i.e. {@code "/"}
140         *
141         * @param redirectUrl the url to where the user will be redirected after logout
142         */
143        public void setRedirectUrl(String redirectUrl) {
144            this.redirectUrl = redirectUrl;
145        }
146    }