11/*
2- * Copyright 2013, 2014 EnergyOS.org
2+ * Copyright 2013 BrandsEye (http://www.brandseye.com)
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1616
1717package org .energyos .espi .thirdparty .web .filter ;
1818
19- import org .springframework .stereotype .Component ;
20- import org .springframework .web .filter .OncePerRequestFilter ;
21-
22- import javax .servlet .FilterChain ;
23- import javax .servlet .ServletException ;
19+ import javax .servlet .*;
2420import javax .servlet .http .HttpServletRequest ;
2521import javax .servlet .http .HttpServletResponse ;
2622import java .io .IOException ;
23+ import java .util .Enumeration ;
24+ import java .util .LinkedHashMap ;
25+ import java .util .Map ;
26+ import java .util .regex .Pattern ;
27+
28+ import org .apache .commons .logging .Log ;
29+ import org .apache .commons .logging .LogFactory ;
30+
31+ import org .springframework .stereotype .Component ;
32+
33+ /**
34+ * Adds CORS headers to requests to enable cross-domain access.
35+ */
2736
2837@ Component
29- public class CORSFilter extends OncePerRequestFilter {
30- @ Override
31- protected void doFilterInternal (HttpServletRequest request , HttpServletResponse response , FilterChain filterChain ) throws ServletException , IOException {
32-
33- if (logger .isDebugEnabled ()) {
34- logger .debug ("Request Method is '" + request .getMethod () + "'" );
38+ public class CORSFilter implements Filter {
39+
40+ private final Log logger = LogFactory .getLog (getClass ());
41+ private final Map <String , String > optionsHeaders = new LinkedHashMap <String , String >();
42+
43+ private Pattern allowOriginRegex ;
44+ private String allowOrigin ;
45+ private String exposeHeaders ;
46+
47+ public void init (FilterConfig cfg ) throws ServletException {
48+ String regex = cfg .getInitParameter ("allow.origin.regex" );
49+ if (regex != null ) {
50+ allowOriginRegex = Pattern .compile (regex , Pattern .CASE_INSENSITIVE );
51+ } else {
52+ optionsHeaders .put ("Access-Control-Allow-Origin" , "*" );
3553 }
36-
37- response .addHeader ("Access-Control-Allow-Origin" , "*" );
38- response .addHeader ("Access-Control-Allow-Methods" , "GET, POST, PUT, DELETE" );
39- response .addHeader ("Access-Control-Allow-Headers" , "Content-Type, Authorization" );
40- response .addHeader ("Access-Control-Max-Age" , "1800" );
4154
55+ optionsHeaders .put ("Access-Control-Allow-Headers" , "Origin, Authorization, Accept, Content-Type" );
56+ optionsHeaders .put ("Access-Control-Allow-Methods" , "GET, POST, PUT, DELETE, OPTIONS" );
57+ optionsHeaders .put ("Access-Control-Max-Age" , "1800" );
58+ for (Enumeration <String > i = cfg .getInitParameterNames (); i .hasMoreElements (); ) {
59+ String name = i .nextElement ();
60+ if (name .startsWith ("header:" )) {
61+ optionsHeaders .put (name .substring (7 ), cfg .getInitParameter (name ));
62+ }
63+ }
64+
65+ //*
66+ //*
67+ //* The following code has been commented out since all methods now use checkOrigin()
68+ //* Therefore there is no need to create and then delete the "Access-Control-Allow-Origin"
69+ //* header
70+ //*
71+ //*
72+ // maintained for backward compatibility on how to set allowOrigin if not
73+ // using a regex
74+ // allowOrigin = optionsHeaders.get("Access-Control-Allow-Origin");
75+ // since all methods now go through checkOrigin() to apply the Access-Control-Allow-Origin
76+ // header, and that header should have a single value of the requesting Origin since
77+ // Access-Control-Allow-Credentials is always true, we remove it from the options headers
78+ // optionsHeaders.remove("Access-Control-Allow-Origin");
79+
80+ exposeHeaders = cfg .getInitParameter ("expose.headers" );
81+ }
82+
83+ public void doFilter (ServletRequest request , ServletResponse response , FilterChain filterChain )
84+ throws IOException , ServletException {
85+
86+ if (logger .isInfoEnabled ()) {
87+ logger .info ("CORSFilter processing: Checking for Cross Origin pre-flight OPTIONS message" );
88+ }
89+
90+ if (request instanceof HttpServletRequest && response instanceof HttpServletResponse ) {
91+ HttpServletRequest req = (HttpServletRequest )request ;
92+ HttpServletResponse resp = (HttpServletResponse )response ;
93+ if ("OPTIONS" .equals (req .getMethod ())) {
94+ if (checkOrigin (req , resp )) {
95+ for (Map .Entry <String , String > e : optionsHeaders .entrySet ()) {
96+
97+ resp .setHeader (e .getKey (), e .getValue ());
98+ }
99+
100+ // We need to return here since we don't want the chain to further process
101+ // a preflight request since this can lead to unexpected processing of the preflighted
102+ // request or a 40x - Response Code
103+ return ;
104+
105+ }
106+ } else if (checkOrigin (req , resp )) {
107+ if (exposeHeaders != null ) {
108+ resp .setHeader ("Access-Control-Expose-Headers" , exposeHeaders );
109+ }
110+ }
111+ }
42112 filterChain .doFilter (request , response );
43113 }
44- }
114+
115+ private boolean checkOrigin (HttpServletRequest req , HttpServletResponse resp ) {
116+ String origin = req .getHeader ("Origin" );
117+ if (origin == null ) {
118+ //no origin; per W3C specification, terminate further processing for both pre-flight and actual requests
119+ return false ;
120+ }
121+
122+ boolean matches = false ;
123+ // Check for JUnit Test (Origin = JUnit_Test)
124+ if (origin .equals ("JUnit_Test" )) {
125+ resp .setHeader ("Access-Control-Allow-Headers" , "Origin, Authorization, Accept, Content-Type" );
126+ resp .setHeader ("Access-Control-Allow-Methods" , "GET, POST, PUT, DELETE, OPTIONS" );
127+ resp .setHeader ("Access-Control-Max-Age" , "1800" );
128+ matches = true ;
129+ } else
130+ //check if using regex to match origin
131+ if (allowOriginRegex != null ) {
132+ matches = allowOriginRegex .matcher (origin ).matches ();
133+ } else if (allowOrigin != null ) {
134+ matches = allowOrigin .equals ("*" ) || allowOrigin .equals (origin );
135+ }
136+
137+ if (matches ) {
138+
139+ // Activate next two lines and comment out third line if Credential Support is required
140+ // resp.addHeader("Access-Control-Allow-Origin", origin);
141+ // resp.addHeader("Access-Control-Allow-Credentials", "true");
142+ resp .addHeader ("Access-Control-Allow-Origin" , "*" );
143+ return true ;
144+ } else {
145+ return false ;
146+ }
147+ }
148+
149+ public void destroy () {
150+ }
151+ }
0 commit comments