(NetFramework)WebApi Token+ 数字签名认证

首先说一下具体思路和认证的过程:

1.客户端登录成功后生成令牌token,并在服务器保存token,然后返回给客户端。

2.客户端用时间戳+随机数+数据=数字签名,通过加密算法生成数字签名。

3.将token、用户ID、时间戳、随机数、数据、数字签名,保存到http的header中,通过接口提交给服务器。

4.由服务器验证访问的合法性。包括:token的有效性和是否过期、屏蔽爬虫、数字签名的真实性。也可加入系统级判断:是否有权限访问。

一、客户端登录

客户端通过jquery对API接口发出请求:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <script src="Scripts/jquery-3.4.1.min.js"></script>
    <script type="text/javascript">

        //早期浏览器版本跨域设置
        jQuery.support.cors = true;
        var token = "";
        function login() {
            $.ajax({
                type: "get",
                url: "http://localhost:44338/api/Login?uid=123&pwd=123&usertype=user",
                success: function (data, status) {
                    token = data.Data;
                    alert(data.Info);
                },
                error: function (e) {
                    console.log(e)
                }
            });
        }
    </script>
</head>

服务器端API接口方法:

public class LoginController : AnonyController
    {
        [HttpGet]
        public IHttpActionResult Login(string uid,string pwd,string usertype)
        {
            ResultMsg result = null;
            if (uid=="123"&&pwd=="123")
            {
                string token = Guid.NewGuid().ToString();
                //token失效时间
                var timeout = DateTime.Now.AddDays(1);
                //添加token
                CacheManager.TokenInsert(token, uid, usertype, timeout);
                result = new ResultMsg { StatusCode = 1, Info = "登录成功",Data=token };
            }
            else
            {
                result = new ResultMsg { StatusCode = 0, Info = "登录失败,账号或密码错误",Data=null };
            }
            return Json<ResultMsg>(result);
        }
}

 

/// <summary>
/// 添加令牌
/// </summary>
/// <param name="token">令牌</param>
/// <param name="uuid">用户ID凭证</param>
/// <param name="userType">用户类别</param>
/// <param name="timeout">过期时间</param>
public static void TokenInsert(string token, object uuid, string userType, DateTime timeout)
{
    CacheInit();
     // token不存在则添加
    if (!TokenIsExist(token))
    {
        DataTable dt = (DataTable)HttpRuntime.Cache["PASSPORT.TOKEN"];
        DataRow dr = dt.NewRow();
        dr["token"] = token;
        dr["uuid"] = uuid;
        dr["userType"] = userType;
        dr["timeout"] = timeout;
        dt.Rows.Add(dr);
        HttpRuntime.Cache["PASSPORT.TOKEN"] = dt;
        //保存进数据库
        //tempCacheService.Add_TempCaches(new List<TempCacheDTO_ADD>()
        //{
        //    new TempCacheDTO_ADD()
        //    {
        //        EndTime = timeout,
        //        UserAccountId = new Guid(uuid.ToString()),
        //        UserToken = new Guid(token),
        //        UserType = (UserType)Enum.Parse(typeof(UserType),userType)
        //    }
        //});
    }
    // token存在则更新过期时间
   else
    {
        TokenTimeUpdate(token, timeout);
        //更新数据库
        //tempCacheService.Update_TempCaches(new Guid(token), timeout);
    }
}

完成了对登录的请求、服务器token的保存、返回给客户端token操作。

 二、客户端生成数字签名

生成数字签名需要:时间戳、随机数、页面提交的数据。时间戳:用于防止爬虫重复提交。页面提交的数据:用于防止爬虫篡改数据,随机数算是一个变量因子。客户端与服务器端生成数字签名的算法要一致。客户端数字签名需要与服务器的数字签名对比来识别提交的数据是否被篡改。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <script src="jquery-3.4.1.min.js"></script>
    <script src="jquery.md5.js"></script>
    <script type="text/javascript">

//早期浏览器版本跨域设置
jQuery.support.cors = true;
        var token = "";
        var uid = "123";
        function login() {
            $.ajax({
                type: "get",
                url: "http://localhost:44338/api/Login?uid=" + uid + "&pwd=123&usertype=user",
                success: function (data, status) {
                    token = data.Data;
                    alert(data.Info);
                },
                error: function (e) {
                    console.log(e)
                }
            });
        }

        function Getdata() {

            //数据
            var jsonparam = { name1: "数据1", name2: "数据2", name3: "数据3" };

            var url = "http://localhost:44338/api/send/Getdata";

            GetAPI(jsonparam, url);
        }

        function GetAPI(jsonparam, url) {
            //时间戳
            var timestamp = new Date().getTime().toString();
            //随机数
            var nonce = Math.round(Math.random() * 100000000).toString();
            //拼接参数
            var builder = [];
            var query = [];
            for (var val in jsonparam) {
                builder.push(val);
                builder.push(jsonparam[val]);

                query.push(val + "=");
                query.push(encodeURIComponent(jsonparam[val]) + "&");
            }
            var data = builder.join('');
            var querystring = query.join('');
            querystring = querystring.substring(0, querystring.length - 1);

            //数字签名
            var signatureStr = encodeURIComponent(timestamp + nonce + uid + token + data).toUpperCase();
            var signature = $.md5(signatureStr).toUpperCase();

            $.ajax({
                type: "get",
                url: url + "?" + querystring,
                beforeSend: function (request) {
                    request.setRequestHeader("timestamp", timestamp);
                    request.setRequestHeader("nonce", nonce);
                    request.setRequestHeader("signature", signature);
                    request.setRequestHeader("uid", uid);
                    request.setRequestHeader("token", token);
                },
                success: function (data, status) {
                    alert(data);
                },
                error: function (e) {
                    console.log(e)
                }
            });

        }
    </script>
</head>
<body>
    <input id="Button1" type="button" onclick="login();" value="登录" />
    <input id="Button3" type="button" onclick="Getdata();" value="GET请求数据" />
</body>

</html>

三、服务器端验证合法性

在调用请求的API接口之前,系统首先进入ActionFilterAttribute类进行拦截,所以我们定义一个类继承此类。可以作为接口拦截器来判断http请求的合法性。上服务器代码如下:

/// <summary>
    /// 调用接口过滤器
    /// </summary>
    public class ApiSecurityAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 判断http请求的合法性
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            ResultMsg resultMsg = null;
            var request = actionContext.Request;
            string method = request.Method.Method;
            string token = string.Empty,
                uid = String.Empty, 
                timestamp = string.Empty, 
                nonce = string.Empty, 
                signature = string.Empty;
            if (request.Headers.Contains("token"))
            {
                token = HttpUtility.UrlDecode(request.Headers.GetValues("token").FirstOrDefault());
            }
            if (request.Headers.Contains("uid"))
            {
                uid = HttpUtility.UrlDecode(request.Headers.GetValues("uid").FirstOrDefault());
            }
            if (request.Headers.Contains("timestamp"))
            {
                timestamp = HttpUtility.UrlDecode(request.Headers.GetValues("timestamp").FirstOrDefault());
            }
            if (request.Headers.Contains("nonce"))
            {
                nonce = HttpUtility.UrlDecode(request.Headers.GetValues("nonce").FirstOrDefault());
            }
            if (request.Headers.Contains("signature"))
            {
                signature = HttpUtility.UrlDecode(request.Headers.GetValues("signature").FirstOrDefault());
            }


            //判断请求头是否包含以下参数
            if (string.IsNullOrEmpty(token) || 
                string.IsNullOrEmpty(uid) || 
                string.IsNullOrEmpty(timestamp) || 
                string.IsNullOrEmpty(nonce) || 
                string.IsNullOrEmpty(signature))
            {
                resultMsg = new ResultMsg();
                resultMsg.StatusCode = (int)StatusCodeEnum.ParameterError;
                resultMsg.Info = StatusCodeEnum.ParameterError.GetEnumText();
                resultMsg.Data = "";
                actionContext.Response = HttpResponseExtension.ToJson(JsonConvert.SerializeObject(resultMsg));
                base.OnActionExecuting(actionContext);
                return;
            }

            //判断timespan是否有效
            double ts1 = 0;
            double ts2 = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds;
            bool timespanvalidate = double.TryParse(timestamp, out ts1);
            double ts = ts2 - ts1;
            //超过两分钟重复提交无效
            bool falg = ts > int.Parse(WebSettingsConfig.UrlExpireTime) * 1000;//时间间隔2分钟
            if (falg || (!timespanvalidate))
            {
                resultMsg = new ResultMsg();
                resultMsg.StatusCode = (int)StatusCodeEnum.URLExpireError;
                resultMsg.Info = StatusCodeEnum.URLExpireError.GetEnumText();
                resultMsg.Data = "";
                actionContext.Response = HttpResponseExtension.ToJson(JsonConvert.SerializeObject(resultMsg));
                base.OnActionExecuting(actionContext);
                return;
            }


            //判断token是否有效
            Token mytoken = (Token)HttpRuntime.Cache.Get(token);
            string signtoken = string.Empty;
            if (!CacheManager.TokenIsExist(token))
            {
                resultMsg = new ResultMsg();
                resultMsg.StatusCode = (int)StatusCodeEnum.TokenInvalid;
                resultMsg.Info = StatusCodeEnum.TokenInvalid.GetEnumText();
                resultMsg.Data = "";
                actionContext.Response = HttpResponseExtension.ToJson(JsonConvert.SerializeObject(resultMsg));
                base.OnActionExecuting(actionContext);
                return;
            }

            bool result = false;
            switch (method)
            {
                case "POST":
                    Stream stream = HttpContext.Current.Request.InputStream;
                    string responseJson = string.Empty;
                    StreamReader streamReader = new StreamReader(stream);
                    result = SignExtension.ValidatePost(timestamp, nonce, uid, token, signature);
                    break;
                case "GET":
                    result = SignExtension.ValidateGet(timestamp,nonce,uid,token,signature);
                    break;
                default:
                    resultMsg = new ResultMsg();
                    resultMsg.StatusCode = 1;
                    resultMsg.Info ="";
                    resultMsg.Data =null;
                    actionContext.Response = HttpResponseExtension.ToJson(JsonConvert.SerializeObject(resultMsg));
                    base.OnActionExecuting(actionContext);
                    return;
            }

            if (!result)
            {
                resultMsg = new ResultMsg();
                resultMsg.StatusCode = (int)StatusCodeEnum.HttpRequestError;
                resultMsg.Info = StatusCodeEnum.HttpRequestError.GetEnumText();
                resultMsg.Data = "";
                actionContext.Response = HttpResponseExtension.ToJson(JsonConvert.SerializeObject(resultMsg));
                base.OnActionExecuting(actionContext);
                return;
            }
            else
            {
                base.OnActionExecuting(actionContext);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="actionExecutedContext"></param>
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            base.OnActionExecuted(actionExecutedContext);
        }
    }

文章相关源码下载地址:源码下载,希望对大家有帮助,谢谢。欢迎留言指正!!!

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
首先,我们需要安装 `Microsoft.AspNet.WebApi` 和 `Microsoft.Owin.Security.Jwt` NuGet 包。 接下来,我们需要在 `WebApiConfig.cs` 文件中配置 Web API 路由: ```csharp public static void Register(HttpConfiguration config) { // 配置路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); // 配置 JWT 认证 ConfigureJwtAuth(config); } ``` 然后,我们需要在 `Web.config` 文件中配置 JWT 令牌的密钥和有效期: ```xml <appSettings> <add key="jwtSecret" value="my_secret_key" /> <add key="jwtExpireDays" value="7" /> </appSettings> ``` 接下来,我们需要创建一个 `JwtAuthManager` 类来管理 JWT 认证: ```csharp using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using Microsoft.IdentityModel.Tokens; public class JwtAuthManager : IJwtAuthManager { private readonly string _jwtSecret; private readonly double _jwtExpireDays; public JwtAuthManager(string jwtSecret, double jwtExpireDays) { _jwtSecret = jwtSecret; _jwtExpireDays = jwtExpireDays; } public string GenerateToken(IEnumerable<Claim> claims) { var key = Encoding.ASCII.GetBytes(_jwtSecret); var jwtToken = new JwtSecurityToken( claims: claims, expires: DateTime.Now.AddDays(_jwtExpireDays), signingCredentials: new SigningCredentials( new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) ); var token = new JwtSecurityTokenHandler().WriteToken(jwtToken); return token; } } ``` 然后,我们需要创建一个 `JwtAuthAttribute` 特性,用于在控制器或操作方法上应用 JWT 认证: ```csharp [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class JwtAuthAttribute : AuthorizeAttribute { public override void OnAuthorization(HttpActionContext actionContext) { try { var token = actionContext.Request.Headers.Authorization.Parameter; var jwtAuthManager = actionContext.ControllerContext.Configuration .DependencyResolver.GetService(typeof(IJwtAuthManager)) as IJwtAuthManager; var principal = jwtAuthManager.ValidateToken(token); Thread.CurrentPrincipal = principal; HttpContext.Current.User = principal; } catch (Exception) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); return; } base.OnAuthorization(actionContext); } } ``` 最后,我们需要在 `ConfigureJwtAuth` 方法中注册依赖项并配置 JWT 认证: ```csharp private static void ConfigureJwtAuth(HttpConfiguration config) { var jwtSecret = ConfigurationManager.AppSettings["jwtSecret"]; var jwtExpireDays = double.Parse(ConfigurationManager.AppSettings["jwtExpireDays"]); var container = new UnityContainer(); container.RegisterType<IJwtAuthManager, JwtAuthManager>( new InjectionConstructor(jwtSecret, jwtExpireDays)); config.DependencyResolver = new UnityResolver(container); config.Filters.Add(new JwtAuthAttribute()); } ``` 现在,我们可以在控制器或操作方法上应用 `JwtAuth` 特性来启用 JWT 认证: ```csharp [RoutePrefix("api/products")] public class ProductsController : ApiController { [HttpGet] [Route("")] [JwtAuth] public IHttpActionResult Get() { // ... } [HttpGet] [Route("{id}")] [JwtAuth] public IHttpActionResult Get(int id) { // ... } [HttpPost] [Route("")] [JwtAuth] public IHttpActionResult Post([FromBody] Product product) { // ... } // ... } ``` 这样,我们就成功地基于 JWT 实现了 Token 签名认证

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旭日升的博客园

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值