Shinsuke Sugaya vor 6 Jahren
Ursprung
Commit
8eadcbb48b

+ 102 - 0
src/main/java/org/codelibs/fess/filter/CorsFilter.java

@@ -0,0 +1,102 @@
+/*
+ * Copyright 2012-2019 CodeLibs Project and the Others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.codelibs.fess.filter;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.codelibs.core.lang.StringUtil;
+import org.codelibs.fess.mylasta.direction.FessConfig;
+import org.codelibs.fess.util.ComponentUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CorsFilter implements Filter {
+
+    private static final Logger logger = LoggerFactory.getLogger(CorsFilter.class);
+
+    protected static final String OPTIONS = "OPTIONS";
+
+    protected static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
+
+    protected static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
+
+    protected static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
+
+    protected static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
+
+    protected static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
+
+    protected static final String WILDCARD = "*";
+
+    @Override
+    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException,
+            ServletException {
+        final HttpServletRequest httpRequest = (HttpServletRequest) request;
+        final String origin = httpRequest.getHeader("Origin");
+        if (StringUtil.isBlank(origin)) {
+            chain.doFilter(request, response);
+            return;
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("HTTP Request: " + httpRequest.getMethod());
+        }
+
+        final FessConfig fessConfig = ComponentUtil.getFessConfig();
+
+        final String allowOrigin = getAllowOrigin(fessConfig, origin);
+        if (StringUtil.isNotBlank(allowOrigin)) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("allowOrigin: " + allowOrigin);
+            }
+            final HttpServletResponse httpResponse = (HttpServletResponse) response;
+            httpResponse.addHeader(ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigin);
+            httpResponse.addHeader(ACCESS_CONTROL_ALLOW_METHODS, fessConfig.getApiCorsAllowMethods());
+            httpResponse.addHeader(ACCESS_CONTROL_ALLOW_HEADERS, fessConfig.getApiCorsAllowHeaders());
+            httpResponse.addHeader(ACCESS_CONTROL_MAX_AGE, fessConfig.getApiCorsMaxAge());
+            httpResponse.addHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS, fessConfig.getApiCorsAllowCredentials());
+
+            if (httpRequest.getMethod().equals(OPTIONS)) {
+                httpResponse.setStatus(HttpServletResponse.SC_ACCEPTED);
+                return;
+            }
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    protected String getAllowOrigin(final FessConfig fessConfig, final String origin) {
+        final String allowOrigin = fessConfig.getApiCorsAllowOrigin();
+        if (StringUtil.isBlank(allowOrigin)) {
+            return StringUtil.EMPTY;
+        }
+
+        if (WILDCARD.equals(allowOrigin)) {
+            return allowOrigin;
+        }
+
+        return fessConfig.getApiCorsAllowOriginList().stream().filter(s -> s.equals(origin)).findFirst().orElse(StringUtil.EMPTY);
+    }
+
+}

+ 98 - 0
src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java

@@ -199,6 +199,21 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
     /** The key of the configuration. e.g.  */
     String API_DASHBOARD_RESPONSE_HEADERS = "api.dashboard.response.headers";
 
+    /** The key of the configuration. e.g. * */
+    String API_CORS_ALLOW_ORIGIN = "api.cors.allow.origin";
+
+    /** The key of the configuration. e.g. GET, POST, OPTIONS, DELETE, PUT */
+    String API_CORS_ALLOW_METHODS = "api.cors.allow.methods";
+
+    /** The key of the configuration. e.g. 3600 */
+    String API_CORS_MAX_AGE = "api.cors.max.age";
+
+    /** The key of the configuration. e.g. Origin, Content-Type, Accept, Authorization */
+    String API_CORS_ALLOW_HEADERS = "api.cors.allow.headers";
+
+    /** The key of the configuration. e.g. true */
+    String API_CORS_ALLOW_CREDENTIALS = "api.cors.allow.credentials";
+
     /** The key of the configuration. e.g.  */
     String VIRTUAL_HOST_HEADERS = "virtual.host.headers";
 
@@ -1847,6 +1862,56 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
      */
     Integer getApiDashboardResponseHeadersAsInteger();
 
+    /**
+     * Get the value for the key 'api.cors.allow.origin'. <br>
+     * The value is, e.g. * <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getApiCorsAllowOrigin();
+
+    /**
+     * Get the value for the key 'api.cors.allow.methods'. <br>
+     * The value is, e.g. GET, POST, OPTIONS, DELETE, PUT <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getApiCorsAllowMethods();
+
+    /**
+     * Get the value for the key 'api.cors.max.age'. <br>
+     * The value is, e.g. 3600 <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getApiCorsMaxAge();
+
+    /**
+     * Get the value for the key 'api.cors.max.age' as {@link Integer}. <br>
+     * The value is, e.g. 3600 <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     * @throws NumberFormatException When the property is not integer.
+     */
+    Integer getApiCorsMaxAgeAsInteger();
+
+    /**
+     * Get the value for the key 'api.cors.allow.headers'. <br>
+     * The value is, e.g. Origin, Content-Type, Accept, Authorization <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getApiCorsAllowHeaders();
+
+    /**
+     * Get the value for the key 'api.cors.allow.credentials'. <br>
+     * The value is, e.g. true <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getApiCorsAllowCredentials();
+
+    /**
+     * Is the property for the key 'api.cors.allow.credentials' true? <br>
+     * The value is, e.g. true <br>
+     * @return The determination, true or false. (if not found, exception but basically no way)
+     */
+    boolean isApiCorsAllowCredentials();
+
     /**
      * Get the value for the key 'virtual.host.headers'. <br>
      * The value is, e.g.  <br>
@@ -6202,6 +6267,34 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
             return getAsInteger(FessConfig.API_DASHBOARD_RESPONSE_HEADERS);
         }
 
+        public String getApiCorsAllowOrigin() {
+            return get(FessConfig.API_CORS_ALLOW_ORIGIN);
+        }
+
+        public String getApiCorsAllowMethods() {
+            return get(FessConfig.API_CORS_ALLOW_METHODS);
+        }
+
+        public String getApiCorsMaxAge() {
+            return get(FessConfig.API_CORS_MAX_AGE);
+        }
+
+        public Integer getApiCorsMaxAgeAsInteger() {
+            return getAsInteger(FessConfig.API_CORS_MAX_AGE);
+        }
+
+        public String getApiCorsAllowHeaders() {
+            return get(FessConfig.API_CORS_ALLOW_HEADERS);
+        }
+
+        public String getApiCorsAllowCredentials() {
+            return get(FessConfig.API_CORS_ALLOW_CREDENTIALS);
+        }
+
+        public boolean isApiCorsAllowCredentials() {
+            return is(FessConfig.API_CORS_ALLOW_CREDENTIALS);
+        }
+
         public String getVirtualHostHeaders() {
             return get(FessConfig.VIRTUAL_HOST_HEADERS);
         }
@@ -8491,6 +8584,11 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
             defaultMap.put(FessConfig.API_JSON_RESPONSE_HEADERS, "");
             defaultMap.put(FessConfig.API_GSA_RESPONSE_HEADERS, "");
             defaultMap.put(FessConfig.API_DASHBOARD_RESPONSE_HEADERS, "");
+            defaultMap.put(FessConfig.API_CORS_ALLOW_ORIGIN, "*");
+            defaultMap.put(FessConfig.API_CORS_ALLOW_METHODS, "GET, POST, OPTIONS, DELETE, PUT");
+            defaultMap.put(FessConfig.API_CORS_MAX_AGE, "3600");
+            defaultMap.put(FessConfig.API_CORS_ALLOW_HEADERS, "Origin, Content-Type, Accept, Authorization");
+            defaultMap.put(FessConfig.API_CORS_ALLOW_CREDENTIALS, "true");
             defaultMap.put(FessConfig.VIRTUAL_HOST_HEADERS, "");
             defaultMap.put(FessConfig.HTTP_PROXY_HOST, "");
             defaultMap.put(FessConfig.HTTP_PROXY_PORT, "8080");

+ 15 - 0
src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java

@@ -70,6 +70,8 @@ import org.lastaflute.web.validation.theme.typed.LongTypeValidator;
 
 public interface FessProp {
 
+    String CORS_ALLOW_ORIGIN = "CorsAllowOrigin";
+
     String API_DASHBOARD_RESPONSE_HEADER_LIST = "apiDashboardResponseHeaderList";
 
     String API_JSON_RESPONSE_HEADER_LIST = "apiJsonResponseHeaderList";
@@ -1969,4 +1971,17 @@ public interface FessProp {
         }
         return list;
     }
+
+    String getApiCorsAllowOrigin();
+
+    default List<String> getApiCorsAllowOriginList() {
+        List<String> list = (List<String>) propMap.get(CORS_ALLOW_ORIGIN);
+        if (list == null) {
+            list =
+                    split(getApiCorsAllowOrigin(), "\n").get(
+                            stream -> stream.map(String::trim).filter(StringUtil::isNotEmpty).collect(Collectors.toList()));
+            propMap.put(CORS_ALLOW_ORIGIN, list);
+        }
+        return list;
+    }
 }

+ 5 - 0
src/main/resources/fess_config.properties

@@ -136,6 +136,11 @@ api.search.scroll=false
 api.json.response.headers=
 api.gsa.response.headers=
 api.dashboard.response.headers=
+api.cors.allow.origin=*
+api.cors.allow.methods=GET, POST, OPTIONS, DELETE, PUT
+api.cors.max.age=3600
+api.cors.allow.headers=Origin, Content-Type, Accept, Authorization
+api.cors.allow.credentials=true
 
 # Virtual Host: Host:fess.codelibs.org=fess
 virtual.host.headers=

+ 11 - 0
src/main/webapp/WEB-INF/web.xml

@@ -35,6 +35,11 @@
     </init-param>
   </filter>
 
+  <filter>
+    <filter-name>corsFilter</filter-name>
+    <filter-class>org.codelibs.fess.filter.CorsFilter</filter-class>
+  </filter>
+
   <filter>
     <filter-name>webApiFilter</filter-name>
     <filter-class>org.codelibs.fess.filter.WebApiFilter</filter-class>
@@ -77,6 +82,12 @@
     <dispatcher>INCLUDE</dispatcher>
   </filter-mapping>
 
+  <filter-mapping>
+    <filter-name>corsFilter</filter-name>
+    <url-pattern>/*</url-pattern>
+    <dispatcher>REQUEST</dispatcher>
+  </filter-mapping>
+
   <filter-mapping>
     <filter-name>webApiFilter</filter-name>
     <url-pattern>/*</url-pattern>