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机制,测试身份验证是否正常,结果如下

webapi框架搭建-安全机制(二)-身份验证的更多相关文章

  1. webapi框架搭建-安全机制(三)-简单的基于角色的权限控制

    webapi框架搭建系列博客 上一篇已经完成了“身份验证”,如果只是想简单的实现基于角色的权限管理,我们基本上不用写代码,微软已经提供了authorize特性,直接用就行. Authorize特性的使 ...

  2. webapi框架搭建-安全机制(四)-可配置的基于角色的权限控制

    webapi框架搭建系列博客 在上一篇的webapi框架搭建-安全机制(三)-简单的基于角色的权限控制,某个角色拥有哪些接口的权限是用硬编码的方式写在接口上的,如RBAuthorize(Roles = ...

  3. webapi框架搭建-安全机制(一)

    本系列博客链接:webapi框架搭建系列博客 前言 webapi接口是开放给外部使用的,包括接口的地址,传参的规范,还有返回结果的说明.正因为接口的开放性,使得接口的安全很重要.试想一下,用抓包工具( ...

  4. webapi框架搭建系列博客

    webapi框架搭建系列博客 webapi框架搭建-创建项目(一) webapi框架搭建-创建项目(二)-以iis为部署环境的配置 webapi框架搭建-创建项目(三)-webapi owin web ...

  5. webapi框架搭建-创建项目(二)-以iis为部署环境的配置

    上篇:webapi快速框架搭建-创建项目(一) 在"创建项目(一)"这一篇里已经创建了一个空的项目,但项目上什么都没有,本篇描述如何将webapi配置成部署在iis上. 步骤 用n ...

  6. webapi框架搭建-创建项目(三)-webapi owin

    上一篇:创建项目(二) 在上一篇里,我们已经创建好了webapi应用,并已经部署到iis里,本篇讲如何用owin自宿主或是iis宿主来部署webapi应用. owin介绍 传统的asp.net网站只能 ...

  7. webapi框架搭建-日志管理log4net

    前言 本篇讲怎么在前几篇已经创建好的项目里加上日志处理机制,我们采用Log4net技术.跟多的log4net技术的细节请查阅log4net的官网. log4net官网:http://logging.a ...

  8. webapi框架搭建-webapi异常处理

    webapi框架搭建系列博客 前言 上一篇我们已经完成了项目的日志管理,在项目开发中日志会经常记录程序中的异常,供后续问题排查使用.本篇讲如何在webapi里加入异常处理机制. 目的和原则 1.程序任 ...

  9. webapi框架搭建-数据访问ef code first

    webapi框架搭建系列博客 为什么用ef? 我相信很多博友和我一样都有这种“选择困难症”,我曾经有,现在也有,这是技术人的一个通病——总想用“更完美”的方式去实现,导致在技术选择上犹豫不决,或总是推 ...

随机推荐

  1. 初涉JSP+JDBC 基于SQL2008的登陆验证程序

    简单的以代码的形式纪念一下,因为现在还没有解决SQL2008驱动的问题,并且有好多东西要学,所以日后会有更新~ 所安装的软件有:SQL2008,eclipse,tomcat,JDK,涉及环境配置.等等 ...

  2. Spring笔记②--各种属性注入

    Ioc 反转控制 反转资源获取的方向 分离接口与实现 采用工厂模式 采用反转控制   Di 依赖注入 依赖容器把资源注入   配置bean 通过全类名(反射) 配置形式:基于xml方式 Ioc容器的b ...

  3. KEIL C51代码优化详细分析

    阅读了<单片机与嵌入式系统应用>2005年第10期杂志<经验交流>栏目的一篇文章<Keil C51对同一端口的连续读取方法>(原文)后,笔者认为该文并未就此问题进行 ...

  4. python下的Box2d物理引擎的配置

    /******************************* I come back! 由于已经大四了,正在找工作 导致了至今以来第二长的时间内没有更新博客.向大家表示道歉 *********** ...

  5. msg: ReferenceError: Can't find variable: urchinTracker

    在调试的时候发现selenium在启动浏览器打开url地址的时候报这个错误 msg: ReferenceError: Can't find variable: urchinTracker 检查了脚本发 ...

  6. loadrunner在win10破解提示:Cannot save the license information because acceses to the registry is denied的解决办法

    方法1 下图1-1中提示就是说明了破解的时候权限不足导致,解决办法就是使用管理员权限打开loadrunner破解就好了,但是右键“以管理员身份运行”选项打开loadrunner又是会提示1-2中的问题 ...

  7. 基本数据类型用== 比较数值 引用类型用==比较的是jvm中的地址 比较数值用equal

    基本数据类型用== 比较数值     引用类型用==比较的是jvm中的地址 比较数值用equal

  8. LAMP 环境下添加多个虚拟主机(网站)

    Ubuntu系统 #在/var/www目录下新建两个文件夹bbs和oa,执行完以上命令后,/var/www目录下有bbs.oa三个文件夹,名称与二级域名对应,分别存放三个系统的php文件,这样便于日后 ...

  9. MT【151】传球问题

    (清华2017.4.29标准学术能力测试10) 甲.乙.丙.丁四人做相互传球的游戏,第一次甲传给其他三人中的一人,第二次由拿到球的人再传给其他三人中的一人,这样的传球共进行了$4$次,则第四次球传回甲 ...

  10. MT【123】利用第一次的技巧

    已知 \(r_1=0,r_{100}=0.85,(r_k\) 表示投 k 次投中的概率.) 求证:(1)是否存在\(n_0\)使得\(r_{n_0}=0.5\) (2)是否存在\(n_1\)使得\(r ...