我们知道WEB网站的身份验证一般通过session或者cookie完成的,登录成功后客户端发送的任何请求都带上cookie,服务端根据客户端发送来的cookie来识别用户。
WEB API使用这样的方法不是很适合,于是就有了基于令牌的认证,使用令牌认证有几个好处:可扩展性、松散耦合、移动终端调用比较简单等等。
需要安装的NuGet包
JWT
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
namespace HrApi.App_Start
{
/// <summary>
/// HTTP消息拦截器
/// </summary>
public class RequestHandler : DelegatingHandler
{
/// <summary>
/// 拦截请求
/// </summary>
/// <param name="request">请求</param>
/// <param name="cancellationToken">用于发送取消操作信号</param>
/// <returns></returns>
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
//获取URL参数
NameValueCollection query = HttpUtility.ParseQueryString(request.RequestUri.Query);
JavaScriptSerializer serializer = new JavaScriptSerializer();
//获取请求头信息
if (request.Method.ToString().ToUpper() == "POST" && request.Headers != null)
{
var obj = request.Headers.Authorization;
if (obj != null && obj.Scheme != null && obj.Parameter != null)
{
ServerResult ret = new ServerResult();
var scheme = obj.Scheme;
var token = obj.Parameter;//token
var userInfo = JwtToken.ValidateJwtToken(token);
if (userInfo == "expired")
{
ret.code = "401";
ret.msg = "Token过期,请重新登录";
return SendError(serializer.Serialize(ret), HttpStatusCode.OK);
//过期
}
else if (userInfo == "invalid")
{
ret.msg = "Token验证不通过,请重新登录";
ret.code = "401";
return SendError(serializer.Serialize(ret), HttpStatusCode.OK);
//验证失败
}
else if (userInfo == "error")
{
ret.msg = "Token验证出错,请重新登录";
ret.code = "401";
return SendError(serializer.Serialize(ret), HttpStatusCode.OK);
//验证出错
}
else
{
//string ss = tokenInfo;
Dictionary<string, object> user = (Dictionary<string, object>)serializer.DeserializeObject(userInfo);
string username = user["username"].ToString();
string Id = user["userId"].ToString();
double exp = Convert.ToDouble(user["exp"].ToString());
string companyId = user["companyId"].ToString();
//获取Post正文数据,比如json文本
//string fRequesContent = request.Content.ReadAsStringAsync().Result;
调用内部处理接口,并获取HTTP响应消息
double now_exp = (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
double exp2 = exp - now_exp;
if (now_exp < exp && exp - now_exp < 9000)
{//两个半小时后 再次刷新token
HttpResponseMessage response2 = await base.SendAsync(request, cancellationToken);
if (response2.Content != null)//向前端返回402 刷新token
{
string fResponseContent = response2.Content.ReadAsStringAsync().Result;
Dictionary<string, object> dic = (Dictionary<string, object>)serializer.DeserializeObject(fResponseContent);
string newNoken = JwtToken.CreateToken(Id, username, companyId);
dic["code"] = 402;
dic.Add("token", newNoken);
string responseResultStr = serializer.Serialize(dic);
response2.Content = new StringContent(responseResultStr);
}
return response2;
}
//}
}
}
}
//请求处理耗时跟踪
Stopwatch sw = new Stopwatch();
sw.Start();
//调用内部处理接口,并获取HTTP响应消息
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
//篡改HTTP响应消息正文
//response.Content = new StringContent(response.Content.ReadAsStringAsync().Result.Replace(@"\\", @"\"));
if (response.Content != null)
{
string fResponseContent = response.Content.ReadAsStringAsync().Result;
}
sw.Stop();
//记录处理耗时
long exeMs = sw.ElapsedMilliseconds;
return response;
}
/// <summary>
/// 构造自定义HTTP响应消息
/// </summary>
/// <param name="error"></param>
/// <param name="code"></param>
/// <returns></returns>
private HttpResponseMessage SendError(string error, HttpStatusCode code)
{
var response = new HttpResponseMessage();
try
{
response.Content = new StringContent(error);
response.StatusCode = code;
//response.Headers.Add("Content-Type", "application/json");
response.Headers.Add("Connection", "close");
response.Headers.Add("Access-Control-Allow-Origin", "*");
return response;
}
catch (Exception ex)
{
string ss = ex.Message;
return new HttpResponseMessage(); ;
}
}
}
}
在项目中添加JwtToken 类,来解析 token
using System;
using System.Collections.Generic;
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using JWT.Exceptions;
using System.Web.Script.Serialization;
using System.Collections;
using System.Web;
namespace BLL_API
{
public class JwtToken
{
private static string secret = "XXXwwww4564qqq";
/// <summary>
/// 创建token
/// </summary>
/// <returns></returns>
public static string CreateJwtToken(IDictionary<string, object> payload, string secret, IDictionary<string, object> extraHeaders = null)
{
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(payload, secret);
return token;
}
/// <summary>
/// 校验解析token
/// </summary>
/// <returns></returns>
public static string ValidateJwtToken(string token)
{
try
{
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtAlgorithm alg = new HMACSHA256Algorithm();
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, alg);
var json = decoder.Decode(token, secret, true);
//校验通过,返回解密后的字符串
return json;
}
catch (TokenExpiredException)
{
//表示过期
return "expired";
}
catch (SignatureVerificationException)
{
//表示验证不通过
return "invalid";
}
catch (Exception)
{
return "error";
}
}
/// <summary>
/// 通过参数生成Token
/// </summary>
/// <returns></returns>
public static string CreateToken(object userId,string username, object companyId)
{
#region 生成Token
double exp = (DateTime.UtcNow.AddHours(3) - new DateTime(1970, 1, 1)).TotalSeconds;
var payload = new Dictionary<string, object>
{
{ "userId", userId },
{ "username", username },
{ "companyId", companyId },
{ "exp",exp }
};
var token = JwtToken.CreateJwtToken(payload, secret);
return token;
#endregion
}
/// <summary>
/// Token验证,和刷新token
/// </summary>
/// <returns></returns>
public static string ValidateAndGetCompanyID()
{
ServerResult serverResult = BLL_API.ServerResult.GetAnNoDataResult("200", "success");
string Authorization = HttpContext.Current.Request.Headers["Authorization"].ToString();
string company_id ="0";
if (Authorization.Length > 0)
{
string tokenInfo = ValidateJwtToken(Authorization.Replace("Bearer ", ""));
if (tokenInfo == "expired")
{
serverResult.code = "401";
serverResult.msg = "Token超时了,请重新登录";
//过期
}
else if (tokenInfo == "invalid")
{
serverResult.code = "401";
serverResult.msg = "Token验证失败,请重新登录";
//验证失败
}
else if (tokenInfo == "error")
{
serverResult.code = "401";
serverResult.msg = "Token验证出错,请重新登录";
//验证出错
}
else
{
//刷新token
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, object> obj = (Dictionary<string, object>)serializer.DeserializeObject(tokenInfo);
string username = obj["username"].ToString();
string userId = obj["userId"].ToString();
string companyId = obj["companyId"].ToString();
double exp = Convert.ToDouble(obj["exp"].ToString());
double nowTime = (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
Hashtable hs = new Hashtable();
hs.Add("username", username);
hs.Add("userId", userId);
hs.Add("companyId", companyId);
double now_exp = (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
double exp2 = exp - now_exp;
if (now_exp < exp && exp - now_exp < 9000)
//if (nowTime < exp)
{
#region 刷新Token
string newToken = CreateToken(userId, username,companyId);
hs.Add("token", newToken);
serverResult.code = "402";//刷新token
#endregion
}
serverResult.data = hs;
if(!string.IsNullOrEmpty(companyId))
{
company_id = companyId;
}
}
}
return company_id;
}
}
}
这样就可以通过 Jwt生成需要的token了。