微軟利用OAuth2為RESTful API提供了完整的鑒權機制,但是可能微軟保姆做的太完整了,在這個機制中指定了數據持久化的方法是用EF,而且對於用戶、許可權等已經進行了封裝,對於系統中已經有了自己的用戶表,和不是採用EF做持久化的系統來說限制太大,不自由,而且現實中很多情況下,授權伺服器和資源服務 ...
微軟利用OAuth2為RESTful API提供了完整的鑒權機制,但是可能微軟保姆做的太完整了,在這個機制中指定了數據持久化的方法是用EF,而且對於用戶、許可權等已經進行了封裝,對於系統中已經有了自己的用戶表,和不是採用EF做持久化的系統來說限制太大,不自由,而且現實中很多情況下,授權伺服器和資源伺服器並不是同一個伺服器,甚至資料庫用的都不是同一個資料庫,這種情況下直接利用微軟的授權機制會有一定的麻煩,基於這種情況不如自己實現一套完整的分散式的鑒權機制。
自定義的鑒權機制中利用redis來緩存授權令牌信息,結構大體如下:
從圖上可以看出,用戶登錄成功後的用戶信息緩存在redis,用戶帶著令牌訪問資源伺服器時再對令牌進行解密,獲取到key,然後就可以獲取到用戶信息,可以根據判斷redis中是否有這個key來校驗用戶是否經過授權,大體思路就是這些,下邊就是具體的代碼實現
redis的操作幫助類,幫助類中實現了redis的讀寫分離
public class RedisBase { #if DEBUG private static string[] ReadWriteHosts = { "127.0.0.1:6379" }; private static string[] ReadOnlyHosts = { "127.0.0.1:6379" }; #endif #if !DEBUG private static string[] ReadWriteHosts = System.Configuration.ConfigurationSettings.AppSettings["RedisWriteHosts"].Split(new char[] { ';' }); private static string[] ReadOnlyHosts = System.Configuration.ConfigurationSettings.AppSettings["RedisReadOnlyHosts"].Split(new char[] { ';' }); #endif #region -- 連接信息 -- public static PooledRedisClientManager prcm = CreateManager(ReadWriteHosts, ReadOnlyHosts); private static PooledRedisClientManager CreateManager(string[] readWriteHosts, string[] readOnlyHosts) { // 支持讀寫分離,均衡負載 return new PooledRedisClientManager(readWriteHosts, readOnlyHosts, new RedisClientManagerConfig { MaxWritePoolSize = 5, // “寫”鏈接池鏈接數 MaxReadPoolSize = 5, // “讀”鏈接池鏈接數 AutoStart = true, }); } #endregion #region KEY #endregion #region -- Item String -- /// <summary> /// 設置單體 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="t"></param> /// <param name="timeSpan"></param> /// <returns></returns> public static bool Item_Set<T>(string key, T t) { try { using (IRedisClient redis = prcm.GetClient()) { return redis.Set<T>(key, t, new TimeSpan(1, 0, 0)); } } catch (Exception ex) { // LogInfo } return false; } public static int String_Append(string key, string value) { try { using (IRedisClient redis = prcm.GetClient()) { return redis.AppendToValue(key, value); } } catch (Exception ex) { } return 0; } /// <summary> /// 獲取單體 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static T Item_Get<T>(string key) where T : class { using (IRedisClient redis = prcm.GetReadOnlyClient()) { return redis.Get<T>(key); } } /// <summary> /// 移除單體 /// </summary> /// <param name="key"></param> public static bool Item_Remove(string key) { using (IRedisClient redis = prcm.GetClient()) { return redis.Remove(key); } } /// <summary> /// 根據指定的Key,將值加1(僅整型有效) /// </summary> /// <param name="key"></param> /// <returns></returns> public static long IncrementValue(string key) { using (IRedisClient redis = prcm.GetClient()) { return redis.IncrementValue(key); } } /// <summary> /// 根據指定的Key,將值加上指定值(僅整型有效) /// </summary> /// <param name="key"></param> /// <param name="count"></param> /// <returns></returns> public static long IncrementValueBy(string key,int count) { using (IRedisClient redis = prcm.GetClient()) { return redis.IncrementValueBy(key,count); } } /// <summary> /// 設置過期時間 /// </summary> /// <param name="key"></param> /// <param name="time"></param> /// <returns></returns> public static bool ExpireEntryIn(string key, TimeSpan time) { using (IRedisClient redis = prcm.GetClient()) { return redis.ExpireEntryIn(key, time); } } /// <summary> /// 設置過期時間 /// </summary> /// <param name="key"></param> /// <param name="expireAt"></param> /// <returns></returns> public static bool ExpireEntryAt(string key, DateTime expireAt) { using (IRedisClient redis = prcm.GetClient()) { return redis.ExpireEntryAt(key, expireAt); } } /// <summary> /// 判斷key是否已存在 /// </summary> /// <param name="key"></param> /// <returns></returns> public static bool ContainsKey(string key) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { return redis.ContainsKey(key); } } #endregion #region -- List -- public static void List_Add<T>(string key, T t) { using (IRedisClient redis = prcm.GetClient()) { var redisTypedClient = redis.As<T>(); redisTypedClient.AddItemToList(redisTypedClient.Lists[key], t); } } public static bool List_Remove<T>(string key, T t) { using (IRedisClient redis = prcm.GetClient()) { var redisTypedClient = redis.As<T>(); return redisTypedClient.RemoveItemFromList(redisTypedClient.Lists[key], t) > 0; } } public static void List_RemoveAll<T>(string key) { using (IRedisClient redis = prcm.GetClient()) { var redisTypedClient = redis.As<T>(); redisTypedClient.Lists[key].RemoveAll(); } } public static long List_Count(string key) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { return redis.GetListCount(key); } } public static List<T> List_GetRange<T>(string key, int start, int count) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { var c = redis.As<T>(); return c.Lists[key].GetRange(start, start + count - 1); } } public static List<T> List_GetList<T>(string key) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { var c = redis.As<T>(); return c.Lists[key].GetRange(0, c.Lists[key].Count); } } public static List<T> List_GetList<T>(string key, int pageIndex, int pageSize) { int start = pageSize * (pageIndex - 1); return List_GetRange<T>(key, start, pageSize); } /// <summary> /// 設置緩存過期 /// </summary> /// <param name="key"></param> /// <param name="datetime"></param> public static void List_SetExpire(string key, DateTime datetime) { using (IRedisClient redis = prcm.GetClient()) { redis.ExpireEntryAt(key, datetime); } } #endregion #region -- Set -- public static void Set_Add<T>(string key, T t) { using (IRedisClient redis = prcm.GetClient()) { var redisTypedClient = redis.As<T>(); redisTypedClient.Sets[key].Add(t); } } public static bool Set_Contains<T>(string key, T t) { using (IRedisClient redis = prcm.GetClient()) { var redisTypedClient = redis.As<T>(); return redisTypedClient.Sets[key].Contains(t); } } public static bool Set_Remove<T>(string key, T t) { using (IRedisClient redis = prcm.GetClient()) { var redisTypedClient = redis.As<T>(); return redisTypedClient.Sets[key].Remove(t); } } #endregion #region -- Hash -- /// <summary> /// 判斷某個數據是否已經被緩存 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="dataKey"></param> /// <returns></returns> public static bool Hash_Exist<T>(string key, string dataKey) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { return redis.HashContainsEntry(key, dataKey); } } /// <summary> /// 存儲數據到hash表 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="dataKey"></param> /// <returns></returns> public static bool Hash_Set<T>(string key, string dataKey, T t) { using (IRedisClient redis = prcm.GetClient()) { string value = ServiceStack.Text.JsonSerializer.SerializeToString<T>(t); return redis.SetEntryInHash(key, dataKey, value); } } /// <summary> /// 移除hash中的某值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="dataKey"></param> /// <returns></returns> public static bool Hash_Remove(string key, string dataKey) { using (IRedisClient redis = prcm.GetClient()) { return redis.RemoveEntryFromHash(key, dataKey); } } /// <summary> /// 移除整個hash /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="dataKey"></param> /// <returns></returns> public static bool Hash_Remove(string key) { using (IRedisClient redis = prcm.GetClient()) { return redis.Remove(key); } } /// <summary> /// 從hash表獲取數據 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="dataKey"></param> /// <returns></returns> public static T Hash_Get<T>(string key, string dataKey) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { string value = redis.GetValueFromHash(key, dataKey); return ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(value); } } /// <summary> /// 獲取整個hash的數據 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static List<T> Hash_GetAll<T>(string key) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { var list = redis.GetHashValues(key); if (list != null && list.Count > 0) { List<T> result = new List<T>(); foreach (var item in list) { var value = ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(item); result.Add(value); } return result; } return null; } } /// <summary> /// 設置緩存過期 /// </summary> /// <param name="key"></param> /// <param name="datetime"></param> public static void Hash_SetExpire(string key, DateTime datetime) { using (IRedisClient redis = prcm.GetClient()) { redis.ExpireEntryAt(key, datetime); } } #endregion #region -- SortedSet -- /// <summary> /// 添加數據到 SortedSet /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="t"></param> /// <param name="score"></param> public static bool SortedSet_Add<T>(string key, T t, double score) { using (IRedisClient redis = prcm.GetClient()) { string value = ServiceStack.Text.JsonSerializer.SerializeToString<T>(t); return redis.AddItemToSortedSet(key, value, score); } } /// <summary> /// 為有序集 key 的成員 member 的 score 值加上增量 increment /// 可以通過傳遞一個負數值 increment ,讓 score 減去相應的值 /// 當 key 不存在,或 member 不是 key 的成員時, ZINCRBY key increment member 等同於 ZADD key increment member /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="t"></param> /// <param name="incrementBy"></param> /// <returns></returns> public static double SortedSet_Zincrby<T>(string key,T t,double incrementBy) { using (IRedisClient redis = prcm.GetClient()) { string value = ServiceStack.Text.JsonSerializer.SerializeToString<T>(t); return redis.IncrementItemInSortedSet(key, value, incrementBy); } } /// <summary> /// 返回有序集 key 中,成員 member 的 score 值。 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="t">member</param> /// <returns></returns> public static double SortedSet_ZSCORE<T>(string key,T t) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { string value = ServiceStack.Text.JsonSerializer.SerializeToString<T>(t); return redis.GetItemScoreInSortedSet(key, value); } } /// <summary> /// 移除數據從SortedSet /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="t"></param> /// <returns></returns> public static bool SortedSet_Remove<T>(string key, T t) { using (IRedisClient redis = prcm.GetClient()) { string value = ServiceStack.Text.JsonSerializer.SerializeToString<T>(t); return redis.RemoveItemFromSortedSet(key, value); } } /// <summary> /// 修剪SortedSet /// </summary> /// <param name="key"></param> /// <param name="size">保留的條數</param> /// <returns></returns> public static long SortedSet_Trim(string key, int size) { using (IRedisClient redis = prcm.GetClient()) { return redis.RemoveRangeFromSortedSet(key, size, 9999999); } } /// <summary> /// 獲取SortedSet的長度 /// </summary> /// <param name="key"></param> /// <returns></returns> public static long SortedSet_Count(string key) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { return redis.GetSortedSetCount(key); } } /// <summary> /// 按照由小到大排序獲取SortedSet的分頁數據 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="pageIndex"></param> /// <param name="pageSize"></param> /// <returns></returns> public static List<T> SortedSet_GetList<T>(string key, int pageIndex, int pageSize) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { var list = redis.GetRangeFromSortedSet(key, (pageIndex - 1) * pageSize, pageIndex * pageSize - 1); if (list != null && list.Count > 0) { List<T> result = new List<T>(); foreach (var item in list) { var data = ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(item); result.Add(data); } return result; } } return null; } /// <summary> /// 按照由大到小順序獲取SortedSet的分頁數據 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="pageIndex"></param> /// <param name="pageSize"></param> /// <returns></returns> public static List<T> SortedSet_GetListDesc<T>(string key, int pageIndex, int pageSize) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { var list = redis.GetRangeFromSortedSetDesc(key, (pageIndex - 1) * pageSize, pageIndex * pageSize - 1); if (list != null && list.Count > 0) { List<T> result = new List<T>(); foreach (var item in list) { var data = ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(item); result.Add(data); } return result; } } return null; } /// <summary> /// 按照由大到小的順序獲取SortedSet包含分數的分頁數據 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="pageIndex"></param> /// <param name="pageSize"></param> /// <returns></returns> public static IDictionary<T,double> SortedSet_GetListWidthScoreDesc<T>(string key, int pageIndex, int pageSize) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { var list = redis.GetRangeWithScoresFromSortedSetDesc(key, (pageIndex - 1) * pageSize, pageIndex * pageSize - 1); if (list != null && list.Count > 0) { IDictionary<T,double> result = new Dictionary<T,double>(); foreach (var item in list) { var data = ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(item.Key); result[data]=item.Value; } return result; } } return null; } /// <summary> /// 獲取SortedSet的全部數據 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="pageIndex"></param> /// <param name="pageSize"></param> /// <returns></returns> public static List<T> SortedSet_GetListALL<T>(string key) { using (IRedisClient redis = prcm.GetReadOnlyClient()) { var list = redis.GetRangeFromSortedSet(key, 0, 9999999); if (list != null && list.Count > 0) { List<T> result = new List<T>(); foreach (var item in list) { var data = ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(item); result.Add(data); } return result; } } return null; } /// <summary> /// 設置緩存過期 /// </summary> /// <param name="key"></param> /// <param name="datetime"></param> public static void SortedSet_SetExpire(string key, DateTime datetime) { using (IRedisClient redis = prcm.GetClient()) { redis.ExpireEntryAt(key, datetime); } } #endregion }
用戶類UserInfo
public class UserInfo { }
用戶管理類,在asp.net web api中添加一個用戶管理類,用來管理用戶信息
public class ApiUserManager { private HttpActionContext actionContext; public ApiUserManager(HttpActionContext actionContext) { this.actionContext = actionContext; } private UserInfo _User; /// <summary> /// 當前用戶 /// </summary> public UserInfo User { get { if (_User==null) { string key = GetKey(); if (!string.IsNullOrEmpty(key) && RedisBase.ContainsKey(key)) { _User = RedisBase.Item_Get<UserInfo>(key); } } return _User; } } string GetKey() { if (actionContext.Request.Headers.Contains("Authorization")) { string base64Code = actionContext.Request.Headers.GetValues("Authorization").FirstOrDefault(); //code結構為:userid-UserAgent.MD5()-隨機數-時間戳 string code = EncryptUtil.UnBase64(base64Code); string[] para = code.Split(new[] { "-" }, StringSplitOptions.RemoveEmptyEntries); string key = (para[0] + para[1] + para[3]).MD5(); return key; } return string.Empty; } /// <summary> /// 用戶是否已經登錄 /// </summary> /// <returns></returns> public bool ExistsLogin() { string base64Code = string.Empty; if (actionContext.Request.Headers.Contains("Authorization")) { base64Code = actionContext.Request.Headers.GetValues("Authorization").FirstOrDefault(); } if (base64Code.IsNull()) { return false; } //code結構為:userid-UserAgent.MD5()-隨機數-時間戳 string code = EncryptUtil.UnBase64(base64Code); string[] para = code.Split(new[] { "-" }, StringSplitOptions.RemoveEmptyEntries); if (para.Length != 4) { return false; } string key = (para[0] + para[1] + para[3]).MD5(); if (!RedisBase.ContainsKey(key)) { return false; } return true; } /// <summary> /// 用戶登錄返回令牌 /// </summary> /// <param name="user"></param> /// <returns></returns> public string GetUserToken(UserInfo user) { string uagin = actionContext.Request.Headers.UserAgent.TryToString().MD5(); string rm = Utils.GenPsw(11,11); long time = Utils.GetUnixTime(); string code = string.Format(<