You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by pr...@apache.org on 2022/04/29 12:37:09 UTC
[ranger] 02/03: RANGER-3727: Create common module for handling authentication
This is an automated email from the ASF dual-hosted git repository.
pradeep pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
commit fb53a3dc0022020e68e4ae9edb316a58f7d0a800
Author: Kishor Gollapalliwar <ki...@gmail.com>
AuthorDate: Thu Apr 28 12:32:51 2022 +0530
RANGER-3727: Create common module for handling authentication
Signed-off-by: pradeep <pr...@apache.org>
---
pom.xml | 2 +
ranger-authn/.gitignore | 1 +
ranger-authn/pom.xml | 67 +++++
.../apache/ranger/authz/handler/RangerAuth.java | 67 +++++
.../ranger/authz/handler/RangerAuthHandler.java | 28 ++
.../handler/jwt/RangerDefaultJwtAuthHandler.java | 95 +++++++
.../authz/handler/jwt/RangerJwtAuthHandler.java | 313 +++++++++++++++++++++
7 files changed, 573 insertions(+)
diff --git a/pom.xml b/pom.xml
index ec8c6751d..56c82dc82 100644
--- a/pom.xml
+++ b/pom.xml
@@ -310,6 +310,7 @@
<module>ranger-kylin-plugin-shim</module>
<module>plugin-elasticsearch</module>
<module>ranger-elasticsearch-plugin-shim</module>
+ <module>ranger-authn</module>
<!--
'distro' should be the last module. If a module gets inserted after
ranger-elasticsearch-plugin-shim, make sure to update dependency in distro/pom.xml
@@ -607,6 +608,7 @@
<module>ranger-presto-plugin-shim</module>
<module>plugin-elasticsearch</module>
<module>ranger-elasticsearch-plugin-shim</module>
+ <module>ranger-authn</module>
<!--
'distro' should be the last module. If a module gets inserted after
ranger-elasticsearch-plugin-shim, make sure to update dependency in distro/pom.xml
diff --git a/ranger-authn/.gitignore b/ranger-authn/.gitignore
new file mode 100644
index 000000000..b83d22266
--- /dev/null
+++ b/ranger-authn/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/ranger-authn/pom.xml b/ranger-authn/pom.xml
new file mode 100644
index 000000000..827fd0746
--- /dev/null
+++ b/ranger-authn/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>ranger-authn</artifactId>
+ <name>ranger-authn</name>
+ <description>Ranger Authentication module</description>
+ <packaging>jar</packaging>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <parent>
+ <groupId>org.apache.ranger</groupId>
+ <artifactId>ranger</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-common</artifactId>
+ <version>${hadoop.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-all</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>${commons.lang.version}</version>
+ </dependency>
+
+ <!-- JWT -->
+ <dependency>
+ <groupId>com.nimbusds</groupId>
+ <artifactId>nimbus-jose-jwt</artifactId>
+ <version>${nimbus-jose-jwt.version}</version>
+ </dependency>
+
+ <!-- Test -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuth.java b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuth.java
new file mode 100644
index 000000000..b0757ae7d
--- /dev/null
+++ b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuth.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.ranger.authz.handler;
+
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+
+public class RangerAuth {
+ public static enum AUTH_TYPE {
+ JWT_JWKS("JWT-JWKS");
+
+ private final String authType;
+
+ private AUTH_TYPE(String authType) {
+ this.authType = authType;
+ }
+ }
+
+ private String userName;
+ private AUTH_TYPE type;
+ private boolean isAuthenticated;
+
+ public RangerAuth(final AuthenticationToken authenticationToken, AUTH_TYPE type) {
+ this.userName = authenticationToken.getName();
+ this.isAuthenticated = true;
+ this.type = type;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public AUTH_TYPE getType() {
+ return type;
+ }
+
+ public void setType(AUTH_TYPE type) {
+ this.type = type;
+ }
+
+ public boolean isAuthenticated() {
+ return isAuthenticated;
+ }
+
+ public void setAuthenticated(boolean isAuthenticated) {
+ this.isAuthenticated = isAuthenticated;
+ }
+}
diff --git a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuthHandler.java b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuthHandler.java
new file mode 100644
index 000000000..4dcc37cc7
--- /dev/null
+++ b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuthHandler.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.ranger.authz.handler;
+
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface RangerAuthHandler {
+ void initialize(final Properties config) throws Exception;
+ RangerAuth authenticate(final HttpServletRequest request);
+}
diff --git a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerDefaultJwtAuthHandler.java b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerDefaultJwtAuthHandler.java
new file mode 100644
index 000000000..c0d5b8d9a
--- /dev/null
+++ b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerDefaultJwtAuthHandler.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.ranger.authz.handler.jwt;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.ranger.authz.handler.RangerAuth;
+
+import com.nimbusds.jose.proc.JWSKeySelector;
+import com.nimbusds.jose.proc.SecurityContext;
+import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
+import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier;
+import com.nimbusds.jwt.proc.DefaultJWTProcessor;
+import com.nimbusds.jwt.proc.JWTClaimsSetVerifier;
+
+/**
+ * Default implementation of Ranger JWT authentication
+ *
+ */
+public class RangerDefaultJwtAuthHandler extends RangerJwtAuthHandler {
+
+ protected static final String AUTHORIZATION_HEADER = "Authorization";
+
+ @Override
+ public ConfigurableJWTProcessor<SecurityContext> getJwtProcessor(JWSKeySelector<SecurityContext> keySelector) {
+ ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
+ JWTClaimsSetVerifier<SecurityContext> claimsVerifier = new DefaultJWTClaimsVerifier<>();
+
+ jwtProcessor.setJWSKeySelector(keySelector);
+ jwtProcessor.setJWTClaimsSetVerifier(claimsVerifier);
+
+ return jwtProcessor;
+ }
+
+ @Override
+ public RangerAuth authenticate(HttpServletRequest httpServletRequest) {
+ RangerAuth rangerAuth = null;
+ String jwtAuthHeaderStr = getJwtAuthHeader(httpServletRequest);
+ String jwtCookieStr = StringUtils.isBlank(jwtAuthHeaderStr) ? getJwtCookie(httpServletRequest) : null;
+
+ AuthenticationToken authenticationToken = authenticate(jwtAuthHeaderStr, jwtCookieStr);
+
+ if (authenticationToken != null) {
+ rangerAuth = new RangerAuth(authenticationToken, RangerAuth.AUTH_TYPE.JWT_JWKS);
+ }
+
+ return rangerAuth;
+ }
+
+ public static boolean canAuthenticateRequest(final ServletRequest request) {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ String jwtAuthHeaderStr = getJwtAuthHeader(httpServletRequest);
+ String jwtCookieStr = StringUtils.isBlank(jwtAuthHeaderStr) ? getJwtCookie(httpServletRequest) : null;
+
+ return shouldProceedAuth(jwtAuthHeaderStr, jwtCookieStr);
+ }
+
+ public static String getJwtAuthHeader(final HttpServletRequest httpServletRequest) {
+ return httpServletRequest.getHeader(AUTHORIZATION_HEADER);
+ }
+
+ public static String getJwtCookie(final HttpServletRequest httpServletRequest) {
+ String jwtCookieStr = null;
+ Cookie[] cookies = httpServletRequest.getCookies();
+ if (cookies != null) {
+ for (Cookie cookie : cookies) {
+ if (cookieName.equals(cookie.getName())) {
+ jwtCookieStr = cookie.getName() + "=" + cookie.getValue();
+ break;
+ }
+ }
+ }
+ return jwtCookieStr;
+ }
+}
diff --git a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java
new file mode 100644
index 000000000..0973b42de
--- /dev/null
+++ b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java
@@ -0,0 +1,313 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.ranger.authz.handler.jwt;
+
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.hadoop.security.authentication.util.CertificateUtil;
+import org.apache.ranger.authz.handler.RangerAuthHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSObject;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jose.jwk.source.JWKSource;
+import com.nimbusds.jose.jwk.source.RemoteJWKSet;
+import com.nimbusds.jose.proc.BadJOSEException;
+import com.nimbusds.jose.proc.JWSKeySelector;
+import com.nimbusds.jose.proc.JWSVerificationKeySelector;
+import com.nimbusds.jose.proc.SecurityContext;
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
+
+public abstract class RangerJwtAuthHandler implements RangerAuthHandler {
+ private static final Logger LOG = LoggerFactory.getLogger(RangerJwtAuthHandler.class);
+
+ private JWSVerifier verifier = null;
+ private String jwksProviderUrl = null;
+ public static final String TYPE = "ranger-jwt"; // Constant that identifies the authentication mechanism.
+ public static final String KEY_PROVIDER_URL = "jwks.provider-url"; // JWKS provider URL
+ public static final String KEY_JWT_PUBLIC_KEY = "jwt.public-key"; // JWT token provider public key
+ public static final String KEY_JWT_COOKIE_NAME = "jwt.cookie-name"; // JWT cookie name
+ public static final String KEY_JWT_AUDIENCES = "jwt.audiences";
+ public static final String JWT_AUTHZ_PREFIX = "Bearer ";
+
+ protected List<String> audiences = null;
+ protected JWKSource<SecurityContext> keySource = null;
+
+ protected static String cookieName = "hadoop-jwt";
+
+ @Override
+ public void initialize(final Properties config) throws Exception {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("===>>> RangerJwtAuthHandler.initialize()");
+ }
+
+ // mandatory configurations
+ jwksProviderUrl = config.getProperty(KEY_PROVIDER_URL);
+ if (!StringUtils.isBlank(jwksProviderUrl)) {
+ keySource = new RemoteJWKSet<>(new URL(jwksProviderUrl));
+ }
+
+ // optional configurations
+ String pemPublicKey = config.getProperty(KEY_JWT_PUBLIC_KEY);
+
+ // setup JWT provider public key if configured
+ if (StringUtils.isNotBlank(pemPublicKey)) {
+ verifier = new RSASSAVerifier(CertificateUtil.parseRSAPublicKey(pemPublicKey));
+ } else if (StringUtils.isBlank(jwksProviderUrl)) {
+ throw new Exception("RangerJwtAuthHandler: Mandatory configs ('jwks.provider-url' & 'jwt.public-key') are missing, must provide atleast one.");
+ }
+
+ // setup custom cookie name if configured
+ String customCookieName = config.getProperty(KEY_JWT_COOKIE_NAME);
+ if (customCookieName != null) {
+ cookieName = customCookieName;
+ }
+
+ // setup audiences if configured
+ String audiencesStr = config.getProperty(KEY_JWT_AUDIENCES);
+ if (StringUtils.isNotBlank(audiencesStr)) {
+ audiences = Arrays.asList(audiencesStr.split(","));
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<<<=== RangerJwtAuthHandler.initialize()");
+ }
+ }
+
+ protected AuthenticationToken authenticate(final String jwtAuthHeader, final String jwtCookie) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("===>>> RangerJwtAuthHandler.authenticate()");
+ }
+
+ AuthenticationToken token = null;
+ if (shouldProceedAuth(jwtAuthHeader, jwtCookie)) {
+ String serializedJWT = getJWT(jwtAuthHeader, jwtCookie);
+
+ if (StringUtils.isNotBlank(serializedJWT)) {
+ try {
+ final SignedJWT jwtToken = SignedJWT.parse(serializedJWT);
+ boolean valid = validateToken(jwtToken);
+ if (valid) {
+ final String userName = jwtToken.getJWTClaimsSet().getSubject();
+ LOG.info("Issuing AuthenticationToken for user: [{}]", userName);
+ token = new AuthenticationToken(userName, userName, TYPE);
+ } else {
+ LOG.warn("Validation failed for JWT token: [{}] ", jwtToken.serialize());
+ }
+ } catch (ParseException pe) {
+ LOG.warn("Unable to parse the JWT token", pe);
+ }
+ } else {
+ LOG.warn("JWT token not found.");
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<<<=== RangerJwtAuthHandler.authenticate()");
+ }
+
+ return token;
+ }
+
+ protected String getJWT(final String jwtAuthHeader, final String jwtCookie) {
+ String serializedJWT = null;
+
+ // try to fetch from AUTH header
+ if (StringUtils.isNotBlank(jwtAuthHeader) && jwtAuthHeader.startsWith(JWT_AUTHZ_PREFIX)) {
+ serializedJWT = jwtAuthHeader.substring(JWT_AUTHZ_PREFIX.length());
+ }
+
+ // if not found in AUTH header, try to fetch from cookie
+ if (StringUtils.isBlank(serializedJWT) && StringUtils.isNotBlank(jwtCookie)) {
+ String[] cookie = jwtCookie.split("=");
+ if (cookieName.equals(cookie[0])) {
+ serializedJWT = cookie[1];
+ }
+ }
+
+ return serializedJWT;
+ }
+
+ /**
+ * This method provides a single method for validating the JWT for use in
+ * request processing. It provides for the override of specific aspects of this
+ * implementation through submethods used within but also allows for the
+ * override of the entire token validation algorithm.
+ *
+ * @param jwtToken the token to validate
+ * @return true if valid
+ */
+ protected boolean validateToken(final SignedJWT jwtToken) {
+ boolean expValid = validateExpiration(jwtToken);
+ boolean sigValid = false;
+ boolean audValid = false;
+
+ if (expValid) {
+ sigValid = validateSignature(jwtToken);
+
+ if (sigValid) {
+ audValid = validateAudiences(jwtToken);
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("expValid={}, sigValid={}, audValid={}", expValid, sigValid, audValid);
+ }
+
+ return sigValid && audValid && expValid;
+ }
+
+ /**
+ * Verify the signature of the JWT token in this method. This method depends on
+ * the public key that was established during init based upon the provisioned
+ * public key. Override this method in subclasses in order to customize the
+ * signature verification behavior.
+ *
+ * @param jwtToken the token that contains the signature to be validated
+ * @return valid true if signature verifies successfully; false otherwise
+ */
+ protected boolean validateSignature(final SignedJWT jwtToken) {
+ boolean valid = false;
+
+ if (JWSObject.State.SIGNED == jwtToken.getState()) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("JWT token is in a SIGNED state");
+ }
+
+ if (jwtToken.getSignature() != null) {
+ try {
+ if (StringUtils.isNotBlank(jwksProviderUrl)) {
+ JWSKeySelector<SecurityContext> keySelector = new JWSVerificationKeySelector<>(jwtToken.getHeader().getAlgorithm(), keySource);
+
+ // Create a JWT processor for the access tokens
+ ConfigurableJWTProcessor<SecurityContext> jwtProcessor = getJwtProcessor(keySelector);
+
+ // Process the token
+ jwtProcessor.process(jwtToken, null);
+ valid = true;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("JWT token has been successfully verified.");
+ }
+ } else if (verifier != null) {
+ if (jwtToken.verify(verifier)) {
+ valid = true;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("JWT token has been successfully verified.");
+ }
+ } else {
+ LOG.warn("JWT signature verification failed.");
+ }
+ } else {
+ LOG.warn("Cannot authenticate JWT token as neither JWKS provider URL nor public key provided.");
+ }
+ } catch (JOSEException | BadJOSEException e) {
+ LOG.error("Error while validating signature.", e);
+ }
+ }
+ }
+
+ if (!valid) {
+ LOG.warn("Signature could not be verified.");
+ }
+
+ return valid;
+ }
+
+ public abstract ConfigurableJWTProcessor<SecurityContext> getJwtProcessor(final JWSKeySelector<SecurityContext> keySelector);
+
+ /**
+ * Validate whether any of the accepted audience claims is present in the issued
+ * token claims list for audience. Override this method in subclasses in order
+ * to customize the audience validation behavior.
+ *
+ * @param jwtToken the JWT token where the allowed audiences will be found
+ * @return true if an expected audience is present, otherwise false
+ */
+ protected boolean validateAudiences(final SignedJWT jwtToken) {
+ boolean valid = false;
+ try {
+ List<String> tokenAudienceList = jwtToken.getJWTClaimsSet().getAudience();
+ // if there were no expected audiences configured then just
+ // consider any audience acceptable
+ if (audiences == null) {
+ valid = true;
+ } else {
+ // if any of the configured audiences is found then consider it
+ // acceptable
+ for (String aud : tokenAudienceList) {
+ if (audiences.contains(aud)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("JWT token audience has been successfully validated.");
+ }
+ valid = true;
+ break;
+ }
+ }
+ if (!valid) {
+ LOG.warn("JWT audience validation failed.");
+ }
+ }
+ } catch (ParseException pe) {
+ LOG.warn("Unable to parse the JWT token.", pe);
+ }
+ return valid;
+ }
+
+ /**
+ * Validate that the expiration time of the JWT token has not been violated. If
+ * it has then throw an AuthenticationException. Override this method in
+ * subclasses in order to customize the expiration validation behavior.
+ *
+ * @param jwtToken the token that contains the expiration date to validate
+ * @return valid true if the token has not expired; false otherwise
+ */
+ protected boolean validateExpiration(final SignedJWT jwtToken) {
+ boolean valid = false;
+ try {
+ Date expires = jwtToken.getJWTClaimsSet().getExpirationTime();
+ if (expires == null || new Date().before(expires)) {
+ valid = true;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("JWT token expiration date has been successfully validated.");
+ }
+ } else {
+ LOG.warn("JWT token provided is expired.");
+ }
+ } catch (ParseException pe) {
+ LOG.warn("Failed to validate JWT expiry.", pe);
+ }
+
+ return valid;
+ }
+
+ public static boolean shouldProceedAuth(final String authHeader, final String jwtCookie) {
+ return (StringUtils.isNotBlank(authHeader) && authHeader.startsWith(JWT_AUTHZ_PREFIX)) || (StringUtils.isNotBlank(jwtCookie) && jwtCookie.startsWith(cookieName));
+ }
+}