使用.Net Core + Vue + IdentityServer4 + Ocelot 实现一个简单的DEMO +源码
程序员文章站
2022-07-21 19:07:50
.Net Core + Vue + IdentityServer4 + Ocelot ......
运行环境
vue 使用的是d2admin:
github地址:https://github.com/fengddd/permissionadmin.git
net core的环境:webapi 使用的是:net core sdk2.1 identityserver和ocelot:net core sdk2.2
github地址:https://github.com/fengddd/identityserverocelotdemo.git
我也是在初学阶段,所以有些地方可能描述的不是很清楚,可以下载源码查看,也可能大家一起交流,学习
建立identityserver4项目
根据 根据edison zhou大佬的博客,配置好 quickstart ui 以及identityserver的依赖项
新建identityserver4的配置文件identityconfig
public static ienumerable<identityresource> getidentityresources() { return new identityresource[] { new identityresources.openid(), new identityresources.profile(), new identityresource("delimitclaim","delimitclaim",new list<string>(){ "role", "name"}), //在claims添加role 和 name信息 }; } public static ienumerable<apiresource> getapiresource() { return new list<apiresource> { new apiresource("identityserverapi", "identityserverapi"), }; } public static ienumerable<client> getclients() { new client { clientid = "js", //客户端id clientname = "javascript client", //客户端名称 allowedgranttypes = granttypes.code, //授权模式 //allowedgranttypes = granttypes.implicit, requirepkce = true, requireclientsecret = false, requireconsent = false, //禁用 consent 页面确认 allowaccesstokensviabrowser = true, alwaysincludeuserclaimsinidtoken = true, redirecturis = { "http://localhost:8080/#/identityservercallback", //登陆后回调页面 "http://localhost:8080/#/identityserverrefreshtoken" //刷新token的页面 }, postlogoutredirecturis = { "http://localhost:8080/#/identityserverclient" },//注销退出后跳转的页面(登录页) allowedcorsorigins = { "http://localhost:8080" }, //跨域 accesstokenlifetime = 60, //accesstoken 的有效时间 allowedscopes = { identityserverconstants.standardscopes.openid, identityserverconstants.standardscopes.profile, "identityserverapi", //授权的scopes "delimitclaim" //claims 信息 }, allowofflineaccess = true, } }
有时我们需要自定义验证以及自定义一些claim的信息,所以需要实现 iprofileservice 接口
public virtual task getprofiledataasync(profiledatarequestcontext context) { context.logprofilerequest(logger); //判断是否有请求claim信息 if (context.requestedclaimtypes.any()) { var userclaims = new list<claim> { new claim("demo1", "测试1"), new claim("demo2", "测试2"), }; list<testuser> userlist = new list<testuser>() { new testuser(){subjectid = "cfac01a9-ba15-4678-bccb-cc22d7896362",password = "123456",username="李锋",claims = userclaims}, new testuser(){subjectid = "cfac01a9-ba15-4678-bccb-cc22d7855555",password = "123456",username="张三"}, }; testuserstore userstore = new testuserstore(userlist); //根据用户唯一标识查找用户信息 var user = userstore.findbysubjectid(context.subject.getsubjectid()); if (user != null) { //调用此方法以后内部会进行过滤,只将用户请求的claim加入到 context.issuedclaims 集合中 这样我们的请求方便能正常获取到所需claim context.addrequestedclaims(user.claims); } //context.issuedclaims=userclaims; } context.logissuedclaims(logger); return task.completedtask; } /// <summary> /// 验证用户是否有效 例如:token创建或者验证 /// </summary> /// <param name="context">the context.</param> /// <returns></returns> public virtual task isactiveasync(isactivecontext context) { logger.logdebug("isactive called from: {caller}", context.caller); var userclaims = new list<claim> { new claim("demo1", "测试1"), new claim("demo2", "测试2"), }; list<testuser> userlist = new list<testuser>() { new testuser(){subjectid = "cfac01a9-ba15-4678-bccb-cc22d7896362",password = "123456",username="李锋",claims = userclaims}, new testuser(){subjectid = "cfac01a9-ba15-4678-bccb-cc22d7855555",password = "123456",username="张三"}, }; testuserstore userstore = new testuserstore(userlist); var user = userstore.findbysubjectid(context.subject.getsubjectid()); context.isactive = user?.isactive == true; return task.completedtask; }
其中关于claims的地方这里做测试所以直接实例出来的数据,这里可以通过读取数据库进行验证,以及添加claims的信息
在startup 的configureservices 注入identityserver信息 ,configure下注册useidentityserver中间件
services.addidentityserver() .adddevelopersigningcredential() .addinmemoryidentityresources(identityconfig.getidentityresources()) .addinmemoryapiresources(identityconfig.getapiresource()) .addinmemoryclients(identityconfig.getclients()) //.addtestusers(identityconfig.getusers().tolist()) .addprofileservice<identityprofileservice>(); //使用的是code模式,使用自定义claims信息 //.addresourceownervalidator<identityresourceownerpasswordvalidator>(); app.useidentityserver();
然后找到quickstart中的登录方法,这里修改为从数据库读取验证用户信息
建立vue项目
安装依赖项:oidc-client :npm install oidc-client --save axios:npm install axios --save
添加 identityserverclient 页面
添加 identityservercallback页面
添加 identityservercallback页面
访问identityserverclient 页面时会自动跳转到identityserver4的登录页面,输入账号密码,验证成功之后,会跳转到identityservercallback页面,然后在identityservercallback页面设置路由,跳转到目标页面
这里主要讲哈前端的配置,建议看https://www.cnblogs.com/fireworkseasycool/p/10576911.html教程和oidc-client官方文档https://github.com/identitymodel/oidc-client-js/wiki
注意使用:自动刷新token使用自动刷新token需要accesstokenexpiringnotificationtime和automaticsilentrenew 一起设置,当accsstoken要过期前:accesstokenexpiringnotificationtime设置的时间,会去请求
identityserver4 connect/token接口,刷新token,请求到token以后会触发adduserloaded事件,我们可以把token 保存在浏览器的缓存中(cookies,localstorage)中,然后在axios中全局拦截请求,添加headers
信息
authorization 是token的信息,x-claims是claims的信息,传递authorization 是为了identityserver 进行验证授权,x-claims是为了后面结合ocelot,携带一些ocelot下游需要的参数进去
建立ocelotgateway项目
添加ocelot.json文件
"reroutes": [ { "downstreampathtemplate": "/api/{url}", "downstreamscheme": "http", "downstreamhostandports": [ { "host": "localhost", "port": 44375 } ], "upstreampathtemplate": "/api/{url}", "upstreamhttpmethod": [ "get", "post" ], "priority": 2, "authenticationoptions": { "authenticationproviderkey": "identityserverkey", "allowscopes": [ "identityserverapi", "delimitclaim" ] }, "ratelimitoptions": { "clientwhitelist": [ //白名单 ], "enableratelimiting": true, //启用限流 "period": "1m", "periodtimespan": 30, "limit": 5 }, "qosoptions": { "exceptionsallowedbeforebreaking": 3, "durationofbreak": 3000, "timeoutvalue": 5000 }
]
}
authenticationoptions 表示认证的信息:identityserver4验证信息,authenticationproviderkey 填写 addidentityserverauthentication 的key 一致,然后配置跨域,注册中间件
services.addauthentication() .addidentityserverauthentication("identityserverkey", options => { options.authority = "http://localhost:17491"; options.apiname = "identityserverapi"; options.supportedtokens = identityserver4.accesstokenvalidation.supportedtokens.both; options.requirehttpsmetadata = false; }); services.addocelot() .addconsul() .addpolly(); //配置跨域处理 services.addcors(options => { options.addpolicy("any", builder => { builder.allowanyorigin() //允许任何来源的主机访问 .allowanymethod() .allowanyheader() .allowcredentials();//指定处理cookie }); }); //配置cors app.usecors("any"); app.useauthentication();
program中添加
public static iwebhostbuilder createwebhostbuilder(string[] args) { return webhost.createdefaultbuilder(args) .configureappconfiguration((hostingcontext, config) => { config .setbasepath(hostingcontext.hostingenvironment.contentrootpath) .addocelot(hostingcontext.hostingenvironment) //ocelot合并配置文件,不能出现同样的一个端口,多个路由 .addenvironmentvariables(); }) .usestartup<startup>(); }
学习的资料链接,参考资料
edison zhou: identityserver4和ocelot
晓晨master: identityserver4
solenovex : identityserver4 bibi上还有identityserver4视频喔
灭蒙鸟: 入门教程:js认证和webapi
.net框架学苑: ocelot 一系列教程
下一篇: 夏季喝姜茶竟有这么多好处