ASP.NET Core 2.0利用Jwt实现授权认证
背景
在微服务架构下,一般都会按不同的业务或功能将整个系统切分成不同的独立子系统,再通过rest api或rpc进行通讯并相互调用,形成各个子系统之间的串联结构。在这里,我们将采用rest api的通讯方式。
比如:
1、有一个“用户中心”独立子系统名为“lezhima.userhub”,是一个基于asp.net core mvc 2.0的项目。
2、有一个处理用户订单的独立子系统名为“lezhima.userorder”,是一个基于asp.net core webp api 2.0的项目。
3、同时还有一个处理用户文件上传的独立子系统名为“lezhima.userupload”,是一个基于asp.net core webp api 2.0的项目。
业务关系如下:
用户成功登录后进入“lezhima.userhub”,在用户查看订单时通过前端ajax调用“lezhima.userorder”的web api接口,在用户上传图片是通过前端ajax调用“lezhima.userupload”的web api接口。
至此,我们了解了上面的业务关系后,心里一定产生出如下两个问题:
1、如何保障“lezhima.userorder”与“lezhima.userupload”两个独立系统内的web api接口安全,因为它们已经被暴露在了前端。
2、如何在“lezhima.userhub”站颁发token。
那么,带着问题我们下面就结合asp.net core 自带的jwt技术来讨论具体的实现(也许聪明的你有更好的解决方法,请一定告知我,谢谢)。
jwt 全名为:json web token,是一个很成熟的技术,园子里也有很多这方面的知识,我这里就不再重述了。
实现原理
“lezhima.userhub”站因为已经做了登录验证,我们暂且认为它是可信的,所以在前端ajax请求“lezhima.userorder”站的web api接口时先到自已后端去生成一个token,并随之同本次跨站请求一块携带至“lezhima.userorder”站,“lezhima.userorder”站验证请求头中的token是否合法,如合法则继续路由到具体方法中,否则结束请求。“lezhima.userupload”站原理与“lezhima.userorder”相同。
实现代码
lezhima.userhub颁发token代码:
/// <summary>
/// 颁发一个指定有效期的token,并将当前登录的用户id传递进来
/// </summary>
/// <param name="currentuserid"></param>
/// <param name="expiresminutes"></param>
/// <returns></returns>
public static async task<string> getaccesstoken(string currentuserid,int expiresminutes=2)
{
return await task.run(() =>
{
//约定私钥,下面三个参数可放到配置文件中
var secret = "nguznmnlnzqtzthkzc00yjrh";
//发行者
var iss = "andre";
//接受者
var aud = "andre";
if (string.isnullorempty(secret) || string.isnullorempty(iss) || string.isnullorempty(aud))
return "";
if (string.isnullorempty(currentuserid))
currentuserid = guid.newguid().tostring();
var now = datetime.utcnow;
var claims = new claim[]
{
new claim(jwtregisteredclaimnames.sub, currentuserid),
new claim(jwtregisteredclaimnames.iat, now.touniversaltime().tostring(), claimvaluetypes.integer64)
};
var signingkey = new symmetricsecuritykey(encoding.ascii.getbytes(secret));
var jwt = new jwtsecuritytoken(
issuer: iss,
audience: aud,
claims: claims,
notbefore: now,
expires: now.add(timespan.fromminutes(expiresminutes)),
signingcredentials: new signingcredentials(signingkey, securityalgorithms.hmacsha256)
);
return new jwtsecuritytokenhandler().writetoken(jwt); //生成一个新的token
});
}
lezhima.userhub前端ajax跨站请求代码:
//封装一个ajax请求公共方法
function getwebdatabyobject(url, requestmethon, paramter) {
jquery.support.cors = true;
apiurl = 'http://127.0.0.1:8012/';
var token = gettoken(); //调用本站内的token颁发web api接口
var result = [];
$.ajax({
type: requestmethon,
url: apiurl + url,
data: paramter,
async: false,
beforesend: function (xhr) {
//将token携带到请求头中
xhr.setrequestheader("authorization", "bearer " + token);
},
success: function (data) {
result = data;
},
error: function (xmlhttprequest, textstatus, errorthrown) {
// 状态码
console.log(xmlhttprequest.status);
// 状态
console.log(xmlhttprequest.readystate);
// 错误信息
console.log(textstatus);
}
});
return result;
}
“lezhima.userorder”站开启jwt的token验证,在startup.cs里添加如下代码:
public iserviceprovider configureservices(iservicecollection services)
{
services.addcors();
//从配置文件中获取私钥、发行者、接受者三个参数
//三个参数的值必需与颁发token站相同
var audienceconfig = configuration.getsection("audience");
var signingkey = new symmetricsecuritykey(encoding.ascii.getbytes(audienceconfig["secret"]));
var tokenvalidationparameters = new tokenvalidationparameters
{
validateissuersigningkey = true,
issuersigningkey = signingkey,
validateissuer = true,
validissuer = audienceconfig["iss"],
validateaudience = true,
validaudience = audienceconfig["aud"],
validatelifetime = true,
clockskew = timespan.zero,
requireexpirationtime = true,
};
//注入jwt验证
services.addauthentication(jwtbearerdefaults.authenticationscheme)
.addjwtbearer(options => {
options.requirehttpsmetadata = false;
options.tokenvalidationparameters = tokenvalidationparameters;
});
services.addmvc();
var builder = new containerbuilder();
builder.registermodule(new evolution());
builder.populate(services);
var container = builder.build();
return container.resolve<iserviceprovider>();
}
public void configure(iapplicationbuilder app, ihostingenvironment env)
{
if (env.isdevelopment())
{
app.usedeveloperexceptionpage();
}
app.usecors(builder =>
builder.withorigins("*")
.allowanyheader()
.allowanymethod()
.allowcredentials()
);
//开启验证
app.useauthentication();
app.usemvc();
}
“lezhima.userorder”站内的控制器里添加验证过滤器[authorize],如下代码:
[route("api/[controller]")]
//添加过滤器后,该控制器内所有action都将进行token验证
[authorize]
public class ordercontroller : controller
{
}
至此,基于asp.net core的jwt跨站验证token方案就全部完成了,是不是很简单呀^_^ ^_^
推荐阅读
-
ASP.NET Core 3.0 一个 jwt 的轻量角色/用户、单个API控制的授权认证库
-
ASP.NET Core 2.0利用Jwt实现授权认证
-
ASP.NET Core使用JWT认证授权的方法
-
Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员、后台管理员同时登录
-
asp.net core3.1cookie和jwt混合认证授权实现多种身份验证方案
-
ASP.NET Core 使用 JWT 自定义角色/策略授权需要实现的接口
-
ASP.NET Core学习之使用JWT认证授权详解
-
浅谈ASP.NET Core 中jwt授权认证的流程原理
-
ASP.Net Core3.0中使用JWT认证的实现
-
ASP.NET Core使用JWT自定义角色并实现策略授权需要的接口