AuthenticatedURL
.
+ */
+public class WhoClient {
+
+ public static void main(String[] args) {
+ try {
+ if (args.length != 1) {
+ System.err.println("Usage: AuthenticatedURL
instances are not thread-safe.
+ *
+ * The usage pattern of the {@link AuthenticatedURL} is:
+ *
+ * + * + * // establishing an initial connection + * + * URL url = new URL("http://foo:8080/bar"); + * AuthenticatedURL.Token token = new AuthenticatedURL.Token(); + * AuthenticatedURL aUrl = new AuthenticatedURL(); + * HttpURLConnection conn = new AuthenticatedURL(url, token).openConnection(); + * .... + * // use the 'conn' instance + * .... + * + * // establishing a follow up connection using a token from the previous connection + * + * HttpURLConnection conn = new AuthenticatedURL(url, token).openConnection(); + * .... + * // use the 'conn' instance + * .... + * + *+ */ +public class AuthenticatedURL { + + /** + * Name of the HTTP cookie used for the authentication token between the client and the server. + */ + public static final String AUTH_COOKIE = "alfredo.auth"; + + private static final String AUTH_COOKIE_EQ = AUTH_COOKIE + "="; + + /** + * Client side authentication token. + */ + public static class Token { + + private String token; + + /** + * Creates a token. + */ + public Token() { + } + + /** + * Creates a token using an existing string representation of the token. + * + * @param tokenStr string representation of the tokenStr. + */ + public Token(String tokenStr) { + if (tokenStr == null) { + throw new IllegalArgumentException("tokenStr cannot be null"); + } + set(tokenStr); + } + + /** + * Returns if a token from the server has been set. + * + * @return if a token from the server has been set. + */ + public boolean isSet() { + return token != null; + } + + /** + * Sets a token. + * + * @param tokenStr string representation of the tokenStr. + */ + void set(String tokenStr) { + token = tokenStr; + } + + /** + * Returns the string representation of the token. + * + * @return the string representation of the token. + */ + @Override + public String toString() { + return token; + } + + /** + * Return the hashcode for the token. + * + * @return the hashcode for the token. + */ + @Override + public int hashCode() { + return (token != null) ? token.hashCode() : 0; + } + + /** + * Return if two token instances are equal. + * + * @param o the other token instance. + * + * @return if this instance and the other instance are equal. + */ + @Override + public boolean equals(Object o) { + boolean eq = false; + if (o instanceof Token) { + Token other = (Token) o; + eq = (token == null && other.token == null) || (token != null && this.token.equals(other.token)); + } + return eq; + } + } + + private static Class extends Authenticator> DEFAULT_AUTHENTICATOR = KerberosAuthenticator.class; + + /** + * Sets the default {@link Authenticator} class to use when an {@link AuthenticatedURL} instance + * is created without specifying an authenticator. + * + * @param authenticator the authenticator class to use as default. + */ + public static void setDefaultAuthenticator(Class extends Authenticator> authenticator) { + DEFAULT_AUTHENTICATOR = authenticator; + } + + /** + * Returns the default {@link Authenticator} class to use when an {@link AuthenticatedURL} instance + * is created without specifying an authenticator. + * + * @return the authenticator class to use as default. + */ + public static Class extends Authenticator> getDefaultAuthenticator() { + return DEFAULT_AUTHENTICATOR; + } + + private Authenticator authenticator; + + /** + * Creates an {@link AuthenticatedURL}. + */ + public AuthenticatedURL() { + this(null); + } + + /** + * Creates an
AuthenticatedURL
.
+ *
+ * @param authenticator the {@link Authenticator} instance to use, if null
a {@link
+ * KerberosAuthenticator} is used.
+ */
+ public AuthenticatedURL(Authenticator authenticator) {
+ try {
+ this.authenticator = (authenticator != null) ? authenticator : DEFAULT_AUTHENTICATOR.newInstance();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Returns an authenticated {@link HttpURLConnection}.
+ *
+ * @param url the URL to connect to. Only HTTP/S URLs are supported.
+ * @param token the authentication token being used for the user.
+ *
+ * @return an authenticated {@link HttpURLConnection}.
+ *
+ * @throws IOException if an IO error occurred.
+ * @throws AuthenticationException if an authentication exception occurred.
+ */
+ public HttpURLConnection openConnection(URL url, Token token) throws IOException, AuthenticationException {
+ if (url == null) {
+ throw new IllegalArgumentException("url cannot be NULL");
+ }
+ if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) {
+ throw new IllegalArgumentException("url must be for a HTTP or HTTPS resource");
+ }
+ if (token == null) {
+ throw new IllegalArgumentException("token cannot be NULL");
+ }
+ authenticator.authenticate(url, token);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ injectToken(conn, token);
+ return conn;
+ }
+
+ /**
+ * Helper method that injects an authentication token to send with a connection.
+ *
+ * @param conn connection to inject the authentication token into.
+ * @param token authentication token to inject.
+ */
+ public static void injectToken(HttpURLConnection conn, Token token) {
+ String t = token.token;
+ if (t != null) {
+ if (!t.startsWith("\"")) {
+ t = "\"" + t + "\"";
+ }
+ conn.addRequestProperty("Cookie", AUTH_COOKIE_EQ + t);
+ }
+ }
+
+ /**
+ * Helper method that extracts an authentication token received from a connection.
+ *
+ * This method is used by {@link Authenticator} implementations.
+ *
+ * @param conn connection to extract the authentication token from.
+ * @param token the authentication token.
+ *
+ * @throws IOException if an IO error occurred.
+ * @throws AuthenticationException if an authentication exception occurred.
+ */
+ public static void extractToken(HttpURLConnection conn, Token token) throws IOException, AuthenticationException {
+ if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ MapOPTIONS
request.
+ *
+ * @param url the URl to authenticate against.
+ * @param token the authentication token being used for the user.
+ *
+ * @throws IOException if an IO error occurred.
+ * @throws AuthenticationException if an authentication error occurred.
+ */
+ @Override
+ public void authenticate(URL url, AuthenticatedURL.Token token)
+ throws IOException, AuthenticationException {
+ if (!token.isSet()) {
+ this.url = url;
+ base64 = new Base64(0);
+ conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod(AUTH_HTTP_METHOD);
+ conn.connect();
+ if (isNegotiate()) {
+ doSpnegoSequence(token);
+ } else {
+ getFallBackAuthenticator().authenticate(url, token);
+ }
+ }
+ }
+
+ /**
+ * If the specified URL does not support SPNEGO authentication, a fallback {@link Authenticator} will be used.
+ *
+ * This implementation returns a {@link PseudoAuthenticator}.
+ *
+ * @return the fallback {@link Authenticator}.
+ */
+ protected Authenticator getFallBackAuthenticator() {
+ return new PseudoAuthenticator();
+ }
+
+ /*
+ * Indicates if the response is starting a SPNEGO negotiation.
+ */
+ private boolean isNegotiate() throws IOException {
+ boolean negotiate = false;
+ if (conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
+ String authHeader = conn.getHeaderField(WWW_AUTHENTICATE);
+ negotiate = authHeader != null && authHeader.trim().startsWith(NEGOTIATE);
+ }
+ return negotiate;
+ }
+
+ /**
+ * Implements the SPNEGO authentication sequence interaction using the current default principal
+ * in the Kerberos cache (normally set via kinit).
+ *
+ * @param token the authentication token being used for the user.
+ *
+ * @throws IOException if an IO error occurred.
+ * @throws AuthenticationException if an authentication error occurred.
+ */
+ private void doSpnegoSequence(AuthenticatedURL.Token token) throws IOException, AuthenticationException {
+ try {
+ AccessControlContext context = AccessController.getContext();
+ Subject subject = Subject.getSubject(context);
+ if (subject == null) {
+ subject = new Subject();
+ LoginContext login = new LoginContext("", subject);
+ login.login();
+ }
+ Subject.doAs(subject, new PrivilegedExceptionActionOPTIONS
request injecting an additional
+ * parameter {@link #USER_NAME} in the query string with the value returned by the {@link #getUserName()}
+ * method.
+ *
+ * If the response is successful it will update the authentication token.
+ *
+ * @param url the URl to authenticate against.
+ * @param token the authencation token being used for the user.
+ *
+ * @throws IOException if an IO error occurred.
+ * @throws AuthenticationException if an authentication error occurred.
+ */
+ @Override
+ public void authenticate(URL url, AuthenticatedURL.Token token) throws IOException, AuthenticationException {
+ String strUrl = url.toString();
+ String paramSeparator = (strUrl.contains("?")) ? "&" : "?";
+ strUrl += paramSeparator + USER_NAME_EQ + getUserName();
+ url = new URL(strUrl);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("OPTIONS");
+ conn.connect();
+ AuthenticatedURL.extractToken(conn, token);
+ }
+
+ /**
+ * Returns the current user name.
+ *
+ * This implementation returns the value of the Java system property 'user.name'
+ *
+ * @return the current user name.
+ */
+ protected String getUserName() {
+ return System.getProperty("user.name");
+ }
+}
diff --git a/hadoop-alfredo/src/main/java/org/apache/hadoop/alfredo/server/AuthenticationFilter.java b/hadoop-alfredo/src/main/java/org/apache/hadoop/alfredo/server/AuthenticationFilter.java
new file mode 100644
index 00000000000..2b39d7ee592
--- /dev/null
+++ b/hadoop-alfredo/src/main/java/org/apache/hadoop/alfredo/server/AuthenticationFilter.java
@@ -0,0 +1,402 @@
+/**
+ * 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. See accompanying LICENSE file.
+ */
+package org.apache.hadoop.alfredo.server;
+
+import org.apache.hadoop.alfredo.client.AuthenticatedURL;
+import org.apache.hadoop.alfredo.client.AuthenticationException;
+import org.apache.hadoop.alfredo.util.Signer;
+import org.apache.hadoop.alfredo.util.SignerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Random;
+
+/**
+ * The {@link AuthenticationFilter} enables protecting web application resources with different (pluggable)
+ * authentication mechanisms.
+ *
+ * Out of the box it provides 2 authentication mechanisms: Pseudo and Kerberos SPNEGO.
+ *
+ * Additional authentication mechanisms are supported via the {@link AuthenticationHandler} interface.
+ *
+ * This filter delegates to the configured authentication handler for authentication and once it obtains an
+ * {@link AuthenticationToken} from it, sets a signed HTTP cookie with the token. For client requests
+ * that provide the signed HTTP cookie, it verifies the validity of the cookie, extracts the user information
+ * and lets the request proceed to the target resource.
+ *
+ * The supported configuration properties are:
+ * 3600
seconds.null
the filter will invoke the configured {@link AuthenticationHandler}
+ * to perform user authentication.
+ *
+ * @param request request object.
+ *
+ * @return the Authentication token if the request is authenticated, null
otherwise.
+ *
+ * @throws IOException thrown if an IO error occurred.
+ * @throws AuthenticationException thrown if the token is invalid or if it has expired.
+ */
+ protected AuthenticationToken getToken(HttpServletRequest request) throws IOException, AuthenticationException {
+ AuthenticationToken token = null;
+ String tokenStr = null;
+ Cookie[] cookies = request.getCookies();
+ if (cookies != null) {
+ for (Cookie cookie : cookies) {
+ if (cookie.getName().equals(AuthenticatedURL.AUTH_COOKIE)) {
+ tokenStr = cookie.getValue();
+ try {
+ tokenStr = signer.verifyAndExtract(tokenStr);
+ } catch (SignerException ex) {
+ throw new AuthenticationException(ex);
+ }
+ break;
+ }
+ }
+ }
+ if (tokenStr != null) {
+ token = AuthenticationToken.parse(tokenStr);
+ if (!token.getType().equals(authHandler.getType())) {
+ throw new AuthenticationException("Invalid AuthenticationToken type");
+ }
+ if (token.isExpired()) {
+ throw new AuthenticationException("AuthenticationToken expired");
+ }
+ }
+ return token;
+ }
+
+ /**
+ * If the request has a valid authentication token it allows the request to continue to the target resource,
+ * otherwise it triggers an authentication sequence using the configured {@link AuthenticationHandler}.
+ *
+ * @param request the request object.
+ * @param response the response object.
+ * @param filterChain the filter chain object.
+ *
+ * @throws IOException thrown if an IO error occurred.
+ * @throws ServletException thrown if a processing error occurred.
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
+ throws IOException, ServletException {
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ try {
+ boolean newToken = false;
+ AuthenticationToken token = getToken(httpRequest);
+ if (token == null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Request [{}] triggering authentication", getRequestURL(httpRequest));
+ }
+ token = authHandler.authenticate(httpRequest, httpResponse);
+ if (token != null && token != AuthenticationToken.ANONYMOUS) {
+ token.setExpires(System.currentTimeMillis() + getValidity() * 1000);
+ }
+ newToken = true;
+ }
+ if (token != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Request [{}] user [{}] authenticated", getRequestURL(httpRequest), token.getUserName());
+ }
+ final AuthenticationToken authToken = token;
+ httpRequest = new HttpServletRequestWrapper(httpRequest) {
+
+ @Override
+ public String getAuthType() {
+ return authToken.getType();
+ }
+
+ @Override
+ public String getRemoteUser() {
+ return authToken.getUserName();
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return (authToken != AuthenticationToken.ANONYMOUS) ? authToken : null;
+ }
+ };
+ if (newToken && token != AuthenticationToken.ANONYMOUS) {
+ String signedToken = signer.sign(token.toString());
+ Cookie cookie = createCookie(signedToken);
+ httpResponse.addCookie(cookie);
+ }
+ filterChain.doFilter(httpRequest, httpResponse);
+ }
+ } catch (AuthenticationException ex) {
+ if (!httpResponse.isCommitted()) {
+ Cookie cookie = createCookie("");
+ cookie.setMaxAge(0);
+ httpResponse.addCookie(cookie);
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage());
+ }
+ LOG.warn("Authentication exception: " + ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Creates the Alfredo authentiation HTTP cookie.
+ *
+ * It sets the domain and path specified in the configuration.
+ *
+ * @param token authentication token for the cookie.
+ *
+ * @return the HTTP cookie.
+ */
+ protected Cookie createCookie(String token) {
+ Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, token);
+ if (getCookieDomain() != null) {
+ cookie.setDomain(getCookieDomain());
+ }
+ if (getCookiePath() != null) {
+ cookie.setPath(getCookiePath());
+ }
+ return cookie;
+ }
+}
diff --git a/hadoop-alfredo/src/main/java/org/apache/hadoop/alfredo/server/AuthenticationHandler.java b/hadoop-alfredo/src/main/java/org/apache/hadoop/alfredo/server/AuthenticationHandler.java
new file mode 100644
index 00000000000..e79c938699f
--- /dev/null
+++ b/hadoop-alfredo/src/main/java/org/apache/hadoop/alfredo/server/AuthenticationHandler.java
@@ -0,0 +1,89 @@
+/**
+ * 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. See accompanying LICENSE file.
+ */
+package org.apache.hadoop.alfredo.server;
+
+import org.apache.hadoop.alfredo.client.AuthenticationException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Interface for server authentication mechanisms.
+ *
+ * The {@link AuthenticationFilter} manages the lifecycle of the authentication handler.
+ *
+ * Implementations must be thread-safe as one instance is initialized and used for all requests.
+ */
+public interface AuthenticationHandler {
+
+ /**
+ * Returns the authentication type of the authentication handler.
+ *
+ * This should be a name that uniquely identifies the authentication type.
+ * For example 'simple' or 'kerberos'.
+ *
+ * @return the authentication type of the authentication handler.
+ */
+ public String getType();
+
+ /**
+ * Initializes the authentication handler instance.
+ *
+ * This method is invoked by the {@link AuthenticationFilter#init} method.
+ *
+ * @param config configuration properties to initialize the handler.
+ *
+ * @throws ServletException thrown if the handler could not be initialized.
+ */
+ public void init(Properties config) throws ServletException;
+
+ /**
+ * Destroys the authentication handler instance.
+ *
+ * This method is invoked by the {@link AuthenticationFilter#destroy} method.
+ */
+ public void destroy();
+
+ /**
+ * Performs an authentication step for the given HTTP client request.
+ *
+ * This method is invoked by the {@link AuthenticationFilter} only if the HTTP client request is
+ * not yet authenticated.
+ *
+ * Depending upon the authentication mechanism being implemented, a particular HTTP client may
+ * end up making a sequence of invocations before authentication is successfully established (this is
+ * the case of Kerberos SPNEGO).
+ *
+ * This method must return an {@link AuthenticationToken} only if the the HTTP client request has
+ * been successfully and fully authenticated.
+ *
+ * If the HTTP client request has not been completely authenticated, this method must take over
+ * the corresponding HTTP response and it must return null
.
+ *
+ * @param request the HTTP client request.
+ * @param response the HTTP client response.
+ *
+ * @return an {@link AuthenticationToken} if the HTTP client request has been authenticated,
+ * null
otherwise (in this case it must take care of the response).
+ *
+ * @throws IOException thrown if an IO error occurred.
+ * @throws AuthenticationException thrown if an Authentication error occurred.
+ */
+ public AuthenticationToken authenticate(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, AuthenticationException;
+
+}
diff --git a/hadoop-alfredo/src/main/java/org/apache/hadoop/alfredo/server/AuthenticationToken.java b/hadoop-alfredo/src/main/java/org/apache/hadoop/alfredo/server/AuthenticationToken.java
new file mode 100644
index 00000000000..0ae9947a8f1
--- /dev/null
+++ b/hadoop-alfredo/src/main/java/org/apache/hadoop/alfredo/server/AuthenticationToken.java
@@ -0,0 +1,226 @@
+/**
+ * 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. See accompanying LICENSE file.
+ */
+package org.apache.hadoop.alfredo.server;
+
+import org.apache.hadoop.alfredo.client.AuthenticationException;
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * The {@link AuthenticationToken} contains information about an authenticated HTTP client and doubles
+ * as the {@link Principal} to be returned by authenticated {@link HttpServletRequest}s
+ *
+ * The token can be serialized/deserialized to and from a string as it is sent and received in HTTP client
+ * responses and requests as a HTTP cookie (this is done by the {@link AuthenticationFilter}).
+ */
+public class AuthenticationToken implements Principal {
+
+ /**
+ * Constant that identifies an anonymous request.
+ */
+ public static final AuthenticationToken ANONYMOUS = new AuthenticationToken();
+
+ private static final String ATTR_SEPARATOR = "&";
+ private static final String USER_NAME = "u";
+ private static final String PRINCIPAL = "p";
+ private static final String EXPIRES = "e";
+ private static final String TYPE = "t";
+
+ private final static SetSystem.currentTimeMillis() + validityPeriod
).
+ */
+ public AuthenticationToken(String userName, String principal, String type) {
+ checkForIllegalArgument(userName, "userName");
+ checkForIllegalArgument(principal, "principal");
+ checkForIllegalArgument(type, "type");
+ this.userName = userName;
+ this.principal = principal;
+ this.type = type;
+ this.expires = -1;
+ }
+
+ /**
+ * Check if the provided value is invalid. Throw an error if it is invalid, NOP otherwise.
+ *
+ * @param value the value to check.
+ * @param name the parameter name to use in an error message if the value is invalid.
+ */
+ private static void checkForIllegalArgument(String value, String name) {
+ if (value == null || value.length() == 0 || value.contains(ATTR_SEPARATOR)) {
+ throw new IllegalArgumentException(name + ILLEGAL_ARG_MSG);
+ }
+ }
+
+ /**
+ * Sets the expiration of the token.
+ *
+ * @param expires expiration time of the token in milliseconds since the epoch.
+ */
+ public void setExpires(long expires) {
+ if (this != AuthenticationToken.ANONYMOUS) {
+ this.expires = expires;
+ generateToken();
+ }
+ }
+
+ /**
+ * Generates the token.
+ */
+ private void generateToken() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(USER_NAME).append("=").append(userName).append(ATTR_SEPARATOR);
+ sb.append(PRINCIPAL).append("=").append(principal).append(ATTR_SEPARATOR);
+ sb.append(TYPE).append("=").append(type).append(ATTR_SEPARATOR);
+ sb.append(EXPIRES).append("=").append(expires);
+ token = sb.toString();
+ }
+
+ /**
+ * Returns the user name.
+ *
+ * @return the user name.
+ */
+ public String getUserName() {
+ return userName;
+ }
+
+ /**
+ * Returns the principal name (this method name comes from the JDK {@link Principal} interface).
+ *
+ * @return the principal name.
+ */
+ @Override
+ public String getName() {
+ return principal;
+ }
+
+ /**
+ * Returns the authentication mechanism of the token.
+ *
+ * @return the authentication mechanism of the token.
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Returns the expiration time of the token.
+ *
+ * @return the expiration time of the token, in milliseconds since Epoc.
+ */
+ public long getExpires() {
+ return expires;
+ }
+
+ /**
+ * Returns if the token has expired.
+ *
+ * @return if the token has expired.
+ */
+ public boolean isExpired() {
+ return expires != -1 && System.currentTimeMillis() > expires;
+ }
+
+ /**
+ * Returns the string representation of the token.
+ *
+ * This string representation is parseable by the {@link #parse} method.
+ *
+ * @return the string representation of the token.
+ */
+ @Override
+ public String toString() {
+ return token;
+ }
+
+ /**
+ * Parses a string into an authentication token.
+ *
+ * @param tokenStr string representation of a token.
+ *
+ * @return the parsed authentication token.
+ *
+ * @throws AuthenticationException thrown if the string representation could not be parsed into
+ * an authentication token.
+ */
+ public static AuthenticationToken parse(String tokenStr) throws AuthenticationException {
+ MapHTTP/${HOSTNAME}@{REALM}
. The realm can be omitted from the
+ * principal as the JDK GSS libraries will use the realm name of the configured default realm.
+ * It does not have a default value.