C#实现JWT无状态验证的实战应用解析
前言
本文主要介绍JWT的实战运用。
准备工作
首先我们创建一个Asp.Net的,包含MVC和WebApi的Web项目。
然后使用Nuget搜索JWT,安装JWT类库,如下图。
设计思路
这里我们简单的做了一个token验证的设计,设计思路如下图所示:
代码实现
缓存
首先,我们先开发工具类,根据设计思路图可得知,我们需要一个缓存类,用于在服务器端存储token。
编写缓存相关类代码如下:
publicclassCacheHelper
{
publicstaticobjectGetCache(stringkey)
{
returnHttpRuntime.Cache[key];
}
publicstaticTGetCache(stringkey)whereT:class
{
return(T)HttpRuntime.Cache[key];
}
publicstaticboolContainsKey(stringkey)
{
returnGetCache(key)!=null;
}
publicstaticvoidRemoveCache(stringkey)
{
HttpRuntime.Cache.Remove(key);
}
publicstaticvoidSetKeyExpire(stringkey,TimeSpanexpire)
{
objectvalue=GetCache(key);
SetCache(key,value,expire);
}
publicstaticvoidSetCache(stringkey,objectvalue)
{
_SetCache(key,value,null,null);
}
publicstaticvoidSetCache(stringkey,objectvalue,TimeSpantimeout)
{
_SetCache(key,value,timeout,ExpireType.Absolute);
}
publicstaticvoidSetCache(stringkey,objectvalue,TimeSpantimeout,ExpireTypeexpireType)
{
_SetCache(key,value,timeout,expireType);
}
privatestaticvoid_SetCache(stringkey,objectvalue,TimeSpan?timeout,ExpireType?expireType)
{
if(timeout==null)
HttpRuntime.Cache[key]=value;
else
{
if(expireType==ExpireType.Absolute)
{
DateTimeendTime=DateTime.Now.AddTicks(timeout.Value.Ticks);
HttpRuntime.Cache.Insert(key,value,null,endTime,Cache.NoSlidingExpiration);
}
else
{
HttpRuntime.Cache.Insert(key,value,null,Cache.NoAbsoluteExpiration,timeout.Value);
}
}
}
}
///
///过期类型
///
publicenumExpireType
{
///
///绝对过期
///注:即自创建一段时间后就过期
///
Absolute,
///
///相对过期
///注:即该键未被访问后一段时间后过期,若此键一直被访问则过期时间自动延长
///
Relative,
}
如上述代码所示,我们编写了缓存帮助类—CacheHelper类。
CacheHelper类:使用HttpRuntime的缓存,类里实现缓存的增删改,因为使用的是HttpRuntime,所以,如果没有设置缓存的超时时间,则缓存的超时时间等于HttpRuntime.Cache配置的默认超时时间。
如果网站挂载在IIS里,那么,HttpRuntime.Cache配置超时时间的地方在该网站的应用程序池中,如下图:
Jwt的帮助类
现在我们编写Jwt的帮助类,代码如下:
publicclassJwtHelper
{
//私钥
publicconststringsecret="MIGfMA0GCSqGSIb3DQEBAQUAA4GNAmD7RTE2drj6hf3oZjJpMPZUQ1Qjb5H3K3PNwIDAQAB";
///
///
///生成JwtToken
///
///不敏感的用户数据
///
publicstaticstringSetJwtEncode(stringusername,intexpiresMinutes)
{
//格式如下
varpayload=newDictionary
{
{"username",username},
{"exp",expiresMinutes},
{"domain",""}
};
IJwtAlgorithmalgorithm=newHMACSHA256Algorithm();
IJsonSerializerserializer=newJsonNetSerializer();
IBase64UrlEncoderurlEncoder=newJwtBase64UrlEncoder();
IJwtEncoderencoder=newJwtEncoder(algorithm,serializer,urlEncoder);
vartoken=encoder.Encode(payload,secret);
returntoken;
}
///
///根据jwtToken获取实体
///
///jwtToken
///
publicstaticIDictionaryGetJwtDecode(stringtoken)
{
IJsonSerializerserializer=newJsonNetSerializer();
IDateTimeProviderprovider=newUtcDateTimeProvider();
IJwtValidatorvalidator=newJwtValidator(serializer,provider);
IBase64UrlEncoderurlEncoder=newJwtBase64UrlEncoder();
IJwtAlgorithmalgorithm=newHMACSHA256Algorithm();
IJwtDecoderdecoder=newJwtDecoder(serializer,validator,urlEncoder,algorithm);
vardicInfo=decoder.DecodeToObject(token,secret,verify:true);//token为之前生成的字符串
returndicInfo;
}
}
代码很简单,实现了JWT的Code的创建和解析。
注:JWT的Code虽然是密文,但它是可以被解析的,所以我们不要在Code里存储重要信息,比如密码。
JWT的Code与解析后的内容如下图所示,左边未Code,右边未解析的内容。
AuthenticationHelper验证帮助类
现在,我们已经可以编写验证类了,利用刚刚已创建的缓存帮助类和JWT帮助类。
AuthenticationHelper验证帮助类代码如下:
publicclassAuthenticationHelper
{
///
///默认30分钟
///
///
publicstaticvoidAddUserAuth(stringusername)
{
vartoken=JwtHelper.SetJwtEncode(username,30);
CacheHelper.SetCache(username,token,newTimeSpan(TimeSpan.TicksPerHour/2));
}
publicstaticvoidAddUserAuth(stringusername,TimeSpants)
{
vartoken=JwtHelper.SetJwtEncode(username,ts.Minutes);
CacheHelper.SetCache(username,token,ts);
}
publicstaticstringGetToken(stringusername)
{
varcachetoken=CacheHelper.GetCache(username);
returncachetoken.ParseToString();
}
publicstaticboolCheckAuth(stringtoken)
{
vardicInfo=JwtHelper.GetJwtDecode(token);
varusername=dicInfo["username"];
varcachetoken=CacheHelper.GetCache(username.ToString());
if(!cachetoken.IsNullOrEmpty()&&cachetoken.ToString()==token)
{
returntrue;
}
else
{
returnfalse;
}
}
}
如代码所示,我们实现了验证token创建、验证token获取、验证Token校验三个方法。
到此,我们的基础代码已经编写完了,下面进入验证的应用。
Fliter
首先,在Global.asax文件中,为我们WebApi添加一个过滤器,代码如下:
publicclassWebApiApplication:System.Web.HttpApplication
{
protectedvoidApplication_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
//webapiFilter
System.Web.Http.GlobalConfiguration.Configuration.Filters.Add(newHttpPermissionFilter());
System.Web.Http.GlobalConfiguration.Configuration.Filters.Add(newHttpExceptionFilter());
//mvcFliter
System.Web.Mvc.GlobalFilters.Filters.Add(newMvcExceptionFilter());
System.Web.Mvc.GlobalFilters.Filters.Add(newMvcPermissionFilter());
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
代码中创建了四个过滤器,分别是MVC的请求和异常过滤器和WebApi的请求和异常过滤器。
这里我们主要看WebApi的请求过滤器——HttpPermissionFilter。代码如下:
publicclassHttpPermissionFilter:System.Web.Http.Filters.ActionFilterAttribute
{
publicoverridevoidOnActionExecuting(HttpActionContextactionContext)
{
stringurl="请求Url"+actionContext.Request.RequestUri.ToString();
varaction=actionContext.ActionDescriptor.ActionName.ToLower();
varcontroller=actionContext.ControllerContext.ControllerDescriptor.ControllerName.ToLower();
if(controller!="login"&&controller!="loginout")
{
//客户端段token获取
vartoken=actionContext.Request.Headers.Authorization!=null?actionContext.Request.Headers.Authorization.ToString():"";
//服务端获取token与客户端token进行比较
if(!token.IsNullOrEmpty()&&AuthenticationHelper.CheckAuth(token))
{
//认证通过,可进行日志等处理
}
else
{
thrownewException("Token无效");
}
}
}
}
我们的HttpPermissionFilter类继承了System.Web.Http.Filters.ActionFilterAttribute,这样他就可以截获所有的WebApi请求了。
然后我们重写了他的OnActionExecuting方法,在方法里,我们查询到当前请求的Controller的名称,然后对其进行了一个简单的判断,如果是login(登录)或loginout(登出),那我们就不对他的token进行验证。如果是其他请求,则会从请求的Headers的Authorization属性里读取token,并使用AuthenticationHelper类对这个token进行正确性的验证。
WebApi接口
现在我们编写WebApi接口,编写一个登录接口和一个普通请求接口。
登录接口:这里我们使用AuthenticationHelper类创建一个token,并把他存储到缓存中。
然后再把token返回给调用者。
普通接口:这里我们不做任何操作,就是简单的返回成功,因为是否可以访问这个接口,已经又Filter控制了。
代码如下:
publicclassLoginController:ApiController
{
publicstringGet(stringusername,stringpwd)
{
AuthenticationHelper.AddUserAuth(username,newTimeSpan(TimeSpan.TicksPerMinute*5));//5分钟
stringtoken=AuthenticationHelper.GetToken(username);
returntoken;
}
}
publicclassRequestController:ApiController
{
publicstringGet()
{
return"请求成功";
}
}
测试页面
现在我们编写测试页面,这里我们实现三个按钮,登录、带token访问Api、无token访问Api。
代码如下:
测试JWT
登录 带token访问Api 无token访问Api
测试结果如下:
如上图所示,我们已经成功实现简单的token验证。
到此JWT的实战应用就已经介绍完了。
代码已经传到Github上了,欢迎大家下载。
Github地址:https://github.com/kiba518/JwtNet
到此这篇关于C#实现JWT无状态验证的实战应用的文章就介绍到这了,更多相关C#实现JWT无状态验证内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。