微软利用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 = , // “写”链接池链接数
MaxReadPoolSize = , // “读”链接池链接数
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(, , ));
}
}
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 ;
}
/// <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) > ;
}
}
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 - );
}
} public static List<T> List_GetList<T>(string key)
{
using (IRedisClient redis = prcm.GetReadOnlyClient())
{
var c = redis.As<T>();
return c.Lists[key].GetRange(, c.Lists[key].Count);
}
} public static List<T> List_GetList<T>(string key, int pageIndex, int pageSize)
{
int start = pageSize * (pageIndex - );
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 > )
{
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, );
}
}
/// <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 - ) * pageSize, pageIndex * pageSize - );
if (list != null && list.Count > )
{
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 - ) * pageSize, pageIndex * pageSize - );
if (list != null && list.Count > )
{
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 - ) * pageSize, pageIndex * pageSize - );
if (list != null && list.Count > )
{
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, , );
if (list != null && list.Count > )
{
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[] + para[] + para[]).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 != )
{
return false;
}
string key = (para[] + para[] + para[]).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(,);
long time = Utils.GetUnixTime();
string code = string.Format("{0}-{1}-{2}-{3}", user.ID, uagin, rm, time);
string token = EncryptUtil.Base64(code);
string key = (user.ID + uagin + time).MD5();
RedisBase.Item_Set(key,user);
RedisBase.ExpireEntryAt(key,DateTime.Now.AddDays());
return token;
}
/// <summary>
/// 刷新当前用户信息【修改用户信息后刷新用户信息到缓存中】
/// </summary>
public void RefreshUser()
{
string key = GetKey();
if (RedisBase.ContainsKey(key))
{
RedisBase.Item_Set(key,User);
}
}
}

获取授权接口【用户登录接口】,在授权接口中校验用户名密码,成功后将用户信息存入redis,产生令牌,并返回

public class AccountController : ApiController
{
UserInfoBll ubll = new UserInfoBll();
/// <summary>
/// 登录、根据用户名密码获取令牌
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns></returns>
[HttpGet]
[Route("token")]
public JsonResult<string> Token(string username, string password)
{
JsonResult<string> result = new JsonResult<string>();
result.code = ;
result.msg = "OK";
UserInfo user = ubll.UserLogin(username, password);
if (user == null)
{
result.Result = "用户名或者密码错误";
}
else
{
ApiUserManager userManager = new ApiUserManager(ActionContext);
result.Result = userManager.GetUserToken(user);
result.code = ;
result.msg = "OK";
}
return result;
}
}

自定义webAPI的过滤器,在过滤器中校验用户是否已经登录,过滤器中还可以设置只能指定的UserAgent可以访问接口,这样就可以让不同的客户端在请求的时候自定义不同的UserAgent,并且可以限制浏览器访问,做到一定的安全性,针对客户端的限制除了利用UserAgent之外还可以在http请求头自定义一个ClientID,为每个客户端配备一个ID,在过滤器中校验ClientID是否合法即可

 public class ApiActionFilterAttribute: ActionFilterAttribute
{
/// <summary>
/// 签名参数
/// </summary>
public string[] Signpara { get; set; } public string[] Cachepara { get; set; } private bool _IsCache = false;
public bool IsCache
{
get
{
return _IsCache;
}
set { _IsCache = value; }
} private bool _IsSigna = false;
public bool IsSigna
{
get { return _IsSigna; }
set { _IsSigna = value; }
} private bool _IsUrlDecode = false;
/// <summary>
/// 是否解码
/// </summary>
public bool IsUrlDecode
{
get { return _IsUrlDecode; }
set { _IsUrlDecode = value; }
} private ClientEnum _CheckClient = ClientEnum.NoCheck;
public ClientEnum CheckClient
{
get { return _CheckClient; }
set { _CheckClient = value; }
}
private bool _IsLogin;
/// <summary>
/// 是否登录
/// </summary>
public bool IsLogin
{
get
{
return _IsLogin;
}
set
{
_IsLogin = value;
}
}
/// <summary>
/// 缓存超时时间
/// </summary>
public int TimeOut { get; set; } public override void OnActionExecuting(HttpActionContext actionContext)
{
JsonResult<string> result = new JsonResult<string>();
if (CheckClient == ClientEnum.WindowsClient && !actionContext.Request.Headers.UserAgent.TryToString().Equals(SystemSet.WindowsClientUserAgent))
{
result.code = -;
result.msg = "illegal client";
//filterContext.HttpContext.Response.Status = HttpStatusCode.OK;
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, result);
return;
}
if (CheckClient == ClientEnum.WebClient && !actionContext.Request.Headers.UserAgent.TryToString().Equals(SystemSet.WebClientUserAgent))
{
result.code = -;
result.msg = "illegal client";
//filterContext.HttpContext.Response.Status = HttpStatusCode.OK;
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, result);
return;
}
if (IsLogin)
{
ApiUserManager userManager = new ApiUserManager(actionContext);
if (!userManager.ExistsLogin())
{
result.code = -;
result.msg = "illegal user";
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, result);
return;
}
}
base.OnActionExecuting(actionContext);
}
}

过滤器的使用可以针对单独一个接口进行限制,也可以针对一个控制器中的所有接口进行限制。

针对单独一个接口限制

/// <summary>
/// 获取当前登录用户信息
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("userinfo")]
[ApiActionFilterAttribute(IsLogin = true)]
public async Task<IHttpActionResult> Userinfo()
{
JsonResult<UserInfo> result = new JsonResult<UserInfo>();
result.code = ;
result.msg = "OK";
ApiUserManager userManager = new ApiUserManager(ActionContext);
result.Result = userManager.User;
return Ok(result);
}

针对整个控制器进行限制的话可以自定义一个控制器基类BaseApiController,所有需要验证用户登录的控制器继承它就可以

    [DExceptionFilterAttribute]
[ApiActionFilterAttribute(IsLogin = true)]
public class BaseApiController: ApiController
{
private UserInfo _User;
/// <summary>
/// 当前登录用户
/// </summary>
public UserInfo User
{
get
{
if (_User==null)
{
ApiUserManager userManager = new ApiUserManager(this.ActionContext);
_User = userManager.User;
}
return _User;
}
}
}

至此授权和鉴权的机制基本已经完成,单独的资源服务器只需要使用公用的redis服务即可,而且redis也可以进行读写分离,就可以进行分布式部署。

项目源码地址:

https://github.com/liemei/asp.netOpenService.git

自定义分布式RESTful API鉴权机制的更多相关文章

  1. ApiAuthValue鉴权机制总结

    一.背景介绍 1.自动化的配置工具autoconfig介绍 项目开发过程中,有些配置会随着运行环境的变化而各不相同.如jdbc驱动的配置,在开发环境可能链接到开发本地的数据库,测试环境则有一套测试专用 ...

  2. 分布式文件系统 / MQ / 鉴权(轮廓)

    FastDFS的轮廓   /  RabbitMQ的轮廓  /  JWT和RSA非对称加密的轮廓

  3. shiro jwt 构建无状态分布式鉴权体系

    一:JWT 1.令牌构造 JWT(json web token)是可在网络上传输的用于声明某种主张的令牌(token),以JSON 对象为载体的轻量级开放标准(RFC 7519). 一个JWT令牌的定 ...

  4. # RESTful登录(基于token鉴权)的设计实例

    使用场景 现在很多基于restful的api接口都有个登录的设计,也就是在发起正式的请求之前先通过一个登录的请求接口,申请一个叫做token的东西.申请成功后,后面其他的支付请求都要带上这个token ...

  5. Spring Cloud注册中心Eureka设置访问权限并自定义鉴权页面

    原文:https://blog.csdn.net/a823007573/article/details/88971496 使用Spring Security实现鉴权 1. 导入Spring Secur ...

  6. 前后端分离开发,基于SpringMVC符合Restful API风格Maven项目实战(附完整Demo)!

    摘要: 本人在前辈<从MVC到前后端分离(REST-个人也认为是目前比较流行和比较好的方式)>一文的基础上,实现了一个基于Spring的符合REST风格的完整Demo,具有MVC分层结构并 ...

  7. web开发常见的鉴权方式

    结合网上找的资料整理了一下,以下是web开发中常见的鉴权方法: 预备:一些基本的知识 RBAC(Role-Based Access Control)基于角色的权限访问控制(参考下面①的连接) l    ...

  8. Spring Security 接口认证鉴权入门实践指南

    目录 前言 SpringBoot 示例 SpringBoot pom.xml SpringBoot application.yml SpringBoot IndexController SpringB ...

  9. RESTful API 编写指南

    基于一些不错的RESTful开发组件,可以快速的开发出不错的RESTful API,但如果不了解开发规范的.健壮的RESTful API的基本面,即便优秀的RESTful开发组件摆在面前,也无法很好的 ...

随机推荐

  1. openlayers应用“三”:百度地图纠偏

    前两篇文章介绍了openlayers3加载百度在线和离线瓦片地图,页面上能够正常显示.地图加载后在地图上显示一条GPS轨迹,发现离实际位置相差太远,如下图所示: 轨迹形状和实际形状相同,但是位移太远, ...

  2. [UWP]了解模板化控件(9):UI指南

    1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性. 譬如,修改HeaderedCont ...

  3. jQuery选择器---基本选择器总结

    今天要跟大家分享一下jQuery选择器的使用方法,它的选择器分为四大类 如图: 基本选择器的使用: 1.id选择器 案例: <div id="notMe"><p& ...

  4. redis集群安装部署

    (要让集群正常工作至少需要3个主节点,在这里我们要创建6个redis节点,其中三个为主节点,三个为从节点,对应的redis节点的ip和端口对应关系如下) 192.168.1.160:7000 192. ...

  5. Win10更新补丁失败后出现无法更新正在撤销 解决办法

    系统更新失败,反复重启还是不行,那是不是下载下来的补丁没用了呢??所以我们先要删除Windows更新的缓存文件!在做以下操作之前,首先我们要确认系统内的windows update & BIT ...

  6. HTML5本地存储之Web Storage应用介绍

    Web Storage是HTML5引入的一个非常重要的功能,可以在客户端本地存储数据,类似HTML4的cookie,但可实现功能要比cookie强大的多,cookie大小被限制在4KB,Web Sto ...

  7. jQuery手风琴菜单!!!!

    jQuery手风琴菜单 第一次发博客也不知道说点什么好,以前敲得一个手风琴菜单刚刚整理出来了,就来分享个大家 手风琴的排版 排版完事了,接下来就写样式吧,把自己喜欢的颜色或者是图片添加进来,就会变成你 ...

  8. 详解Google Chrome浏览器(操作篇)(下)

    开篇概述 由于最近忙于公司产品的架构与研发,已经三个多月没有写博客了,收到有些朋友的来信,问为什么不及时更新博客内容呢,他们说他们正期待着某些内容.对此,非常抱歉,那么我在此也给各位朋友一些承诺,从即 ...

  9. python 使用 'python -m pip install --upgrade pip'提示PermissionError: [WinError 5] 拒绝访问

    执行pip install --upgrade pip 提示"PermissionError: [WinError 5] 拒绝访问",如下图,由于更新的用户权限不够,换成管理员运行 ...

  10. WINFORM数据库操作,有点像安装里面的SQLITE

    程序设计要求 设计一个用户管理系统,对系统中的用户进行管理.假定,用户表中有下列字段:用户名,密码,电话和 email 等信息.要求,1)利用 SQL server 首先创建用户数据表:2)实现对用户 ...