本文共 6930 字,大约阅读时间需要 23 分钟。
JSON Web Token(JWT)跨域身份验证解决方案,详细说明网上很多,这里不就重点介绍了。
问题:WebApi项目开发完成后部署到服务器后WebApi方法任何人都可以调用,有些项目肯定是不允许的,也过不了安全审计。那如何解决呢,这里介绍一种使用JWT 产生Token,为每个方法加一个Token的身份验证,这样,用户在正常登录后,获取到系统为该用户产生的Token,以后,每次调用服务端的WebApi方法时,都需要把这个Token送到服务端做验证,只有正确的Token,才能访问,否则,服务端返回异常。同时,还可以通过这个Token来限制登录,同一时间,相同的用户,只允许存在一个有效用户。关于产生的Token如何存储,或者不存储也可以,这要看具体业务场景了,具体事情具体分析,本文主要是通过示例介绍一种思路,文中示例客户端使用Cookie存储,服务端使用了Redis。下面还是上代码吧。
开发工具:VS2015
软件环境:redis
必需工具:
在需要加Token验证的webapi项目中加jwt.dll的引用
同时也把redisclient项目引用进来,可以上git hub下载。
然后新增一个用产生和验证Token的类JwtHelper
using System;using System.Collections.Generic;using System.Linq;using System.Web;using Newtonsoft.Json.Linq;using JWT;using JWT.Algorithms;using JWT.Serializers;using JWT.Exceptions;using CSRedis;namespace JwtToken{ public class JwtHelper { private const string secret = "scott"; public static string GenerateToken(string userCode) { try { IDateTimeProvider provider = new UtcDateTimeProvider(); var now = provider.GetNow(); var unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); // or use JwtValidator.UnixEpoch var secondsSinceEpoch = Math.Round((now - unixEpoch).TotalSeconds); //3分钟后失效 var payload = new Dictionary{ { "user",userCode}, { "exp",secondsSinceEpoch+ 60*3 }, { "jti",Guid.NewGuid() } }; 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; } catch { return ""; } } public static string VerifyToken(string Token) { string result = ""; try { IDateTimeProvider provider = new UtcDateTimeProvider(); IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); IJsonSerializer serializer = new JsonNetSerializer(); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtValidator validator = new JwtValidator(serializer, provider); IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm); var json = decoder.Decode(Token, secret, verify: true);//token为之前生成的字符串 JObject jobj = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(json); string userCode = jobj["user"].ToString(); RedisClient redis = new RedisClient("192.168.197.132", 6379); string originalToken = redis.Get(userCode); if(!Token.Equals(originalToken)) { result = "该帐号在其它地方登录"; } } catch (TokenExpiredException) { result = "Token 过期"; } catch (SignatureVerificationException) { result = "Token 不正确"; } catch (Exception ex) { result = "Token 验证出错"; } return result; } }}
然后在登录方法中,加入
string _token = JwtHelper.GenerateToken(登录ID);
将用户编码加到Token中,Token生成成功后,将该用户编码的Token存入redis并返回客户端,这里推荐将Token放入header中返加,而不是放在json中返回,放json中,容易被中途截获。
httpResponseMessage.Headers.Add("Authorization", _token);
header中的token数据
客户端在收到登录验证通过的信息后,从header中取出token保存到cookie中
$.ajax({ url: "/api/Login/CheckLogin", type: "Post", contentType: "application/json", dataType: "json", data: JSON.stringify(objPara), success: function (data, textStatus, jqXHR) { $.cookie("Authorization", jqXHR.getResponseHeader("Authorization"), { expires: 1, path: '/' }); }, error: function (XMLHttpRequest, textStatus, errorThrown) { } });
$.cookie("Authorization", jqXHR.getResponseHeader("Authorization"), { expires: 1, path: '/' })
这句代码就是通过jqXHR.getResponseHeader("Authorization")获取header上的token后保存到cookie("Authorization")中,有效时间1天,所有页面均可访问。现在用户已经获得有效Token,接下来,每访问一次webapi,都要将该token发送给服务端,然后验证。接下来有两步工作要做,发送和验证,先说发送,服务端通过header把token送过来,那客户端也应该是通过header将token送过去,先把刚存在cookie中的token取出来
var token = $.cookie("Authorization");
然后在ajax调用中加入headers的参数,如下图。
这样,token就包含在header上,被送到了服务端。
接下来,服务端如何进行验证,这部分是个重点。在这之前,先借用一下网上的一个解释说明一下,有助于理解验证方法。
将生成的Token发送给客户端后,随后,客户端的每次请求只需带上这个Token即可
一般都是将Token存放在Http请求的Headers中,也就是:context.Request.Headers,那么如何接收请求头中的Token呢?接收到Token后如何验证呢?
验证TOKEN时就需要构建 MVC Action 过滤器(AuthorizeAttribute)了,不过在构建 AuthorizeAttribute 之前,有必要对 AuthorizeAttribute 说明下,如下:
首先,AuthorizeAttribute 类位于System.Web.Http 命名空间下及System.Web.Mvc命名空间下,
一般情况下,如果你需要对C# MVC 控制器的访问作认证与授权,你需要用System.Web.Mvc命名空间下的 AuthorizeAttribute ,如果你需要对C# API 控制器的访问作认证与授权,你需要用System.Web.Http 命名空间下的 AuthorizeAttribute !
看完这个解释,接下来,需要新建一个验证属性类ApiTokenCheck,这里只针对webapi方法验证,所以类继承自System.Web.Http.AuthorizeAttribute
using System;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.Web;using System.Web.Http;using System.Web.Http.Controllers;namespace JwtToken{ public class ApiTokenCheck : AuthorizeAttribute { public override void OnAuthorization(HttpActionContext context) { var authHeader = context.Request.Headers.FirstOrDefault(a => a.Key == "Authorization");//获取接收的Token if (context.Request.Headers == null) { context.Response = context.Request.CreateErrorResponse(System.Net.HttpStatusCode.Unauthorized, new HttpError("Token 不正确")); } if(!context.Request.Headers.Any()) { context.Response = context.Request.CreateErrorResponse(System.Net.HttpStatusCode.Unauthorized, new HttpError("Token 不正确")); } if(authHeader.Key == null) { context.Response = context.Request.CreateErrorResponse(System.Net.HttpStatusCode.Unauthorized, new HttpError("Token 不正确")); } var token = authHeader.Value.FirstOrDefault(); string Verify = JwtHelper.VerifyToken(token); if(Verify != "") { context.Response = context.Request.CreateErrorResponse(System.Net.HttpStatusCode.Unauthorized, new HttpError(Verify)); } } }}
建好后,在需要进行验证的webapi方法加上验证属性即可
加了[ApiTokenCheck]的方法被请求调用时,都会先触发OnAuthorization方法进行验证,验证不通过通过下列代码返回异常信息给浏览器
context.Response = context.Request.CreateErrorResponse(System.Net.HttpStatusCode.Unauthorized, new HttpError(Verify));
OK,到这里,token生成,保存,发送,接收,验证 ,验证结果返回这个流程就结束了。
如有描述不清楚的地方,也可以直接看代码。
本文代码位置:
转载地址:http://cgqbi.baihongyu.com/