使用.Net Core + Vue + IdentityServer4 + Ocelot 實現一個簡單的DEMO +源碼


運行環境

Vue 使用的是D2admin:https://doc.d2admin.fairyever.com/zh/

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項目

根據 https://www.cnblogs.com/edisonchou/p/identityserver4_foundation_and_quickstart_01.html 根據Edison Zhou大佬的博客,配置好 QuickStart UI 以及IdentityServer的依賴項

新建IdentityServer4的配置文件IdentityConfig


   public static IEnumerable
  
  
  
          
            GetIdentityResources() { return new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResource("delimitClaim","delimitClaim",new List 
           
             (){ "role", "name"}), //在Claims添加role 和 name信息 }; } public static IEnumerable 
            
              GetApiResource() { return new List 
             
               { new ApiResource("identityServerApi", "identityServerApi"), }; } public static IEnumerable 
              
                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: https://www.cnblogs.com/edisonchou/p/integration_authentication-authorization_service_foundation.html

曉晨Master:https://www.cnblogs.com/stulzq/category/1060023.html

solenovex :https://www.cnblogs.com/cgzl/tag/identity%20server%204/

滅蒙鳥:https://www.jianshu.com/p/fde63052a3a5

.Net框架學苑:https://www.cnblogs.com/letyouknowdotnet/category/1481970.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM