Created
September 27, 2018 15:49
-
-
Save mhagnumdw/741f71d422e59251ddcde6f404603f42 to your computer and use it in GitHub Desktop.
CorsHandler for Pippo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.HashSet; | |
import java.util.Set; | |
import java.util.stream.Collectors; | |
import org.apache.commons.lang3.StringUtils; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import ro.pippo.core.route.RouteContext; | |
import ro.pippo.core.route.RouteHandler; | |
/** | |
* Define how CORS requests are handled. | |
* | |
* <p>The Cross-Origin Resource Sharing standard works by adding new HTTP headers | |
* that allow servers to describe the set of origins that are permitted to read | |
* that information using a web browser.</p> | |
* | |
* <p>For more details see: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS</p> | |
* | |
* <p>Based on: https://github.com/pac4j/pac4j/blob/3806174df54b939ed2785ee493f63b9851fcd03e/pac4j-core/src/main/java/org/pac4j/core/authorization/authorizer/CorsAuthorizer.java</p> | |
*/ | |
public class CorsHandler implements RouteHandler<RouteContext> { | |
private static final Logger log = LoggerFactory.getLogger(CorsHandler.class); | |
private String allowOrigin; | |
private String exposeHeaders; | |
private int maxAge = -1; | |
private Boolean allowCredentials; | |
private String allowMethods; | |
private String allowHeaders; | |
public static Builder builder() { | |
return new Builder(); | |
} | |
private CorsHandler(Builder builder) { | |
this.allowOrigin = builder.allowOrigins.stream().collect(Collectors.joining(", ")); | |
if (StringUtils.isBlank(this.allowOrigin)) { | |
throw new RuntimeException("allowOrigin cannot be blank"); | |
} | |
this.exposeHeaders = builder.exposeHeaders.stream().collect(Collectors.joining(", ")); | |
this.maxAge = builder.maxAge; | |
this.allowCredentials = builder.allowCredentials; | |
this.allowMethods = builder.allowMethods.stream().collect(Collectors.joining(", ")); | |
this.allowHeaders = builder.allowHeaders.stream().collect(Collectors.joining(", ")); | |
log.info(String.format("CorsHandler [allowOrigin=%s, allowMethods=%s, allowHeaders=%s, exposeHeaders=%s, maxAge=%s, allowCredentials=%s]", allowOrigin, allowMethods, allowHeaders, | |
exposeHeaders, maxAge, allowCredentials)); | |
} | |
// TODO: submitted pull request to Pippo to create the constants below. | |
// Change the Strings of the method below by the constants when possible: | |
// See: https://github.com/pippo-java/pippo/pull/452 | |
// 1 | |
// attribute: ro.pippo.core.HttpConstants.Header.ACCESS_CONTROL_ALLOW_ORIGIN | |
// value: "Access-Control-Allow-Origin" | |
// 2 | |
// attribute: ro.pippo.core.HttpConstants.Header.ACCESS_CONTROL_EXPOSE_HEADERS | |
// value: "Access-Control-Expose-Headers" | |
// 3 | |
// attribute: ro.pippo.core.HttpConstants.Header.ACCESS_CONTROL_MAX_AGE | |
// value: "Access-Control-Max-Age" | |
// 4 | |
// attribute: ro.pippo.core.HttpConstants.Header.ACCESS_CONTROL_ALLOW_CREDENTIALS | |
// value: "Access-Control-Allow-Credentials" | |
// 5 | |
// attribute: ro.pippo.core.HttpConstants.Header.ACCESS_CONTROL_ALLOW_METHODS | |
// value: "Access-Control-Allow-Methods" | |
// 6 | |
// attribute: ro.pippo.core.HttpConstants.Header.ACCESS_CONTROL_ALLOW_HEADERS | |
// value: "Access-Control-Allow-Headers" | |
@Override | |
public void handle(RouteContext context) { | |
context.getResponse().header("Access-Control-Allow-Origin", allowOrigin); | |
if (StringUtils.isNotBlank(exposeHeaders)) { | |
context.getResponse().header("Access-Control-Expose-Headers", exposeHeaders); | |
} | |
if (maxAge != -1) { | |
context.getResponse().header("Access-Control-Max-Age", "" + maxAge); | |
} | |
// According to the documentation only if true is what needs to be set | |
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials#Directives | |
if (allowCredentials != null && allowCredentials) { | |
context.getResponse().header("Access-Control-Allow-Credentials", allowCredentials.toString()); | |
} | |
if (allowMethods != null) { | |
context.getResponse().header("Access-Control-Allow-Methods", allowMethods); | |
} | |
if (allowHeaders != null) { | |
context.getResponse().header("Access-Control-Allow-Headers", allowHeaders); | |
} | |
if (context.getRequestMethod().equals("OPTIONS")) { | |
context.getResponse().accepted(); | |
return; | |
} | |
context.next(); | |
} | |
public static class Builder { | |
private Set<String> allowOrigins = new HashSet<>(); | |
private Set<String> exposeHeaders = new HashSet<>(); | |
private int maxAge = -1; | |
private Boolean allowCredentials; | |
private Set<String> allowMethods = new HashSet<>(); | |
private Set<String> allowHeaders = new HashSet<>(); | |
private Builder() { | |
} | |
/** | |
* Creates an instance of CorsHandler. | |
* | |
* @return instance of CorsHandler | |
*/ | |
public CorsHandler build() { | |
return new CorsHandler(this); | |
} | |
/** | |
* <b>Required!</b> The {@code Access-Control-Allow-Origin} response header indicates whether the response can be shared with requesting code from the given origin. | |
* | |
* @param origin | |
* origin, eg: http://pippo.ro | |
* | |
* @return this | |
*/ | |
public Builder addAllowOrigin(String origin) { | |
this.allowOrigins.add(origin); | |
return this; | |
} | |
/** | |
* The {@code Access-Control-Expose-Headers} response header indicates which headers can be exposed as part of the response by listing their names. | |
* | |
* @param header | |
* header name | |
* @return this | |
*/ | |
public Builder addExposeHeader(String header) { | |
this.exposeHeaders.add(header); | |
return this; | |
} | |
/** | |
* The {@code Access-Control-Max-Age} response header indicates how long the results of a preflight request (that is the information contained in the {@code Access-Control-Allow-Methods} and | |
* {@code Access-Control-Allow-Headers} headers) can be cached. | |
* | |
* @param maxAgeInSeconds | |
* max age in seconds | |
* | |
* @return this | |
*/ | |
public Builder setMaxAge(int maxAgeInSeconds) { | |
this.maxAge = maxAgeInSeconds; | |
return this; | |
} | |
/** | |
* The Access-Control-Allow-Credentials response header indicates whether or not the response to the request can be exposed to the page. | |
* | |
* @param allowCredentials | |
* true to expose, false otherwise | |
* | |
* @return this | |
*/ | |
public Builder allowCredentials(boolean allowCredentials) { | |
this.allowCredentials = allowCredentials; | |
return this; | |
} | |
/** | |
* The Access-Control-Allow-Methods response header specifies the method or methods allowed when accessing the resource in response to a preflight request. | |
* | |
* @param method | |
* http method | |
* | |
* @return this | |
*/ | |
public Builder addAllowMethod(String method) { | |
this.allowMethods.add(method); | |
return this; | |
} | |
/** | |
* The {@code Access-Control-Allow-Headers} response header is used in response to a preflight request which includes the {@code Access-Control-Request-Headers} to indicate which HTTP headers | |
* can be used during the actual request. | |
* | |
* @param header | |
* http header | |
* | |
* @return this | |
*/ | |
public Builder addAllowHeader(String header) { | |
this.allowHeaders.add(header); | |
return this; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment