All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.osgl.util.Token Maven / Gradle / Ivy

package org.osgl.util;

/*-
 * #%L
 * OSGL Tool Extension
 * %%
 * Copyright (C) 2017 OSGL (Open Source General Library)
 * %%
 * 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.
 * #L%
 */

import org.osgl.$;
import org.osgl.cache.CacheService;
import org.osgl.cache.CacheServiceProvider;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * A token is tool to generate a string with an ID and optionally a
 * list of string as payload, and encrypt the string so that it can
 * be sent through the wire.
 * 

* Typical use of {@code Token} is to generate a secret handle * in an email sent to the user. And when user click on a link, it * will pass the token back to the server so that server knows * who the user is. The whole process happening is a secure way * without the need for login with password *

*/ @SuppressWarnings("unused") public class Token implements Serializable { public static enum Life { /** * very short life token that live for only 1 min */ ONE_MIN(60), /** * short life token live for 1 hour */ SHORT(60 * 60), ONE_HOUR(60 * 60), /** * Normal life token live for 1 day */ NORMAL(60 * 60 * 24), ONE_DAY(60 * 60 * 24), ONE_WEEK(60 * 60 * 24 * 7), THIRTY_DAYS(60 * 60 * 24 * 30), /** * Long life token live for 90 days */ LONG(60 * 60 * 24 * 90), NINETY_DAYS(60 * 60 * 24 * 90), /** * Never expire token */ FOREVER(-1) ; private long seconds; Life(long seconds) { this.seconds = seconds; } /** * Return the due time in time millis. * * Note `0` or negative number means never due * * @return the due timestamp of this token life from now on */ public long due() { return due(seconds); } static long due(long seconds) { if (seconds <= 0) { return -1; } long now = System.currentTimeMillis(); long period = seconds * 1000; return now + period; } } private static volatile CacheService cache; private static CacheService cache() { if (null != cache) { return cache; } synchronized (Token.class) { if (null == cache) { String cacheName = System.getProperty("aaa.cache.name"); if (S.notBlank(cacheName)) { cache = CacheServiceProvider.Impl.Auto.get(cacheName); } else { cache = CacheServiceProvider.Impl.Auto.get(); } } } return cache; } private String id; private long due; private List payload = new ArrayList(); /** * Return the ID of the token * @return the token ID */ public String id() { return id; } /** * Return the payload of the token * @return the token payload */ public List payload() { return C.list(payload); } /** * Returns the first payload or null if no payload found * @return the first payload for `null` */ public String firstPayload() { return payload.isEmpty() ? null : payload.get(0); } /** * Returns a payload at `index` or `null` if no payload found * there * * @param index the index to fetch the payload * @return the payload at the position or `null` if not available */ public String payload(int index) { return payload.size() > index ? payload.get(index) : null; } /** * Alias of {@link #expired()} * @return `true` if the token {@link #expired() is expired} */ public boolean isExpired() { return expired(); } public boolean expired() { return due > 0 && due <= $.ms(); } /** * Alias of {@link #isEmpty()} * @return if the token is empty */ public boolean empty() { return isEmpty(); } /** * Check if the token contains an ID or not * @return {@code true} if the token contains a ID */ public boolean isEmpty() { return S.isBlank(id); } /** * Alias of {@link #consumed()} * @return `true` if the token is {@link #consumed() is consumed} */ public boolean isConsumed() { return consumed(); } /** * Check if the token is consumed or not * @return {@code true} if the token {@link #consume() marked as consumed} */ public boolean consumed() { return cache().get("auth-tk-consumed-" + (id + due)) != null; } /** * Mark a token to be consumed */ public void consume() { cache().put("auth-tk-consumed-" + (id + due), "true", (int)(due + 1000 - System.currentTimeMillis())/1000); } /** * Alias of {@link #isValid()} * @return `true` if the token {@link #isValid() is valid} */ public boolean valid() { return isValid(); } /** * Check if the token is valid. * * A token is considered to be valid when none of the following criteria met: * * token {@link #isEmpty() is empty} * * token {@link #consumed() is consumed} * * token {@link #expired() is expired} * @return */ public boolean isValid() { return !isEmpty() && !expired() && !consumed(); } /** * Check if the token is NOT valid * @return `true` if the token is not {@link #isValid()} */ public boolean notValid() { return !isValid(); } @Override public int hashCode() { return $.hc(id, due, payload); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof Token) { Token that = (Token)obj; return S.eq(that.id, this.id) && that.due == this.due && $.eq(that.payload, this.payload); } return false; } @Override public String toString() { return S.fmt("{id: %s, expired: %s, due: %s, payload: %s", id, expired(), due, payload); } /** * This method is deprecated. Please use {@link #generateToken(byte[], String, String...)} instead * * Generate a token string with secret key, ID and optionally payloads * @param secret the secret to encrypt to token string * @param oid the ID of the token (could be customer ID etc) * @param payload the payload optionally indicate more information * @return an encrypted token string that is expiring in {@link Life#SHORT} time period */ @Deprecated public static String generateToken(String secret, String oid, String... payload) { return generateToken(secret, Life.SHORT, oid, payload); } /** * Generate a token string with secret key, ID and optionally payloads * @param secret the secret to encrypt to token string * @param oid the ID of the token (could be customer ID etc) * @param payload the payload optionally indicate more information * @return an encrypted token string that is expiring in {@link Life#SHORT} time period */ public static String generateToken(byte[] secret, String oid, String... payload) { return generateToken(secret, Life.SHORT, oid, payload); } /** * This method is deprecated, please use {@link #generateToken(byte[], Life, String, String...)} instead * * Generate a token string with secret key, ID and optionally payloads * @param secret the secret to encrypt to token string * @param tl the expiration of the token * @param oid the ID of the token (could be customer ID etc) * @param payload the payload optionally indicate more information * @return an encrypted token string that is expiring in {@link Life#SHORT} time period */ @Deprecated public static String generateToken(String secret, Life tl, String oid, String... payload) { return generateToken(secret, tl.due(), oid, payload); } /** * Generate a token string with secret key, ID and optionally payloads * @param secret the secret to encrypt to token string * @param tl the expiration of the token * @param oid the ID of the token (could be customer ID etc) * @param payload the payload optionally indicate more information * @return an encrypted token string that is expiring in {@link Life#SHORT} time period */ public static String generateToken(byte[] secret, Life tl, String oid, String... payload) { return generateToken(secret, tl.due(), oid, payload); } /** * This method is deprecated. please use {@link #generateToken(byte[], long, String, String...)} instead * * Generate a token string with secret key, ID and optionally payloads * @param secret the secret to encrypt to token string * @param seconds the expiration of the token in seconds * @param oid the ID of the token (could be customer ID etc) * @param payload the payload optionally indicate more information * @return an encrypted token string that is expiring in {@link Life#SHORT} time period */ @Deprecated public static String generateToken(String secret, long seconds, String oid, String... payload) { return generateToken(secret.getBytes(Charsets.UTF_8), seconds, oid, payload); } /** * Generate a token string with secret key, ID and optionally payloads * @param secret the secret to encrypt to token string * @param seconds the expiration of the token in seconds * @param oid the ID of the token (could be customer ID etc) * @param payload the payload optionally indicate more information * @return an encrypted token string that is expiring in {@link Life#SHORT} time period */ public static String generateToken(byte[] secret, long seconds, String oid, String... payload) { long due = Life.due(seconds); List l = new ArrayList(2 + payload.length); l.add(oid); l.add(String.valueOf(due)); l.addAll(C.listOf(payload)); String s = S.join("|", l); return Crypto.encryptAES(s, secret); } /** * This method is deprecated. Please use {@link #parseToken(byte[], String)} instead * * Parse a token string into token object * @param token the token string * @param secret the secret to decrypt the token string * @return a token instance parsed from the string */ @Deprecated public static Token parseToken(String secret, String token) { return parseToken(secret.getBytes(Charsets.UTF_8), token); } /** * Parse a token string into token object * @param token the token string * @param secret the secret to decrypt the token string * @return a token instance parsed from the string */ public static Token parseToken(byte[] secret, String token) { Token tk = new Token(); if (S.blank(token)) return tk; String s = ""; try { s = Crypto.decryptAES(token, secret); } catch (Exception e) { return tk; } String[] sa = s.split("\\|"); if (sa.length < 2) return tk; tk.id = sa[0]; try { tk.due = Long.parseLong(sa[1]); if (tk.expired()) { return tk; } } catch (Exception e) { tk.due = $.ms() - 1000 * 60 * 60 * 24; return tk; } if (sa.length > 2) { sa = Arrays.copyOfRange(sa, 2, sa.length); tk.payload.addAll(C.listOf(sa)); } return tk; } /** * This method is deprecated. Please use {@link #isTokenValid(byte[], String, String)} instead * * Check if a string is a valid token * @param secret the secret to decrypt the string * @param oid the ID supposed to be encapsulated in the token * @param token the token string * @return {@code true} if the token is valid */ @Deprecated @SuppressWarnings("unused") public static boolean isTokenValid(String secret, String oid, String token) { return isTokenValid(secret.getBytes(Charsets.UTF_8), oid, token); } /** * Check if a string is a valid token * @param secret the secret to decrypt the string * @param oid the ID supposed to be encapsulated in the token * @param token the token string * @return {@code true} if the token is valid */ @SuppressWarnings("unused") public static boolean isTokenValid(byte[] secret, String oid, String token) { if (S.anyBlank(oid, token)) { return false; } String s = Crypto.decryptAES(token, secret); String[] sa = s.split("\\|"); if (sa.length < 2) return false; if (!S.isEqual(oid, sa[0])) return false; try { long due = Long.parseLong(sa[1]); return (due < 1 || due > System.currentTimeMillis()); } catch (Exception e) { return false; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy