|
|
@@ -0,0 +1,366 @@
|
|
|
+package com.zhiqiyun.open.utils;
|
|
|
+
|
|
|
+
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+
|
|
|
+import java.lang.management.ManagementFactory;
|
|
|
+import java.net.NetworkInterface;
|
|
|
+import java.net.SocketException;
|
|
|
+import java.util.Enumeration;
|
|
|
+import java.util.Random;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+
|
|
|
+/**
|
|
|
+ * =============================================================================
|
|
|
+ * ==== name bit Byte number len max-len
|
|
|
+ * -----------------------------------------------------------------------------
|
|
|
+ * ---- mac 1 48 6 281,474,976,710,655 mac 2 49 562,949,953,421,311 9
|
|
|
+ * 11,694,146,092,834,140 ------ MAC RAN(12) * E15 + mac
|
|
|
+ * -----------------------------------------------------------------------------
|
|
|
+ * ---- time 0 13,054,752,000 (from 2011.05.16 100ms) time 1 28,833,984,000
|
|
|
+ * (from 2061.05.16 100ms) 7 3,142,742,836,021 ------ TIME RAN(32) * E11 + time
|
|
|
+ * -----------------------------------------------------------------------------
|
|
|
+ * ---- pid 16 2 65535 3 226,980 ------ PID pid
|
|
|
+ * -----------------------------------------------------------------------------
|
|
|
+ * ---- seq 26 67,108,864 5 844,596,300 ------ SEQ RAN(8) * E8 + seq
|
|
|
+ * -----------------------------------------------------------------------------
|
|
|
+ * ---- ip 32 4 4,294,967,296 6 51,520,374,361 ------ IP RAN(5) * E10 + ip
|
|
|
+ * =============================================================================
|
|
|
+ * ==== *
|
|
|
+ *
|
|
|
+ * @author jtoms
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+public class Token {
|
|
|
+
|
|
|
+ private final static char[] BASE61 = "3ibqZmxNpXOjlHIPQVSTUhWCBgFDctuYAzKd1fenyE6957rwMaLJ2kR4vso8G".toCharArray();
|
|
|
+ private final static int LEN = BASE61.length;
|
|
|
+ private final static int DATA_LENGTH = 30;
|
|
|
+ private final static int TOKEN_LENGTH = 33;
|
|
|
+ private final static int[] BASE61_MAPPING = {32, 24, 23, 27, 41, 26, 60, 13, 14, 51, 34, 50, 48, 7, 10, 15, 16, 54, 18, 19, 20, 17, 22, 9, 31, 4, 49, 2, 28, 35, 38, 37, 25,
|
|
|
+ 21, 1, 11, 53, 12, 5, 39, 58, 8, 3, 46, 57, 29, 30, 56, 47, 6, 40, 33, 36, 52, 0, 55, 44, 42, 45, 59, 43};
|
|
|
+
|
|
|
+ private final static long MAC_ADDR = mac();
|
|
|
+
|
|
|
+ private final static int PID = Integer.parseInt(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
|
|
|
+
|
|
|
+ private final static AtomicInteger SEQ = new AtomicInteger((int) (System.currentTimeMillis() / 1000));
|
|
|
+
|
|
|
+ private final static AtomicInteger[] SEN = {new AtomicInteger((int) (System.nanoTime())), new AtomicInteger((int) (System.nanoTime())),
|
|
|
+ new AtomicInteger((int) (System.nanoTime())), new AtomicInteger((int) (System.nanoTime())), new AtomicInteger((int) (System.nanoTime()))};
|
|
|
+
|
|
|
+ private long mac;
|
|
|
+
|
|
|
+ private int pid;
|
|
|
+
|
|
|
+ private int sequence;
|
|
|
+
|
|
|
+ private long tokenTime;
|
|
|
+
|
|
|
+ private String clientIp;
|
|
|
+
|
|
|
+
|
|
|
+ public Token() {
|
|
|
+ }
|
|
|
+
|
|
|
+ public Token(String ipAddr) {
|
|
|
+ this.mac = MAC_ADDR;
|
|
|
+ this.tokenTime = System.currentTimeMillis() / 100;
|
|
|
+ this.pid = PID;
|
|
|
+ this.clientIp = ipAddr;
|
|
|
+ this.sequence = SEQ.incrementAndGet() & 0x3ffffff;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Token(long mac, long tokenTime, int pid, int sequence, String clientIp) {
|
|
|
+ super();
|
|
|
+ this.mac = mac;
|
|
|
+ this.tokenTime = tokenTime;
|
|
|
+ this.pid = pid;
|
|
|
+ this.sequence = sequence;
|
|
|
+ this.clientIp = clientIp;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Token init(String ipAddr) {
|
|
|
+ return new Token(ipAddr);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static boolean checkToken(String value) {
|
|
|
+ if (value == null || value.length() != Token.TOKEN_LENGTH) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ char[] chs = value.toCharArray();
|
|
|
+ for (int i = 0; i < TOKEN_LENGTH; i++) {
|
|
|
+ if (getNumber(chs[i]) < 0) {
|
|
|
+ log.warn("value index [" + i + "] is invalid encode character [" + chs[i] + "], token: [" + value + "]");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ char[] hash = hash(chs, 0, DATA_LENGTH, TOKEN_LENGTH - DATA_LENGTH);
|
|
|
+ for (int i = DATA_LENGTH, j = 0; i < chs.length; i++, j++) {
|
|
|
+ if (chs[i] != hash[j]) {
|
|
|
+ log.warn("value hash is invalid, value hash: [" + new String(chs, DATA_LENGTH, TOKEN_LENGTH - DATA_LENGTH) + "], calculate hash: [" + new String(hash)
|
|
|
+ + "], token: [" + value + "]");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Token decodeTokenString(String value) {
|
|
|
+ if (log.isDebugEnabled()) {
|
|
|
+ log.debug("start check TOKEN_ID: " + value);
|
|
|
+ }
|
|
|
+ if (!Token.checkToken(value)) {
|
|
|
+ log.warn("check token failed");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ char[] chs = value.toCharArray();
|
|
|
+ int offset = 0;
|
|
|
+ long mac = Token.decodeNode(chs, offset, 9, 1000000000000000L);
|
|
|
+ if (mac < 0) {
|
|
|
+ log.warn("token mac is invalid, mac: [" + new String(chs, 0, 9) + "]token: [" + value + "]");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ long tokenTime = Token.decodeNode(chs, (offset += 9), 7, 100000000000L);
|
|
|
+ if (tokenTime < 0) {
|
|
|
+ log.warn("token time is invalid, tokenTime: [" + new String(chs, offset, 7) + "]token: [" + value + "]");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ int pid = (int) Token.decodeNode(chs, (offset += 7), 3, 0);
|
|
|
+ if (pid < 0 || pid > 226980) {
|
|
|
+ log.warn("token pid is invalid, pid: [" + new String(chs, offset, 3) + "], token: [" + value + "]");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ int sequence = (int) Token.decodeNode(chs, (offset += 3), 5, 100000000L);
|
|
|
+ if (sequence < 0) {
|
|
|
+ log.warn("token sequence is invalid, sequence: [" + new String(chs, offset, 5) + "], token: " + value);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ long clientIp = Token.decodeNode(chs, (offset += 5), 6, 10000000000L);
|
|
|
+ if (clientIp < 0 || clientIp > 0xffffffffL) {
|
|
|
+ log.warn("token clientIp is invalid, clientIp: [" + new String(chs, offset, 6) + "], number client ip: [" + Long.toHexString(clientIp) + "], token: " + value);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return new Token(mac, tokenTime, pid, sequence, Token.toIpAddr(clientIp));
|
|
|
+ }
|
|
|
+
|
|
|
+ static char[] hash(char[] chs, int offset, int length, int hashLength) {
|
|
|
+ char[] hash = new char[hashLength];
|
|
|
+ int p = 0;
|
|
|
+ for (int i = offset, k = offset + length; i < k; i++) {
|
|
|
+ p = p * 31 + chs[i];
|
|
|
+ }
|
|
|
+ p &= Integer.MAX_VALUE;
|
|
|
+ for (int i = 0; i < hashLength; i++, p /= LEN) {
|
|
|
+ hash[i] = BASE61[p % LEN];
|
|
|
+ }
|
|
|
+ return hash;
|
|
|
+ }
|
|
|
+
|
|
|
+ static String encode(long n, int max) {
|
|
|
+ char[] chs = new char[max];
|
|
|
+ int k = 0;
|
|
|
+ while (n > 0) {
|
|
|
+ chs[k++] = BASE61[(int) (n % LEN)];
|
|
|
+ n /= LEN;
|
|
|
+ }
|
|
|
+ while (k < max) {
|
|
|
+ chs[k++] = BASE61[0];
|
|
|
+ }
|
|
|
+ return new String(chs);
|
|
|
+ }
|
|
|
+
|
|
|
+ static Token parse(String token) {
|
|
|
+ if (token == null || token.length() != TOKEN_LENGTH) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ char[] chs = token.toCharArray();
|
|
|
+ for (int i = 0; i < TOKEN_LENGTH; i++) {
|
|
|
+ if (getNumber(chs[i]) < 0) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ char[] hash = hash(chs, 0, DATA_LENGTH, TOKEN_LENGTH - DATA_LENGTH);
|
|
|
+ for (int i = DATA_LENGTH, j = 0; i < chs.length; i++, j++) {
|
|
|
+ if (chs[i] != hash[j]) {
|
|
|
+ log.warn("token: " + token + " hash error");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return parsetTokenString(chs);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int next(int mod, int offset) {
|
|
|
+ return (SEN[offset].incrementAndGet() & 0x7fffffff) % mod;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Token parsetTokenString(char[] chs) {
|
|
|
+ Token token = new Token();
|
|
|
+ int offset = 0;
|
|
|
+ token.mac = decodeNode(chs, offset, 9, 1000000000000000L);
|
|
|
+ token.tokenTime = (long) decodeNode(chs, (offset += 9), 7, 100000000000L);
|
|
|
+ token.pid = (int) decodeNode(chs, (offset += 7), 3, 0);
|
|
|
+ token.sequence = (int) decodeNode(chs, (offset += 3), 5, 100000000L);
|
|
|
+ token.clientIp = toIpAddr(decodeNode(chs, (offset += 5), 6, 10000000000L));
|
|
|
+ return token;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int encodeNode(char[] chs, int ran, long value, int offset, int len) {
|
|
|
+ return encodeNode(chs, ran, value, offset, len, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int encodeNode(char[] chs, int ran, long value, int offset, int len, int random, long randomBase) {
|
|
|
+ if (random > 0) {
|
|
|
+ value = (long) next(random, ran) * randomBase + value;
|
|
|
+ }
|
|
|
+ for (int i = 0; i < len; i++) {
|
|
|
+ chs[offset + i] = BASE61[(int) (value % LEN)];
|
|
|
+ value /= LEN;
|
|
|
+ }
|
|
|
+ return offset + len;
|
|
|
+ }
|
|
|
+
|
|
|
+ static long decode(String str) {
|
|
|
+ char[] chs = str.toCharArray();
|
|
|
+ long p = 0;
|
|
|
+ for (int i = chs.length - 1; i >= 0; i--) {
|
|
|
+ int k = getNumber(chs[i]);
|
|
|
+ if (k < 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ p = p * LEN + k;
|
|
|
+ }
|
|
|
+ return p;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static long decodeNode(char[] chs, int offset, int len, long randomBase) {
|
|
|
+ long n = 0;
|
|
|
+ for (int i = offset + len - 1; i >= offset; i--) {
|
|
|
+ int k = getNumber(chs[i]);
|
|
|
+ if (k < 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ n = n * LEN + k;
|
|
|
+ }
|
|
|
+ return randomBase < 1 ? n : n % randomBase;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static long parseIp(String ip) {
|
|
|
+ char[] chs = ip.toCharArray();
|
|
|
+ long t = 0;
|
|
|
+ int n = 0;
|
|
|
+ for (int i = 0; i < chs.length; i++) {
|
|
|
+ if (chs[i] == '.') {
|
|
|
+ t = (t << 8) | (n & 0xff);
|
|
|
+ n = 0;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (chs[i] >= '0' && chs[i] <= '9') {
|
|
|
+ n = n * 10 + (chs[i] - '0');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (n != 0) {
|
|
|
+ t = (t << 8) + (n & 0xff);
|
|
|
+ }
|
|
|
+ return t & 0xffffffffL;
|
|
|
+ }
|
|
|
+
|
|
|
+ static int getNumber(char c) {
|
|
|
+ if (c >= 'A' && c <= 'Z') {
|
|
|
+ return BASE61_MAPPING[c - 'A'];
|
|
|
+ }
|
|
|
+ if (c >= 'a' && c <= 'z') {
|
|
|
+ return BASE61_MAPPING[c - 'a' + 26];
|
|
|
+ }
|
|
|
+ if (c >= '1' && c <= '9') {
|
|
|
+ return BASE61_MAPPING[c - '1' + 52];
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static long mac() {
|
|
|
+ byte[] bys = null;
|
|
|
+ try {
|
|
|
+ for (Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) {
|
|
|
+ byte[] mac = e.nextElement().getHardwareAddress();
|
|
|
+ if (mac != null && mac.length == 6) {
|
|
|
+ bys = mac;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (SocketException e) {
|
|
|
+ log.error("can not get local machine MAC address, generate random 47bit simulate MAC", e);
|
|
|
+ }
|
|
|
+ if (bys == null) {
|
|
|
+ bys = new byte[7];
|
|
|
+ new Random().nextBytes(bys);
|
|
|
+ bys[0] &= 1;
|
|
|
+ }
|
|
|
+ long mac = 0;
|
|
|
+ for (int i = 0; i < bys.length; i++) {
|
|
|
+ mac = mac * 256L + (bys[i] & 0xff);
|
|
|
+ }
|
|
|
+ mac &= 0x1ffffffffffffL;
|
|
|
+ log.info("current MAC number is: " + mac + ", HEX: " + Long.toHexString(mac));
|
|
|
+ return mac;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String toIpAddr(long ip) {
|
|
|
+ return ((ip >> 24) & 0xff) + "." + ((ip >> 16) & 0xff) + "." + ((ip >> 8) & 0xff) + "." + (ip & 0xff);
|
|
|
+ }
|
|
|
+
|
|
|
+ public long getMac() {
|
|
|
+ return mac;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getPid() {
|
|
|
+ return pid;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getSequence() {
|
|
|
+ return sequence;
|
|
|
+ }
|
|
|
+
|
|
|
+ public long getTokenTime() {
|
|
|
+ return tokenTime * 100L;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getClientIp() {
|
|
|
+ return clientIp;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String toTokenString() {
|
|
|
+ int offset = 0;
|
|
|
+ char[] chs = new char[TOKEN_LENGTH];
|
|
|
+ int p = 0;
|
|
|
+ offset = encodeNode(chs, p++, mac, offset, 9, 12, 1000000000000000L);
|
|
|
+ offset = encodeNode(chs, p++, tokenTime, offset, 7, 32, 100000000000L);
|
|
|
+ offset = encodeNode(chs, p++, pid, offset, 3);
|
|
|
+ offset = encodeNode(chs, p++, sequence, offset, 5, 8, 100000000L);
|
|
|
+ offset = encodeNode(chs, p++, parseIp(clientIp), offset, 6, 5, 10000000000L);
|
|
|
+ char[] hash = hash(chs, 0, offset, TOKEN_LENGTH - DATA_LENGTH);
|
|
|
+ System.arraycopy(hash, 0, chs, offset, hash.length);
|
|
|
+ return new String(chs);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return "Token [mac=" + mac + ", tokenTime=" + String.format("%tF %<tT", getTokenTime()) + ", pid=" + pid + ", sequence=" + sequence + ", clientIp=" + clientIp + "]";
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean equals(Token token) {
|
|
|
+ if (!this.getClientIp().equals(token.getClientIp())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (this.getMac() != token.getMac()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (this.getPid() != token.getPid()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (this.getSequence() != token.getSequence()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+}
|