ASP.NET WebApi TOKEN+签名认证

一、 在开放的api接口中,我们通过http Post或者Get请求服务器的时候,会面临着许多的安全性问题,例如:

①请求来源(身份)是否合法?
②请求参数被篡改?
③请求的唯一性(不可复制),防止请求被恶意攻击
为了保证数据在通信时的安全性,我们可以采用TOKEN+参数签名的方式来进行相关验证。

二、使用TOKEN+签名认证 保证请求安全性

token+签名认证的主要原理是:

1.AppKey从何而来? 通过账号密码登录或者第三方认证服务器认证获取AppKey.(本文中的代码没有这一步骤.本文中的代码没有这一步骤.本文中的代码没有这一步骤.重要的事情说三遍)
如下图:(博客大佬回复的)(staffid或者appId就是此文中的AppKey)
在这里插入图片描述

2.1.做一个认证服务,提供一个认证的webapi,用户AppKey先访问它获取对应的token,用户Token缓存在服务器(该请求不需要进行签名认证),返回Token给用户,Token可能过期失效需要重新认证。

3.在后面的API接口中用户拿着请求头(①AppKey ②TimeStamp ③ Nonce ④Sign签名验证(AppKey +TimeStamp+Nonce+Token+请求参数名,请求参数值) ,注意:请求头中不携带Token。

4.服务器端每次接收到请求就获取对应用户的请求参数,服务器端计算Sign和客户端Sign做对比,如果验证通过则正常访问相应的api,验证失败则返回具体的失败信息

三、 参数说明

AppKey:公钥

TimeStamp:时间戳

Nonce:随机数

Sign:签名值

Token:密钥(禁止传递)

四、 直接上干货

① WebApiConfig 注册过滤器

var webapiAss = Assembly.Load("DAL");
//从webapiAss中拿到所有实现了IAuthorizationFilter接口,或者实现了IActionFilter接口的非抽象过滤器类
var filters = webapiAss.GetTypes().Where(r => !r.IsAbstract && (typeof(IAuthorizationFilter).IsAssignableFrom(r) || typeof(IActionFilter).IsAssignableFrom(r)));
foreach (var item in filters)
{
    //从IOC容器中拿到过滤器类对象(因为过滤器都是实现了IFilter接口的,所有这里以IFilter接口来接收)
    IFilter filter = (IFilter)GlobalConfiguration.Configuration.DependencyResolver.GetService(item);
    config.Filters.Add(filter); //注册过滤器
}

②Global.asax.cs

protected void Application_Start()
{
    AutofacConfig.SetAutofacWebApi();
}

③使用Autofac注入

/// <summary>
/// 使用Autofac注入
/// </summary>
public class AutofacConfig
{
    /// <summary>
    /// 注册接口和实现
    /// </summary>
    public static void SetAutofacWebApi()
    {
        //得到你的HttpConfiguration.
        var configuration = GlobalConfiguration.Configuration;
        var builder = new ContainerBuilder();
        注册控制器
        //builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
        var webapiAssembly = Assembly.Load("DAL");
        //注册webapi项目中实现了IAuthorizationFilter接口或者实现了IActionFilter接口的非抽象过滤器类
        builder.RegisterAssemblyTypes(webapiAssembly).Where(r => !r.IsAbstract &&
                                                                 (typeof(IAuthorizationFilter).IsAssignableFrom(r)) || typeof(IActionFilter).IsAssignableFrom(r)).PropertiesAutowired();
        IContainer container = builder.Build();
        //将依赖关系解析器设置为Autofac。
        var resolver = new AutofacWebApiDependencyResolver(container);
        configuration.DependencyResolver = resolver;
    }
}

④服务器token+签名认证 +过滤器

using DAL;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
 
namespace MES_Server
{
    public class MyAuthenticationAttribute : IAuthorizationFilter//也可以直接继承AuthorizationFilterAttribute
    {
        public bool AllowMultiple => true;
        public async Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
        {
            //获得报文头中的AppKey,TimeStamp,Nonce,Sign (我们与客户端约定,在向服务端发起请求的时候,将AppKey,TimeStamp,Nonce,Sign放到请求报文头中)
            IEnumerable<string> appKeys;
            if (!actionContext.Request.Headers.TryGetValues("AppKey", out appKeys)) //从请求报文头中获取AppKey
            {
                return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) { Content = new StringContent("报文头中的AppKey为空") };
            }
            IEnumerable<string> timeStamps;
            if (!actionContext.Request.Headers.TryGetValues("TimeStamp", out timeStamps))
            {
                return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) { Content = new StringContent("报文头中的TimeStamp为空") };
            }
            IEnumerable<string> nonces;
            if (!actionContext.Request.Headers.TryGetValues("Nonce", out nonces))
            {
                return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) { Content = new StringContent("报文头中的Nonce为空") };
            }

            //GetToken方法不需要进行签名验证
            if (actionContext.ActionDescriptor.ActionName == "GetToken")
            {
                if (string.IsNullOrEmpty(appKeys.First()) || string.IsNullOrEmpty(timeStamps.First()) || string.IsNullOrEmpty(nonces.First()))
                {
                    return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) { Content = new StringContent("AppKey/TimeStamp/Nonce任一数值不能为空") };
                }
                else
                {
                    return await continuation();
                }
            }
            //数字签名
            IEnumerable<string> signs;
            if (!actionContext.Request.Headers.TryGetValues("Sign", out signs)) //从请求报文头中获取Sign
            {
                return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) { Content = new StringContent("报文头中的Sign为空") };
            }
            //判断timespan是否有效
            double ts1 = 0;
            double ts2 = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds;
            bool timespanvalidate = double.TryParse(timeStamps.First(), out ts1);
            double ts = ts2 - ts1;
            bool flag = ts > 1000 * 30;
            if (flag || (!timespanvalidate))
            {
                return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) { Content = new StringContent("timespan认证失败") };
            }
            //判断AppToken是否有效
            AppToken token = (AppToken)HttpRuntime.Cache.Get(appKeys.First());
            string signtoken = string.Empty;
            if (token == null)
            {
                return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) { Content = new StringContent("AppToken认证失败") };
            }
            else
            {
                signtoken = token.SignToken.ToString();
            }
            string requestDataStr = ""; //请求参数字符串
            List<KeyValuePair<string, string>> requestDataList = new List<KeyValuePair<string, string>>();//请求参数键值对
            if (actionContext.Request.Method == HttpMethod.Post) //如果是Post请求
            {
                    Stream stream = HttpContext.Current.Request.InputStream;
                    string responseJson = string.Empty;
                    StreamReader streamReader = new StreamReader(stream);
                    requestDataStr = streamReader.ReadToEnd();
                    stream.Position = 0;
            }
            if (actionContext.Request.Method == HttpMethod.Get) //如果是Get请求
            {
            		//根据请求类型拼接参数
           			NameValueCollection form = HttpContext.Current.Request.QueryString;
                    //第一步:取出所有get参数
                    IDictionary<string, string> parameters = new Dictionary<string, string>();
                    for (int f = 0; f < form.Count; f++)
                    {
                        string key = form.Keys[f];
                        parameters.Add(key, form[key]);
                    }

                    // 第二步:把字典按Key的字母顺序排序
                    IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
                    IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();

                    // 第三步:把所有参数名和参数值串在一起
                    StringBuilder query = new StringBuilder();
                    while (dem.MoveNext())
                    {
                        string key = dem.Current.Key;
                        string value = dem.Current.Value;
                        if (!string.IsNullOrEmpty(key))
                        {
                            query.Append(key).Append(value);
                        }
                    }
                    requestDataStr = query.ToString();
            }
            //计算Sign (即:计算timeStamps+nonces+appKeys+signtoken+requestDataStr的md5值)
            string computedSign = DESEncrypt.DesEncrypt(timeStamps.First() + nonces.First() + appKeys.First() + signtoken + requestDataStr);
            //用户传进来md5值和计算出来的比对一下,就知道数据是否有被篡改过
            if (signs.First().Equals(computedSign, StringComparison.CurrentCultureIgnoreCase))
            {
                return await continuation();
            }
            else
            {
                return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) { Content = new StringContent("sign认证失败") };
            }
        }
    }
}

⑤DES加密解密

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
 
namespace DAL
{
    /**//// <summary>
    /// DESEncrypt加密解密算法。
    /// </summary>
    public sealed class DESEncrypt
    {
        private DESEncrypt()
        {
            //
            // TODO: 在此处添加构造函数逻辑
            //
        }
 
        private static string key = "Glodonxx";
 
        /**//// <summary>
        /// 对称加密解密的密钥
        /// </summary>
        public static string Key
        {
            get
            {
                return key;
            }
            set
            {
                key = value;
            }
        }
 
        /**//// <summary>
        /// DES加密
        /// </summary>
        /// <param name="encryptString"></param>
        /// <returns></returns>
        public static string DesEncrypt(string encryptString)
        {
            byte[] keyBytes = Encoding.UTF8.GetBytes(key.Substring(0, 8));
            byte[] keyIV = keyBytes;
            byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
            DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
            MemoryStream mStream = new MemoryStream();
            CryptoStream cStream = new CryptoStream(mStream, provider.CreateEncryptor(keyBytes, keyIV), CryptoStreamMode.Write);
            cStream.Write(inputByteArray, 0, inputByteArray.Length);
            cStream.FlushFinalBlock();
            return Convert.ToBase64String(mStream.ToArray());
        }
 
        /**//// <summary>
        /// DES解密
        /// </summary>
        /// <param name="decryptString"></param>
        /// <returns></returns>
        public static string DesDecrypt(string decryptString)
        {
            byte[] keyBytes = Encoding.UTF8.GetBytes(key.Substring(0, 8));
            byte[] keyIV = keyBytes;
            byte[] inputByteArray = Convert.FromBase64String(decryptString);
            DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
            MemoryStream mStream = new MemoryStream();
            CryptoStream cStream = new CryptoStream(mStream, provider.CreateDecryptor(keyBytes, keyIV), CryptoStreamMode.Write);
            cStream.Write(inputByteArray, 0, inputByteArray.Length);
            cStream.FlushFinalBlock();
            return Encoding.UTF8.GetString(mStream.ToArray());
        }
    }
}

⑥获取Token,插入缓存

[HttpGet]
/// <summary>
/// 获取Token密钥
/// </summary>
/// <param name="UserDto"></param>
/// <returns></returns>
public async Task<string> GetToken([FromBody]UserDto userDto)
{
     //插入缓存
        AppToken token = (AppToken)HttpRuntime.Cache.Get(userDto.AppKey);
            if (token == null)
        {
            token = new AppToken();
            token.appKey = userDto.AppKey;
            token.SignToken = Guid.NewGuid();
            token.ExpireTime = DateTime.Now.AddMinutes(60);
            HttpRuntime.Cache.Insert(appToken.appKey, token, null, token.ExpireTime, TimeSpan.Zero);
        }
     return Common.Content(Status.Code.Success,"获取密钥成功",token.ToJson());
}

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值