zoukankan      html  css  js  c++  java
  • webapi框架搭建-安全机制(二)-身份验证

    webapi框架搭建系列博客

      身份验证(authentication)的责任是识别出http请求者的身份,除此之外尽量不要管其它的事。webapi的authentication我用authentication filter技术去解决。

    参考资料:

      https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/authentication-filters

    步骤如下

    创建authentication filter

      在项目里新建文件夹Security,并在此文件夹里创建IdentityBasicAuthentication类,代码如下

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Http.Filters;
    
    namespace webapi.Security
    {
        public class IdentityBasicAuthentication:IAuthenticationFilter
        {
            public bool AllowMultiple { get; }
            public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
            {
                throw new NotImplementedException();
            }
    
            public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
            {
                throw new NotImplementedException();
            }
        }
    }
    

      继承自IauthenticationFilter,实现自己的业务代码(后面再实现)

    注册authentication filter

      在webapi的config里加入filter,修改项目代码如下

     /// <summary>
            /// 返回webapi的httpconfiguration配置
            /// 用于webapi应用于owin技术时使用
            /// </summary>
            /// <returns></returns>
            public static HttpConfiguration OwinWebApiConfiguration(HttpConfiguration config)
            {
                config.MapHttpAttributeRoutes();//开启属性路由
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                );
                config.Filters.Add(new WebApiExceptionFilterAttribute());
                config.Filters.Add(new IdentityBasicAuthentication());
                return config;
            }
    

      即上一句:config.Filters.Add(new IdentityBasicAuthentication());

    现在webapi的authentication机制的结构已经完成,剩下要做的就是在结构里填其它的代码了。

    加入JWT机制

      其实jwt的代码我们要写的很少,不要自己去实现jwt规范,可以用已经有的jwt的.net包。

    在nuget里添加jwt.net包

    修改authentication filter代码,加入jwt逻辑

    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Security.Claims;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Http.Filters;
    using System.Web.Http.Results;
    using webapi.Common;
    using webapi.Configs;
    
    namespace webapi.Security
    {
        public class IdentityBasicAuthentication:IAuthenticationFilter
        {
            public bool AllowMultiple { get; }
            /// <summary>
            /// 请求先经过AuthenticateAsync
            /// </summary>
            /// <param name="context"></param>
            /// <param name="cancellationToken"></param>
            /// <returns></returns>
            public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
            {
                // 1、获取token
                context.Request.Headers.TryGetValues("token", out var tokenHeaders);
                // 2、如果没有token,不做任何处理
                if (tokenHeaders == null || !tokenHeaders.Any())
                {
                    return Task.FromResult(0);
                }
                // 3、如果token验证通过,则写入到identity,如果未通过则设置错误
                var jwtHelper=new JWTHelper();
                var payLoadClaims=jwtHelper.DecodeToObject(tokenHeaders.FirstOrDefault(),Config.JWTKey, out bool isValid, out string errMsg);
                if (isValid)
                {
                    var identity = new ClaimsIdentity("jwt", "userId", "roles");//只要ClaimsIdentity设置了authenticationType,authenticated就为true,后面的authority根据authenticated=true来做权限
                    foreach (var keyValuePair in payLoadClaims)
                    {
                        identity.AddClaim(new Claim(keyValuePair.Key, keyValuePair.Value.ToString()));
                    }
                    // 最好是http上下文的principal和进程的currentPrincipal都设置
                    context.Principal = new ClaimsPrincipal(identity);
                    Thread.CurrentPrincipal = new ClaimsPrincipal(identity);
                }
                else
                {
                    context.ErrorResult = new ResponseMessageResult(new HttpResponseMessage()
                    {
                        StatusCode = HttpStatusCode.ProxyAuthenticationRequired,
                        Content = new StringContent(errMsg)
                    });
                }
                return Task.FromResult(0);
            }
    
            /// <summary>
            /// 请求后经过AuthenticateAsync
            /// </summary>
            /// <param name="context"></param>
            /// <param name="cancellationToken"></param>
            /// <returns></returns>
            public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
            {
                return Task.FromResult(0);
            }
        }
    }
    

    附上JWTHelper.cs代码

    JWT.net的用法参考:https://github.com/jwt-dotnet/jwt

    using JWT;
    using JWT.Algorithms;
    using JWT.Serializers;
    using System;
    using System.Collections.Generic;
    
    namespace webapi.Common
    {
        public class JWTHelper
        {
            private IJsonSerializer _jsonSerializer;
            private IDateTimeProvider _dateTimeProvider;
            private IJwtValidator _jwtValidator;
            private IBase64UrlEncoder _base64UrlEncoder;
            private IJwtAlgorithm _jwtAlgorithm;
            private IJwtDecoder _jwtDecoder;
            private IJwtEncoder _jwtEncoder;
            public JWTHelper()
            {
                //非fluent写法
                this._jsonSerializer = new JsonNetSerializer();
                this._dateTimeProvider = new UtcDateTimeProvider();
                this._jwtValidator = new JwtValidator(_jsonSerializer, _dateTimeProvider);
                this._base64UrlEncoder = new JwtBase64UrlEncoder();
                this._jwtAlgorithm = new HMACSHA256Algorithm();
                this._jwtDecoder = new JwtDecoder(_jsonSerializer, _jwtValidator, _base64UrlEncoder);
                this._jwtEncoder = new JwtEncoder(_jwtAlgorithm, _jsonSerializer, _base64UrlEncoder);
    
    
            }
            public string Decode(string token, string key, out bool isValid, out string errMsg)
            {
                isValid = false;
                var result = string.Empty;
                try
                {
                    result = _jwtDecoder.Decode(token, key, true);
                    isValid = true;
                    errMsg = "正确的token";
                    return result;
                }
                catch (TokenExpiredException)
                {
                    errMsg = "token过期";
                    return result;
                }
                catch (SignatureVerificationException)
                {
                    errMsg = "签名无效";
                    return result;
                }
                catch (Exception)
                {
                    errMsg = "token无效";
                    return result;
                }
            }
    
            public T DecodeToObject<T>(string token, string key, out bool isValid, out string errMsg)
            {
                isValid = false;
                try
                {
                    var result = _jwtDecoder.DecodeToObject<T>(token, key, true);
                    isValid = true;
                    errMsg = "正确的token";
                    return result;
                }
                catch (TokenExpiredException)
                {
                    errMsg = "token过期";
                    return default(T);
                }
                catch (SignatureVerificationException)
                {
                    errMsg = "签名无效";
                    return default(T);
                }
                catch (Exception)
                {
                    errMsg = "token无效";
                    return default(T);
                }
            }
    
            public IDictionary<string, object> DecodeToObject(string token, string key, out bool isValid, out string errMsg)
            {
                isValid = false;
                try
                {
                    var result = _jwtDecoder.DecodeToObject(token, key, true);
                    isValid = true;
                    errMsg = "正确的token";
                    return result;
                }
                catch (TokenExpiredException)
                {
                    errMsg = "token过期";
                    return null;
                }
                catch (SignatureVerificationException)
                {
                    errMsg = "签名无效";
                    return null;
                }
                catch (Exception)
                {
                    errMsg = "token无效";
                    return null;
                }
            }
    
            #region 解密 
            public string Encode(Dictionary<string, object> payload, string key, int expiredMinute = 30)
            {
                if (!payload.ContainsKey("exp"))
                {
                    var exp = Math.Round((_dateTimeProvider.GetNow().AddMinutes(expiredMinute) - new DateTime(1970, 1, 1)).TotalSeconds);
                    payload.Add("exp", exp);
                }
                return _jwtEncoder.Encode(payload, key);
            }
            #endregion
    
        }
    }
    

      

    测试结果

    创建SecurityTestController.cs控制器

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Security.Claims;
    using System.Web.Http;
    using webapi.Common;
    
    namespace webapi.example
    {
        [RoutePrefix("api/security")]
        public class SecurityTestController : ApiController
        {
            /// <summary>
            /// 通过get请求里传过来的值生成token
            /// </summary>
            /// <returns></returns>
            [Route("token"),HttpGet]
            public IHttpActionResult GetToken()
            {
                var dic=new Dictionary<string,object>();
                foreach (var queryNameValuePair in Request.GetQueryNameValuePairs())
                {
                    dic.Add(queryNameValuePair.Key,queryNameValuePair.Value);
                }
                var token=new JWTHelper().Encode(dic, "shengyu",30);
                return Ok(token);
            }
    
            /// <summary>
            /// 返回token里加密的信息
            /// </summary>
            /// <returns></returns>
            [Route("GetUserInfoFromToken"),HttpGet]
            public IHttpActionResult GetUser()
            {
                var user = (ClaimsPrincipal)User;
                var dic=new Dictionary<string,object>();
                foreach (var userClaim in user.Claims)
                {
                    dic.Add(userClaim.Type,userClaim.Value);
                }
                return Ok(dic);
            }
        }
        
    }
    

     

    获取一个测试token

    现在将上面生成的token以附加到http request的head里,通过authentication机制,测试身份验证是否正常,结果如下

  • 相关阅读:
    兼容火狐几秒后跳转页面
    js 利用sina ip库获取ip及通信服务商
    jQuery.validate 中文API
    s:select标签的Map形式使用
    【原创随笔】reCAPTCHA加密验证Email地址,正确才可完整查看地址!
    eclipse编辑jsp文件和javascript代码很卡解决办法
    oracle 10 R2 静默安装 + psu
    【容易成功的十种能力你具备几种
    CentOS 新虚拟机网卡设置
    【与人沟通的技巧很重要】
  • 原文地址:https://www.cnblogs.com/shengyu-kmust/p/8283650.html
Copyright © 2011-2022 走看看