运行环境

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 一系列教程