1 module appbase.security.token; 2 3 import core.thread; 4 5 import std.bitmanip; 6 import std.exception : enforce; 7 import std.datetime; 8 import std.concurrency; 9 import std.base64; 10 11 import crypto.rsa; 12 13 import appbase.utils.utility; 14 15 class Token 16 { 17 static ubyte[] generate(ulong userId, RSAKeyInfo publicKey) 18 { 19 ubyte[] token = new ubyte[ulong.sizeof + long.sizeof]; 20 token.write!ulong(userId, 0); 21 long ticks = currTimeTick(); 22 token.write!long(ticks, ulong.sizeof); 23 token ~= strToByte_hex(MD5(token)[0 .. 4]); 24 token = RSA.encrypt(publicKey, token); 25 26 insertCache(userId, token, ticks); 27 28 return token; 29 } 30 31 static bool check(ulong userId, string token, RSAKeyInfo privateKey, const int tokenExpire) 32 { 33 scope(failure) { return false; } 34 35 if (!cleanTaskRunning) 36 { 37 synchronized(Token.classinfo) 38 { 39 if (!cleanTaskRunning) 40 { 41 spawn(&cleanTask, tokenExpire); 42 cleanTaskRunning = true; 43 } 44 } 45 } 46 47 ubyte[] _token = Base64.decode(token); 48 49 if ((userId in pool) && (pool[userId].token == _token)) 50 { 51 return true; 52 } 53 54 ubyte[] data = RSA.decrypt(privateKey, _token); 55 56 if (data.length < ulong.sizeof + long.sizeof + 2) 57 { 58 return false; 59 } 60 61 if (strToByte_hex(MD5(data[0 .. $ - 2])[0 .. 4]) != data[$ - 2 .. $]) 62 { 63 return false; 64 } 65 66 ulong _id = data.peek!ulong(0); 67 if (_id != userId) 68 { 69 return false; 70 } 71 72 long ticks = data.peek!long(ulong.sizeof); 73 if ((Clock.currTime() - currTimeFromTick(ticks)).total!"minutes" > tokenExpire) 74 { 75 return false; 76 } 77 78 insertCache(userId, _token, ticks); 79 80 return true; 81 } 82 83 static string getTokenInPool(ulong userId) 84 { 85 if (userId !in pool) 86 { 87 return string.init; 88 } 89 90 return Base64.encode(pool[userId].token); 91 } 92 93 private: 94 95 static void insertCache(ulong userId, in ubyte[] token, long ticks) 96 { 97 CacheTokenData cache; 98 cache.token = token.dup; 99 cache.ticks = ticks; 100 pool[userId] = cache; 101 } 102 103 package: 104 105 __gshared static CacheTokenData[ulong] pool; 106 __gshared static bool cleanTaskRunning = false; 107 } 108 109 private: 110 111 struct CacheTokenData 112 { 113 ubyte[] token; 114 long ticks; 115 } 116 117 void cleanTask(const int tokenExpire) 118 { 119 while (true) 120 { 121 Thread.sleep(1.hours); 122 123 synchronized(Token.classinfo) 124 { 125 foreach(userId, data; Token.pool) 126 { 127 if ((Clock.currTime() - currTimeFromTick(data.ticks)).total!"minutes" > tokenExpire) 128 { 129 Token.pool.remove(userId); 130 } 131 } 132 } 133 } 134 }