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,一经查实,立即删除!

相关文章

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 …

[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…

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;他在一张纸上写下了两个数组&…

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;且具有更强的适应…

Message Decoding密码翻译

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

.NET Core开发日志——结构化日志

在.NET生态圈中&#xff0c;最早被广泛使用的日志库可能是派生自Java世界里的Apache log4net。而其后来者&#xff0c;莫过于NLog。Nlog与log4net相比&#xff0c;有一项较显著的优势&#xff0c;它支持结构化日志。结构化日志&#xff0c;也被称为语义化日志。其作用有二&…

YBTOJ 特殊数列(哈希表)

文章目录题目描述解析代码题目描述 解析 应该是哈希表板子题了 选一个1e6左右的质数进行处理即可 其实本质就是链前 没啥特别新鲜的 为什么要写呢&#xff1f; 因为这个东西很早之前看的时候完全没有看懂。。。 代码 #include<bits/stdc.h> using namespace std; #def…

Strange Definition CodeForces - 1471D

题意&#xff1a; 定义数字 x 和 y 是“相邻”的当且仅当 lcm(x,y)/gcd(x,y) 是一个平方数。 给定一个长度为 n 的数组 a。 每过一秒&#xff0c;数组 a 会发生变化&#xff1a;ai 会变成数组 a 中与其“相邻”的所有数字的乘积。 定义 di 为数组 a 中与 ai “相邻” 的数字个…

学习三分 (概念 + 模板 + 例题:曲线)

这好像是我第一次尝试写一个新知识入门 而不是习题解 文章目录三分概念模板例题&#xff1a;曲线题目题解代码实现三分概念 我们都知道&#xff0c;二分是在一个单调函数&#xff08;即一次函数&#xff09;上通过每次查找折半的方式&#xff0c;对答案进行搜索查找。那么&am…

迎元旦,庆surging 1.0发布

一位摄影程序员的独白每个人都有爱好&#xff0c;都有释放压力的活动&#xff0c;而我也不例外&#xff0c;我除了每天上班&#xff0c;周末就会约一群好友去拍妹子&#xff0c;成家后&#xff0c;就改为拍虫子&#xff0c;一拍就到了30岁&#xff0c;到了30岁就感觉到了中年的…

YBTOJ:求好元素(哈希表)

文章目录题目描述解析代码题目描述 解析 如果枚举m,n,p的话是n3的 会超时 但我们注意到右边查询只有O(n) 这就很不平衡 所以考虑把p移到右边&#xff0c;预处理枚举m、n存到哈希表中 查询枚举i、p 这样就把复杂度均摊降到了n2 但是本题数据较强 所以我们得保证哈希表的单词查询…

Strange Shuffle CodeForces - 1471E(交互题)

交互题 这类型不同于普通的题。 可以理解为有个问题需要你解决&#xff0c;你通过输入某些东西表示你要问系统的问题&#xff0c;这时系统会回答你的问题。在代码中的回答方式就是会输入某个东西就是系统给你的答案&#xff0c;通过这些信息你可以得到问题的解你是不可以自己测…

学习KMP (概念 + 模板 + 例题: 子串查找)

我又回来了&#xff0c;感jio这几天有点勤啊&#xff01;&#xff01; 这一次我带着KMP来了&#xff0c; 文章目录KMP介绍模板例题&#xff1a; 子串查找题目暴力题解KMP题解代码实现KMP介绍 KMP&#xff0c;即 Knuth-Morris-Pratt 字符串查找算法&#xff0c;由Donald Knuth…

直播预告 - 博时基金DevOps体系建设和自动化测试分享

最近几年&#xff0c;基金行业发展比较快&#xff0c;业务范围从传统公募到大资管&#xff0c;业务地域从中国大陆到全球化&#xff0c;在互联网金融浪潮中扮演了重要角色&#xff0c;金融科技又带来新的挑战和机遇。据毕马威2014年研究报告显示&#xff0c;由于新技术、人口变…