001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.servlet.filters.invoker;
016    
017    import com.liferay.portal.kernel.cache.key.CacheKeyGenerator;
018    import com.liferay.portal.kernel.cache.key.CacheKeyGeneratorUtil;
019    import com.liferay.portal.kernel.concurrent.ConcurrentLFUCache;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.servlet.HttpOnlyCookieServletResponse;
023    import com.liferay.portal.kernel.servlet.NonSerializableObjectRequestWrapper;
024    import com.liferay.portal.kernel.servlet.SanitizedServletResponse;
025    import com.liferay.portal.kernel.util.BasePortalLifecycle;
026    import com.liferay.portal.kernel.util.GetterUtil;
027    import com.liferay.portal.kernel.util.HttpUtil;
028    import com.liferay.portal.kernel.util.JavaConstants;
029    import com.liferay.portal.kernel.util.PropsKeys;
030    import com.liferay.portal.kernel.util.PropsUtil;
031    import com.liferay.portal.kernel.util.ServerDetector;
032    import com.liferay.portal.kernel.util.StringBundler;
033    import com.liferay.portal.kernel.util.StringPool;
034    import com.liferay.portal.kernel.util.StringUtil;
035    import com.liferay.portal.kernel.util.Validator;
036    import com.liferay.portal.kernel.util.WebKeys;
037    
038    import java.io.IOException;
039    
040    import javax.servlet.Filter;
041    import javax.servlet.FilterChain;
042    import javax.servlet.FilterConfig;
043    import javax.servlet.ServletContext;
044    import javax.servlet.ServletException;
045    import javax.servlet.ServletRequest;
046    import javax.servlet.ServletResponse;
047    import javax.servlet.http.HttpServletRequest;
048    import javax.servlet.http.HttpServletResponse;
049    
050    /**
051     * @author Mika Koivisto
052     * @author Brian Wing Shun Chan
053     * @author Shuyang Zhou
054     */
055    public class InvokerFilter extends BasePortalLifecycle implements Filter {
056    
057            @Override
058            public void destroy() {
059                    portalDestroy();
060            }
061    
062            @Override
063            public void doFilter(
064                            ServletRequest servletRequest, ServletResponse servletResponse,
065                            FilterChain filterChain)
066                    throws IOException, ServletException {
067    
068                    HttpServletRequest request = (HttpServletRequest)servletRequest;
069    
070                    String uri = getURI(request);
071    
072                    HttpServletResponse response = (HttpServletResponse)servletResponse;
073    
074                    String requestURL = getURL(request);
075    
076                    if (requestURL.length() > _invokerFilterURIMaxLength) {
077                            response.sendError(HttpServletResponse.SC_REQUEST_URI_TOO_LONG);
078    
079                            if (_log.isWarnEnabled()) {
080                                    StringBundler sb = new StringBundler(5);
081    
082                                    sb.append("Rejected ");
083                                    sb.append(StringUtil.shorten(uri, _invokerFilterURIMaxLength));
084                                    sb.append(" because it has more than ");
085                                    sb.append(_invokerFilterURIMaxLength);
086                                    sb.append(" characters");
087    
088                                    _log.warn(sb.toString());
089                            }
090    
091                            return;
092                    }
093    
094                    request = handleNonSerializableRequest(request);
095    
096                    response =
097                            HttpOnlyCookieServletResponse.getHttpOnlyCookieServletResponse(
098                                    response);
099    
100                    response = secureResponseHeaders(request, response);
101    
102                    request.setAttribute(WebKeys.INVOKER_FILTER_URI, uri);
103    
104                    try {
105                            InvokerFilterChain invokerFilterChain = getInvokerFilterChain(
106                                    request, uri, filterChain);
107    
108                            Thread currentThread = Thread.currentThread();
109    
110                            ClassLoader contextClassLoader =
111                                    currentThread.getContextClassLoader();
112    
113                            invokerFilterChain.setContextClassLoader(contextClassLoader);
114    
115                            invokerFilterChain.doFilter(request, response);
116                    }
117                    finally {
118                            request.removeAttribute(WebKeys.INVOKER_FILTER_URI);
119                    }
120            }
121    
122            @Override
123            public void init(FilterConfig filterConfig) throws ServletException {
124                    _filterConfig = filterConfig;
125    
126                    ServletContext servletContext = _filterConfig.getServletContext();
127    
128                    _contextPath = servletContext.getContextPath();
129    
130                    boolean registerPortalLifecycle = GetterUtil.getBoolean(
131                            _filterConfig.getInitParameter("register-portal-lifecycle"), true);
132    
133                    if (registerPortalLifecycle) {
134                            registerPortalLifecycle();
135                    }
136                    else {
137                            try {
138                                    doPortalInit();
139                            }
140                            catch (Exception e) {
141                                    _log.error(e, e);
142    
143                                    throw new ServletException(e);
144                            }
145                    }
146            }
147    
148            protected void clearFilterChainsCache() {
149                    if (_filterChains != null) {
150                            _filterChains.clear();
151                    }
152            }
153    
154            @Override
155            protected void doPortalDestroy() {
156                    ServletContext servletContext = _filterConfig.getServletContext();
157    
158                    InvokerFilterHelper invokerFilterHelper =
159                            (InvokerFilterHelper)servletContext.getAttribute(
160                                    InvokerFilterHelper.class.getName());
161    
162                    if (invokerFilterHelper != null) {
163                            servletContext.removeAttribute(InvokerFilterHelper.class.getName());
164    
165                            invokerFilterHelper.destroy();
166                    }
167            }
168    
169            @Override
170            protected void doPortalInit() throws Exception {
171                    _invokerFilterChainSize = GetterUtil.getInteger(
172                            PropsUtil.get(PropsKeys.INVOKER_FILTER_CHAIN_SIZE));
173    
174                    if (_invokerFilterChainSize > 0) {
175                            _filterChains = new ConcurrentLFUCache<>(_invokerFilterChainSize);
176                    }
177    
178                    _invokerFilterURIMaxLength = GetterUtil.getInteger(
179                            PropsUtil.get(PropsKeys.INVOKER_FILTER_URI_MAX_LENGTH));
180    
181                    ServletContext servletContext = _filterConfig.getServletContext();
182    
183                    InvokerFilterHelper invokerFilterHelper =
184                            (InvokerFilterHelper)servletContext.getAttribute(
185                                    InvokerFilterHelper.class.getName());
186    
187                    if (invokerFilterHelper == null) {
188                            invokerFilterHelper = new InvokerFilterHelper();
189    
190                            servletContext.setAttribute(
191                                    InvokerFilterHelper.class.getName(), invokerFilterHelper);
192    
193                            invokerFilterHelper.init(_filterConfig);
194                    }
195    
196                    _invokerFilterHelper = invokerFilterHelper;
197    
198                    _invokerFilterHelper.addInvokerFilter(this);
199    
200                    String dispatcher = GetterUtil.getString(
201                            _filterConfig.getInitParameter("dispatcher"));
202    
203                    if (dispatcher.equals("ERROR")) {
204                            _dispatcher = Dispatcher.ERROR;
205                    }
206                    else if (dispatcher.equals("FORWARD")) {
207                            _dispatcher = Dispatcher.FORWARD;
208                    }
209                    else if (dispatcher.equals("INCLUDE")) {
210                            _dispatcher = Dispatcher.INCLUDE;
211                    }
212                    else if (dispatcher.equals("REQUEST")) {
213                            _dispatcher = Dispatcher.REQUEST;
214                    }
215                    else {
216                            throw new IllegalArgumentException(
217                                    "Invalid dispatcher " + dispatcher);
218                    }
219            }
220    
221            protected InvokerFilterChain getInvokerFilterChain(
222                    HttpServletRequest request, String uri, FilterChain filterChain) {
223    
224                    if (_filterChains == null) {
225                            return _invokerFilterHelper.createInvokerFilterChain(
226                                    request, _dispatcher, uri, filterChain);
227                    }
228    
229                    CacheKeyGenerator cacheKeyGenerator =
230                            CacheKeyGeneratorUtil.getCacheKeyGenerator(
231                                    InvokerFilter.class.getName());
232    
233                    String key = String.valueOf(cacheKeyGenerator.getCacheKey(uri));
234    
235                    InvokerFilterChain invokerFilterChain = _filterChains.get(key);
236    
237                    if (invokerFilterChain == null) {
238                            invokerFilterChain = _invokerFilterHelper.createInvokerFilterChain(
239                                    request, _dispatcher, uri, filterChain);
240    
241                            _filterChains.put(key, invokerFilterChain);
242                    }
243    
244                    return invokerFilterChain.clone(filterChain);
245            }
246    
247            protected String getURI(HttpServletRequest request) {
248                    String uri = null;
249    
250                    if (_dispatcher == Dispatcher.ERROR) {
251                            uri = (String)request.getAttribute(
252                                    JavaConstants.JAVAX_SERVLET_ERROR_REQUEST_URI);
253                    }
254                    else if (_dispatcher == Dispatcher.INCLUDE) {
255                            uri = (String)request.getAttribute(
256                                    JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
257                    }
258                    else {
259                            uri = request.getRequestURI();
260                    }
261    
262                    if (Validator.isNotNull(_contextPath) &&
263                            !_contextPath.equals(StringPool.SLASH) &&
264                            uri.startsWith(_contextPath)) {
265    
266                            uri = uri.substring(_contextPath.length());
267                    }
268    
269                    return HttpUtil.normalizePath(uri);
270            }
271    
272            protected String getURL(HttpServletRequest request) {
273                    StringBuffer requestURL = request.getRequestURL();
274    
275                    if (requestURL == null) {
276                            return StringPool.BLANK;
277                    }
278    
279                    String queryString = request.getQueryString();
280    
281                    if (!Validator.isBlank(queryString)) {
282                            requestURL.append(StringPool.QUESTION);
283                            requestURL.append(request.getQueryString());
284                    }
285    
286                    return requestURL.toString();
287            }
288    
289            protected HttpServletRequest handleNonSerializableRequest(
290                    HttpServletRequest request) {
291    
292                    if (ServerDetector.isWebLogic()) {
293                            if (!NonSerializableObjectRequestWrapper.isWrapped(request)) {
294                                    request = new NonSerializableObjectRequestWrapper(request);
295                            }
296                    }
297    
298                    return request;
299            }
300    
301            protected HttpServletResponse secureResponseHeaders(
302                    HttpServletRequest request, HttpServletResponse response) {
303    
304                    if (!GetterUtil.getBoolean(
305                                    request.getAttribute(_SECURE_RESPONSE), true)) {
306    
307                            return response;
308                    }
309    
310                    request.setAttribute(_SECURE_RESPONSE, Boolean.FALSE);
311    
312                    return SanitizedServletResponse.getSanitizedServletResponse(
313                            request, response);
314            }
315    
316            private static final String _SECURE_RESPONSE =
317                    InvokerFilter.class.getName() + "SECURE_RESPONSE";
318    
319            private static final Log _log = LogFactoryUtil.getLog(InvokerFilter.class);
320    
321            private String _contextPath;
322            private Dispatcher _dispatcher;
323            private ConcurrentLFUCache<String, InvokerFilterChain> _filterChains;
324            private FilterConfig _filterConfig;
325            private int _invokerFilterChainSize;
326            private InvokerFilterHelper _invokerFilterHelper;
327            private int _invokerFilterURIMaxLength;
328    
329    }