IdentityServer4与ocelot实现认证与客户端统一入口

关于IdentityServer4与ocelot博客园里已经有很多介绍我这里就不再重复了。

ocelot与IdentityServer4组合认证博客园里也有很多,但大多使用ocelot内置的认证,而且大多都是用来认证API的,查找了很多资料也没看到如何认证oidc,所以这里的ocelot实际只是作为统一入口而不参与认证,认证的完成依然在客户端。代码是使用IdentityServer4的Quickstart5_HybridAndApi 示例修改的。项目结构如下

640?wx_fmt=png 

一 ocelot网关

我们先在示例添加一个网关。

修改launchSettings.json中的端口为54660

640?wx_fmt=png

640?wx_fmt=png

配置文件如下

{

  "ReRoutes": [

    { // MvcClient

      "DownstreamPathTemplate": "/MvcClient/{route}",

      "DownstreamScheme": "http",

      "DownstreamHostAndPorts": [

        {

          "Host": "localhost",

          "Port": 50891

        }

      ],

      "UpstreamPathTemplate": "/MvcClient/{route}",

      "UpstreamHeaderTransform": {

        "X-Forwarded-For": "{RemoteIpAddress}"

      }

    },

    { // signin-oidc

      "DownstreamPathTemplate": "/signin-oidc",

      "DownstreamScheme": "http",

      "DownstreamHostAndPorts": [

        {

          "Host": "localhost",

          "Port": 50891

        }

      ],

      "UpstreamPathTemplate": "/signin-oidc",

      "UpstreamHeaderTransform": {

        "X-Forwarded-For": "{RemoteIpAddress}"

      }

    },

    { // signout-callback-oidc

      "DownstreamPathTemplate": "/signout-callback-oidc",

      "DownstreamScheme": "http",

      "DownstreamHostAndPorts": [

        {

          "Host": "localhost",

          "Port": 50891

        }

      ],

      "UpstreamPathTemplate": "/signout-callback-oidc",

      "UpstreamHeaderTransform": {

        "X-Forwarded-For": "{RemoteIpAddress}"

      }

    },

    { // MyApi

      "DownstreamPathTemplate": "/MyApi/{route}",

      "DownstreamScheme": "http",

      "DownstreamHostAndPorts": [

        {

          "Host": "localhost",

          "Port": 50890

        }

      ],

      "UpstreamPathTemplate": "/MyApi/{route}",

      "UpstreamHeaderTransform": {

        "X-Forwarded-For": "{RemoteIpAddress}"

      }

    },

    { // IdentityServer

      "DownstreamPathTemplate": "/{route}",

      "DownstreamScheme": "http",

      "DownstreamHostAndPorts": [

        {

          "Host": "localhost",

          "Port": 50875

        }

      ],

      "UpstreamPathTemplate": "/IdentityServer/{route}",

      "UpstreamHeaderTransform": {

        "X-Forwarded-For": "{RemoteIpAddress}"

      }

    },

    { // IdentityServer

      "DownstreamPathTemplate": "/{route}",

      "DownstreamScheme": "http",

      "DownstreamHostAndPorts": [

        {

          "Host": "localhost",

          "Port": 50875

        }

      ],

      "UpstreamPathTemplate": "/{route}",

      "UpstreamHeaderTransform": {

        "X-Forwarded-For": "{RemoteIpAddress}"

      }

    }

  ]

}

这里我们定义3个下游服务,MvcClient,MyApi,IdentityServer,并使用路由特性把signin-oidc,signout-callback-oidc导航到MvcClient,由MvcClient负责生成最后的Cooike。并将默认路由指定到IdentityServer服务。


在ConfigureServices中添加Ocelot服务。

services.AddOcelot()

            .AddCacheManager(x =>

            {

                x.WithDictionaryHandle();

            })

            .AddPolly()

在Configure中使用Ocelot中间件

app.UseOcelot().Wait();

Ocelot网关就部署完成了。

二 修改QuickstartIdentityServer配置

首先依然是修改launchSettings.json中的端口为50875

在ConfigureServices中修改AddIdentityServer配置中的PublicOrigin和IssuerUri的Url为http://localhost:54660/IdentityServer/

services.AddIdentityServer(Option =>

            {

                Option.PublicOrigin = "http://localhost:54660/IdentityServer/";

                Option.IssuerUri = "http://localhost:54660/IdentityServer/";

            })

                .AddDeveloperSigningCredential()

                .AddInMemoryIdentityResources(Config.GetIdentityResources())

                .AddInMemoryApiResources(Config.GetApiResources())

                .AddInMemoryClients(Config.GetClients())

                .AddTestUsers(Config.GetUsers());


这样一来发现文档中的IdentityServer地址就变为网关的地址了,进一步实现IdentityServer的负载均衡也是没有问题的。

修改Config.cs中mvc客户端配置如下

ClientId = "mvc",

                    ClientName = "MVC Client",

                    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

                    ClientSecrets =

                    {

                        new Secret("secret".Sha256())

                    },

                   // AccessTokenType = AccessTokenType.Reference,

                    RequireConsent = true,

                    RedirectUris = { "http://localhost:54660/signin-oidc" },

                    PostLogoutRedirectUris = { "http://localhost:54660/signout-callback-oidc" },

                    AllowedScopes =

                    {

                        IdentityServerConstants.StandardScopes.OpenId,

                        IdentityServerConstants.StandardScopes.Profile,

                        "api1"

                    },

                    AllowOfflineAccess = true,

                    //直接返回客户端需要的Claims

                    AlwaysIncludeUserClaimsInIdToken = true,

主要修改RedirectUris和PostLogoutRedirectUris为网关地址,在网关也设置了signin-oidc和signout-callback-oidc转发请求到Mvc客户端。

三 修改MvcClient

修改MvcClient的launchSettings.json端口为50891。

修改MvcClient的Authority地址为http://localhost:54660/IdentityServer和默认路由地址MvcClient/{controller=Home}/{action=index}/{id?}

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();


            services.AddAuthentication(options =>

                {

                    options.DefaultScheme = "Cookies";

                    options.DefaultChallengeScheme = "oidc";

                    //options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

                })

                .AddCookie("Cookies",options=> {

                    options.ExpireTimeSpan = TimeSpan.FromMinutes(30);

                    options.SlidingExpiration = true;

                })

                .AddOpenIdConnect("oidc", options =>

                {

                    options.SignInScheme = "Cookies";


                    options.Authority = "http://localhost:54660/IdentityServer";

                    options.RequireHttpsMetadata = false;


                    options.ClientId = "mvc";

                    options.ClientSecret = "secret";

                    options.ResponseType = "code id_token";


                    options.SaveTokens = true;

                    options.GetClaimsFromUserInfoEndpoint = true;


                    options.Scope.Add("api1");


                    options.Scope.Add("offline_access");

                });

app.UseMvc(routes =>

            {

   

                    routes.MapRoute(

                        name: "default",

                        template: "MvcClient/{controller=Home}/{action=index}/{id?}");

           

            });

修改HomeController,将相关地址修改为网关地址

public async Task<IActionResult> CallApiUsingClientCredentials()

        {

            var tokenClient = new TokenClient("http://localhost:54660/IdentityServer/connect/token", "mvc", "secret");

            var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");


            var client = new HttpClient();

            client.SetBearerToken(tokenResponse.AccessToken);

            var content = await client.GetStringAsync("http://localhost:54660/MyApi/identity");


            ViewBag.Json = JArray.Parse(content).ToString();

            return View("json");

        }


        public async Task<IActionResult> CallApiUsingUserAccessToken()

        {

            var accessToken = await HttpContext.GetTokenAsync("access_token");

            //OpenIdConnectParameterNames

            var client = new HttpClient();

            client.SetBearerToken(accessToken);

            var content = await client.GetStringAsync("http://localhost:54660/MyApi/identity");


            ViewBag.Json = JArray.Parse(content).ToString();

            return View("json");

        }

四 修改Api项目

Api项目修改多一点。

将MvcClient的HomeController和相关视图复制过来,模拟MVC与API同时存在的项目。

修改Api的launchSettings.json端口为50890。

修改Startup

public class Startup

    {

        public void ConfigureServices(IServiceCollection services)

        {

            services.AddDataProtection(options => options.ApplicationDiscriminator = "00000").SetApplicationName("00000");


            services.AddMvc();

            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();


            services.AddAuthentication(options =>

            {

                options.DefaultScheme = "Cookies";

                options.DefaultChallengeScheme = "oidc";

            }).AddCookie("Cookies")

               .AddOpenIdConnect("oidc", options =>

               {

                   options.SignInScheme = "Cookies";


                   options.Authority = "http://localhost:54660/IdentityServer";

                   options.RequireHttpsMetadata = false;


                   options.ClientId = "mvc";

                   options.ClientSecret = "secret";

                   options.ResponseType = "code id_token";


                   options.SaveTokens = true;

                   options.GetClaimsFromUserInfoEndpoint = true;


                   options.Scope.Add("api1");

                   options.Scope.Add("offline_access");

               })

                    .AddIdentityServerAuthentication("Bearer", options =>

                     {

                         options.Authority = "http://localhost:54660/IdentityServer";

                         options.RequireHttpsMetadata = false;

                         options.ApiSecret = "secret123";

                         options.ApiName = "api1";

                         options.SupportedTokens= SupportedTokens.Both;

                     });


            services.AddAuthorization(option =>

            {

                //默认 只写 [Authorize],表示使用oidc进行认证

                option.DefaultPolicy = new AuthorizationPolicyBuilder("oidc").RequireAuthenticatedUser().Build();

                //ApiController使用这个  [Authorize(Policy = "ApiPolicy")],使用jwt认证方案

                option.AddPolicy("ApiPolicy", policy =>

                {

                    policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);

                    policy.RequireAuthenticatedUser();

                });

            });

        }


        public void Configure(IApplicationBuilder app, IHostingEnvironment env)

        {

            //var options = new ForwardedHeadersOptions

            //{

            //    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,

            //    ForwardLimit = 1

            //};

            //options.KnownNetworks.Clear();

            //options.KnownProxies.Clear();

            //app.UseForwardedHeaders(options);

            //if (env.IsDevelopment())

            //{

            //    app.UseDeveloperExceptionPage();

            //}

            //else

            //{

            //    app.UseExceptionHandler("/Home/Error");

            //}


            app.UseAuthentication();


            app.UseStaticFiles();

            app.UseMvc(routes =>

            {

                routes.MapRoute(

                    name: "default",

                    template: "MyApi/{controller=MAccount}/{action=Login}/{id?}");


            });

        }

    }

主要添加了oidc认证配置和配置验证策略来同时支持oidc认证和Bearer认证。

修改IdentityController中的[Authorize]特性为[Authorize(Policy = "ApiPolicy")]

 

 

 依次使用调试-开始执行(不调试)并选择项目名称启动QuickstartIdentityServer,Gateway,MvcClient,Api,启动方式如图

640?wx_fmt=png

应该可以看到Gateway启动后直接显示了IdentityServer的默认首页

640?wx_fmt=png

 

在浏览器输入http://localhost:54660/MVCClient/Home/index进入MVCClient


 

点击Secure进入需要授权的页面,这时候会跳转到登陆页面(才怪

640?wx_fmt=png

实际上我们会遇到一个错误,这是因为ocelot做网关时下游服务获取到的Host实际为localhost:50891,而在IdentityServer中设置的RedirectUris为网关的54660,我们可以通过ocelot转发X-Forwarded-Host头,并在客户端通过UseForwardedHeaders中间件来获取头。但是UseForwardedHeaders中间件为了防止IP欺骗攻击需要设置KnownNetworks和KnownProxies以实现严格匹配。当然也可以通过清空KnownNetworks和KnownProxies的默认值来不执行严格匹配,这样一来就有可能受到攻击。所以这里我直接使用硬编码的方式设置Host,实际使用时应从配置文件获取,同时修改MvcClient和Api相关代码

app.Use(async (context, next) =>

            {

                context.Request.Host = HostString.FromUriComponent(new Uri("http://localhost:54660/"));

                await next.Invoke();

            });

            //var options = new ForwardedHeadersOptions

            //{

            //    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,

            //    ForwardLimit = 1

            //};

            //options.KnownNetworks.Clear();

            //options.KnownProxies.Clear();

            //app.UseForwardedHeaders(options);


在反向代理情况下通过转发X-Forwarded-Host头来获取Host地址应该时常见设置不知道还有没有其他更好的解决办法。

再次启动MVCClient并输入http://localhost:54660/MvcClient/Home/Secure。

640?wx_fmt=png

 

 使用bob,password登陆一下


点击Yes, Allow返回http://localhost:54660/MvcClient/Home/Secure,此时可以查看到登陆后的信息

640?wx_fmt=png

 

 

 分别点击Call API using user token和Call API using application identity来验证一下通过access_token和ClientCredent模式请求来请求API

640?wx_fmt=png

 

成功获取到返回值。

 输入http://localhost:54660/myapi/Home/index来查看API情况


求成功。

点击Secure从API项目查看用户信息,此时展示信息应该和MvcClient一致

640?wx_fmt=png

 

嗯,并没有看到用户信息而是又到了授权页.....,这是因为.netCore使用DataProtection来保护数据(点击查看详细信息),Api项目不能解析由MvcClient生成的Cookie,而被重定向到了IdentityServer服务中。

在MvcClient和Api的ConfigureServices下添加如下代码来同步密钥环。

            services.AddDataProtection(options => options.ApplicationDiscriminator = "00000").SetApplicationName("00000");

 

 

再次启动MvcClient和Api项目并在浏览器中输入http://localhost:54660/MvcClient/home/Secure,此时被要求重新授权,点击Yes, Allow后看到用户信息

再输入http://localhost:54660/myapi/Home/Secure从API项目查看用户信息

640?wx_fmt=png

 

 

 分别点击Call API using user token和Call API using application identity来验证一下通过access_token和ClientCredent模式请求来请求API

640?wx_fmt=png

请求成功。 

如此我们便实现了通过ocelot实现统一入口,通过IdentityServer4来实现认证的需求 

源代码 https://github.com/saber-wang/Quickstart5_HybridAndApi

参考

https://www.cnblogs.com/stulzq/category/1060023.html

https://www.cnblogs.com/xiaoti/p/10118930.html

https://www.cnblogs.com/jackcao/tag/identityserver4/

原文地址:https://www.cnblogs.com/nasha/p/10160695.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/318354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CF1556D-Take a Guess【交互】

正题 题目链接:https://codeforces.com/contest/1556/problem/D 题目大意 现在有nnn个你不知道的数字&#xff0c;你有两种询问操作 询问两个下标的数字的andandand询问两个下标的数字的ororor 要求在2n2n2n次操作以内求出第kkk小的数字 1≤n≤104,0≤ai≤1091\leq n\leq 1…

YBTOJ:彩球抽取(期望)

文章目录题目描述解析代码题目描述 解析 首先&#xff0c;可以使用dp解决本题 设fi,j,k&#xff1a;操作i轮之后编号j的小球有k个的概率 转移和统计答案就都不难了 但是还有一个问题 不难发现这个题循环下去是可以无穷无尽的 所以限定一个i的上界&#xff08;如500000&#xf…

魔改森林

题意&#xff1a; 曾经有一道叫做迷雾森林的题目&#xff0c;然而牛牛认为地图中的障碍太多&#xff0c;实在是太难了&#xff0c;所以删去了很多点&#xff0c;出了这道题。 牛牛给出了一个n行m列的网格图 初始牛牛处在最左下角的格点上(n1,1)&#xff0c;终点在右上角的格点…

基于IdentityServer4 实现.NET Core的认证授权

IdentityServer4是什么&#xff1f;IdentityServer4是基于ASP.NET Core实现的认证和授权框架&#xff0c;是对OpenID Connect和OAuth 2.0协议的实现。OpenID Connect 和 OAuth2.0是什么OpenID Connect:OpenID Connect由OpenID基金会于2014年发布的一个开放标准, 是建立在OAuth …

常见存储、查找算法

存储 散列存储&#xff1a;即哈希的存储方式。 顺序存储&#xff1a;数组的存储方式 链式存储&#xff1a;链式前向星、vector<> 压缩存储 索引存储 查找 常见查找算法 顺序查找 一个一个往下找&#xff0c;复杂度 \(O(\dfrac{n1}{2})\) 。 适合顺序存储&#xff0c…

CF1556E-Equilibrium【栈,树状数组】

正题 题目连接:https://codeforces.com/contest/1556/problem/E 题目大意 两个长度为nnn的序列a,ba,ba,b&#xff0c;qqq次询问一个区间[l,r][l,r][l,r]。 在这个区间中你每次可以选择一个长度为偶数的下标递增的序列&#xff0c;让奇数位置的aaa加一&#xff0c;偶数位置的…

[COCI 2018#5]Parametriziran

这道题呢&#xff01; 算了&#xff0c;不要让这玩意儿活着祸害众生吧&#xff01;让我们来拯救苍生于苦海之中&#xff01;&#xff01; 骚话连篇ing 题目 由小写英文字母和问号组成的字符串成为参数化单词&#xff08;例如&#xff1a;??cd,bcd,??&#xff09;。如果两…

P2324 [SCOI2005]骑士精神(迭代加深搜索,dfs)

传送门 文章目录解析解析 很显然&#xff0c;让马走的话状态记录和转移都会比较复杂 所以转化成让空位跳会更好做一点 但这不是重点 初看本题&#xff0c;其实第一感觉是bfs 但是状态数理论上最差可以达到815&#xff0c;&#xff08;当然基本不可能跑满&#xff09;&#xff…

AcWing 1402. 星空之夜 1月28

AcWing 1402. 星空之夜 1月28 题意&#xff1a; 一个星群是指一组非空的在水平&#xff0c;垂直或对角线方向相邻的星星的集合。 一个星群不能是一个更大星群的一部分。 星群可能是相似的。 如果两个星群的形状、包含星星的数目相同&#xff0c;那么无论它们的朝向如何&#…

RMQ、ST表

ST表 \(\text{ST}\) 表是用于解决可重复贡献问题的数据结构。 可重复贡献问题&#xff1a;区间按位和、区间按位或、区间 \(\gcd\) 、区间最大、区间最小等满足结合律且可重复统计的问题。 模板预处理&#xff1a;(以区间最大值为例) void pre_work() {for(int i2;i<n;i) lg…

NumSharp v0.6 科学计算库发布,新增 LAPACK 的线性库支持

NumSharp&#xff08;Numerical .NET&#xff09;可以说是C&#xff03;中的科学计算库。 它是用C&#xff03;编写的&#xff0c;符合.netstandard 2.0库标准。 它的目标是让.NET开发人员使用NumPy的语法编写机器学习代码&#xff0c;从而最大限度地借鉴现有大量在python代码的…

[COCI] Zamjena

连这种模拟题都能。。。orz ex&#xff0c;太恶心了&#xff01; 驰骋坑底这么久了&#xff0c;我明白了 开始吧&#xff01;我发誓&#xff0c;这个超级兵&#xff0c;我就算用小书包平A都要A了它 题目 Vlatko喜欢使用整数数组&#xff0c;他在一张纸上写下了两个数组&…

P7470-[NOI Online 2021 提高组]岛屿探险【Trie,CDQ分治】

正题 题目链接:https://www.luogu.com.cn/problem/P7470 题目大意 给出nnn个二元组(a,b)(a,b)(a,b)。 qqq次询问给出(l,r,c,d)(l,r,c,d)(l,r,c,d)表示询问[l,r][l,r][l,r]中有多少二元组满足cxora≤min(b,d)c\ xor\ a\leq min(b,d)c xor a≤min(b,d)。 1≤n,q≤1051\leq n,q\…

P2601 [ZJOI2009]对称的正方形(二维哈希)(二分)

洛谷传送门 文章目录题目描述解析代码题目描述 解析 做三个hash 分一下正方形边长的奇偶性 然后枚举中心点&#xff0c;二分边长即可 有点类似模拟赛那道红十字的题 我一开始觉得分奇偶好麻烦啊 为什么不直接枚举左上方的点二分呢&#xff1f;awa 很遗憾的是… 那样答案就没有…

初赛—错题集

计算机基础知识 LAN&#xff1a;局域网&#xff0c;WAN&#xff1a;广域网&#xff0c;MAN&#xff1a;城域网 汇编语言是(依赖于具体计算机)的低级程序设计语言 计算机操作的最小时间单位是(时钟周期)。 注意所需空间需要 \(\div 8\) &#xff01;&#xff01;&#xff01;…

.NET Core 和 DevOps

关键要点无论你目前使用什么样的技术栈&#xff0c;DevOps 都是值得一试的。闭源、专有软件和构建过程与 DevOps 实践不兼容。.NET Core 是开源的&#xff0c;是基于 DevOps 构思和构建的。.NET Core CLI 和 Roslyn API 让整个交付流程变得更加开放&#xff0c;且具有更强的适应…

2021 NOI游记

文章目录前言day1总结T1 轻重边(0/50)考后得分T2 路径交点(0/20)考后得分T3 庆典(28/44)赛后得分day2总结T1 量子通信(20/12)赛后得分T2 密码箱(0/35)赛后得分T3 机器人游戏(12/12)赛后得分总结前言 62pts 太惨了 &#xff08;只是参加了同步赛而已&#xff09; 感觉心态受到了…

【每日一题】1月29日题目 和与或

题意&#xff1a; 给你一个数组R&#xff0c;包含N个元素&#xff0c;求有多少满足条件的序列A使得 0 ≤ A[i] ≤ R [ i ] A[0]A[1]…A[N−1] A[0] | ]A[1]… | A [ N − 1 ] 输出答案对1e99取模 题解&#xff1a; 参考博客 数位dp问题 如果和等于或的话&#xff0c;说明两…

Message Decoding密码翻译

这是一道模拟题ex 其实每一道模拟题都很“简单”&#xff0c; 这道题就是难在读英文题&#xff01;处理输入&#xff01; 真的我竟然花了几个小时就只是为了看懂样例&#xff01;&#xff01;orz 题目大意 考虑下面的01串序列&#xff1a; 0&#xff0c;00&#xff0c;01…

AT3945-[ARC092D]Two Faced Edges【dfs】

正题 题目链接:https://www.luogu.com.cn/problem/AT3945 题目大意 nnn个点mmm条边的一张图&#xff0c;对于每条边求它翻转后强连通分量数量是否变化。 1≤n≤1000,1≤m≤21051\leq n\leq 1000,1\leq m\leq 2\times 10^51≤n≤1000,1≤m≤2105 解题思路 对于一条(x,y)(x,y)(…