微软利用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. css定位 浮动 伪类 margin

    一,margin .标准文档流,margin在竖直方向的不叠加,以较大的为准 .使用margin: auto;的盒子必须有明确的width,并且只有标准文档流的盒子 才能使用margin: auto; ...

  2. UIView的属性

    .alpha 设置视图的透明度.默认为1. // 完全透明 view.alpha = ; // 不透明 view.alpha = ; .clipsToBounds // 默认是NO,当设置为yes时, ...

  3. NodeJS+Express+MongoDB 简单实现数据录入及回显展示【适合新人刚接触学习】

    近期在看NodeJS相关 不得不说NodeJS+Express 进行网站开发是很不错,对于喜欢玩JS的来说真是很好的一种Web开发组合 在接触NodeJS时受平时Java或者C#中API接口等开发的思 ...

  4. [刷题]算法竞赛入门经典(第2版) 5-15/UVa12333 - Revenge of Fibonacci

    题意:在前100000个Fibonacci(以下简称F)数字里,能否在这100000个F里找出以某些数字作为开头的F.要求找出下标最小的.没找到输出-1. 代码:(Accepted,0.250s) / ...

  5. Potato(邪恶土豆)–windows全版本猥琐提权

    工作原理: Potato利用已知的Windows中的问题,以获得本地权限提升,即NTLM中继(特别是基于HTTP > SMB中继)和NBNS欺骗.使用下面介绍的技术,它有可能为一个非特权用户获得 ...

  6. 面向切面编程(Aop)

    AOP中的概念 AOP(Aspect Orient Programming),也就是面向切面编程.可以这样理解,面向对象编程(OOP)是从静态角度考虑程序结构,面向切面编程(AOP)是从动态角度考虑程 ...

  7. Vue声明式渲染

    Vue.js 的核心是一个允许采用简洁的模板语法来声明式的将数据渲染进 DOM,也就是将模板中的文本数据写进DOM中,使用  {{data}}  的格式写入.此代码都是Vue.js官网上的实例. 1. ...

  8. MyBatis起步

    作用:封装了JDBC操作,简化数据库访问代码.封装的功能:1.获取连接,执行SQL,释放连接2.SQL参数设置(可以直接传入对象,Mybtis会将对象的属性传入SQL语句) #{属性值}取代JDBC的 ...

  9. 最简单 iText 的 PDF 生成方案(含中文解决方案)HTML 转为 PDF

    转自:http://my.oschina.net/sanji/blog/277704 最近正好项目有用到 ITEXT ,在网络上搜索了一番,发现了很多方案,但是感觉对于一般开发来说都太复杂了,本文提供 ...

  10. CentOS下SparkR安装部署:hadoop2.7.3+spark2.0.0+scale2.11.8+hive2.1.0

    注:之前本人写了一篇SparkR的安装部署文章:SparkR安装部署及数据分析实例,当时SparkR项目还没正式入主Spark,需要自己下载SparkR安装包,但现在spark已经支持R接口,so更新 ...