gpt4 book ai didi

c# - 无法使用我的身份服务器从 native 应用程序调用 API

转载 作者:行者123 更新时间:2023-12-02 20:00:15 26 4
gpt4 key购买 nike

我想为本地应用程序(客户端移动)在两个单独的 docker 中制作身份服务器和 API。
它在 NGINX 反向代理和 Let's Encrypt 上运行。

                               Dockers
---------------------------
| Reverse Proxy |
| ----------------------- |
-------- | | ---------------- | |
| Mobile | ---------|-> | IdendityServer | | |
-------- | | | Port: 5000 | | |
| | | ---------------- | |
| | | | | |
| | | ---------------- | |
---------------|-> | API | | |
| | | Port: 5001 | | |
| | ---------------- | |
| ----------------------- |
| |
| ---------------- |
| | PostgreSQL | |
| | Port: 5432 | |
| ---------------- |
---------------------------

使用我当前的配置:
  • 带有 Let's Encrypt 的反向代理在移动设备上运行良好
  • 没有 [Authority] 的调用 API 在移动设备上运行良好
  • 与混合流的身份服务器连接正在工作,并且我的用户的声明已列出

  • 我的代码如下。

    身份服务器 Dockerfile
    FROM microsoft/dotnet:2.0-sdk
    COPY is4/* /app/
    WORKDIR /app

    ENV ASPNETCORE_URLS http://*:5000
    EXPOSE 5000

    ENTRYPOINT ["dotnet", "IdentityServer.dll"]

    API Dockerfile
    FROM microsoft/dotnet:2.0-sdk
    COPY api/* /app/
    WORKDIR /app

    ENV ASPNETCORE_URLS http://*:5001
    EXPOSE 5001

    ENTRYPOINT ["dotnet", "ApiServer.dll"]

    DockerCompose
    version: '3'

    services:
    identityserver:
    image: identityserver
    build:
    context: .
    dockerfile: IdentityServer/Dockerfile
    container_name: ids
    restart: always
    ports:
    - 5000:5000
    # expose:
    # - "5000"
    environment:
    ASPNETCORE_ENVIRONMENT: Development
    VIRTUAL_PORT: 5000
    VIRTUAL_HOST: ids.mydomain.com
    LETSENCRYPT_HOST: ids.mydomain.com
    LETSENCRYPT_EMAIL: myuser@mydomain.com
    IDENTITY_ISSUER: "https://ids.mydomain.com"
    IDENTITY_REDIRECT: "com.mobiletest.nativeapp"
    IDENTITY_CORS_ORIGINS: "https://ids.mydomain.com"
    depends_on:
    - db
    apiserver:
    image: apiserver
    build:
    context: .
    dockerfile: ApiServer/Dockerfile
    container_name: api
    restart: always
    ports:
    - 5001:5001
    # expose:
    # - "5001"
    environment:
    ASPNETCORE_ENVIRONMENT: Development
    VIRTUAL_PORT: 5001
    VIRTUAL_HOST: api.mydomain.com
    LETSENCRYPT_HOST: api.mydomain.com
    LETSENCRYPT_EMAIL: myuser@mydomain.com
    IDENTITY_AUTHORITY: "http://identityserver:5000"
    CLIENT_CORS_ORIGINS: "com.mobiletest.nativeapp"
    depends_on:
    - identityserver
    - db
    links:
    - identityserver
    db:
    image: postgresql:10
    build:
    context: .
    dockerfile: PostgreSQL/Dockerfile
    container_name: db
    restart: always
    ports:
    - "5432:5432"
    volumes:
    - /www/database:/var/lib/postgresql/data
    environment:
    - PGDATA=/var/lib/postgresql/data/pgdata

    networks:
    default:
    external:
    name: nginx-proxy

    IdentityServer 启动代码

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDbContext<ApplicationDbContext>(options =>
    options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
    services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

    // Add application services.
    services.AddTransient<IEmailSender, EmailSender>();

    services.AddMvc();

    // Configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer(opt =>
    {
    opt.IssuerUri = Configuration["IDENTITY_ISSUER"];
    opt.PublicOrigin = Configuration["IDENTITY_ISSUER"];
    })
    .AddCorsPolicyService<InMemoryCorsPolicyService>() // Add the CORS service
    .AddDeveloperSigningCredential()
    .AddInMemoryPersistedGrants()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddAspNetIdentity<ApplicationUser>();

    services.AddAuthentication();

    // preserve OIDC state in cache (solves problems with AAD and URL lenghts)
    services.AddOidcStateDataFormatterCache("aad");

    // add CORS policy for non-IdentityServer endpoints
    services.AddCors(options =>
    {
    options.AddPolicy("CorsPolicy", policy =>
    {
    policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
    });
    });
    } // ConfigureServices()

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
    if (env.IsDevelopment())
    {
    app.UseBrowserLink();
    app.UseDeveloperExceptionPage();
    app.UseDatabaseErrorPage();
    }
    else
    {
    app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseIdentityServer();

    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });

    app.UseMvc(routes =>
    {
    routes.MapRoute(
    name: "default",
    template: "{controller=Home}/{action=Index}/{id?}");
    });
    } // Configure()

    身份服务器配置代码

    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
    return new List<IdentityResource>
    {
    new IdentityResources.OpenId(),
    new IdentityResources.Profile()
    };
    }

    public static IEnumerable<ApiResource> GetApiResources()
    {
    return new List<ApiResource>
    {
    new ApiResource("api1", "My API")
    {
    ApiSecrets = { new Secret("secret".Sha256()) }
    }
    };
    }

    public static IEnumerable<Client> GetClients()
    {
    // client credentials client
    return new List<Client>
    {
    new Client
    {
    ClientId = "native.hybrid",
    ClientName = "Native Client (Hybrid with PKCE)",
    AllowedGrantTypes = GrantTypes.Hybrid,
    RequirePkce = true,
    RequireConsent = false,
    //RequireClientSecret = false,
    ClientSecrets = { new Secret("secret".Sha256()) },
    RedirectUris = { Configuration["IDENTITY_REDIRECT"] + "://signin-oidc" },
    PostLogoutRedirectUris = { Configuration["IDENTITY_REDIRECT"] + "://signout-callback-oidc" },
    AllowedScopes = { "openid", "profile" },
    AllowedCorsOrigins = { Configuration["IDENTITY_CORS_ORIGINS"] },
    AllowOfflineAccess = true,
    //AllowAccessTokensViaBrowser = true
    RefreshTokenUsage = TokenUsage.ReUse
    }
    };
    } // GetClients()

    接口(interface)配置代码

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddMvcCore()
    .AddAuthorization()
    .AddJsonFormatters();
    if (Configuration["CLIENT_CORS_ORIGINS"] == "")
    {
    services.AddCors(options =>
    {
    options.AddPolicy("CorsPolicy",
    builder => builder
    .AllowAnyMethod()
    .AllowAnyOrigin()
    .AllowAnyHeader());
    });
    }
    else
    {
    services.AddCors(options =>
    {
    options.AddPolicy("CorsPolicy",
    builder => builder
    .AllowAnyHeader()
    .AllowAnyMethod()
    .WithOrigins(Configuration["CLIENT_CORS_ORIGINS"]));
    });
    }
    services.AddAuthentication("Bearer");
    services.AddAuthentication(options => //adds the authentication services to DI
    {
    //We are using a cookie as the primary means to authenticate a user (via “Cookies” as the DefaultScheme). We set the DefaultChallengeScheme to “oidc” because when we need the user to login, we will be using the OpenID Connect scheme.
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
    })
    .AddCookie("Cookies") //add the handler that can process cookies
    .AddOpenIdConnect("oidc", options => //configure the handler that perform the OpenID Connect protocol
    {
    options.SignInScheme = "Cookies"; //is used to issue a cookie using the cookie handler once the OpenID Connect protocol is complete
    options.Authority = Configuration["IDENTITY_AUTHORITY"]; //indicates that we are trusting IdentityServer
    options.RequireHttpsMetadata = false;
    options.ClientId = "native.hybrid";
    options.SaveTokens = true;
    options.ClientSecret = "secret"; //used to persist the tokens from IdentityServer in the cookie
    options.ResponseType = "code id_token";
    });

    services.AddMvc();
    } // ConfigureServices()

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
    app.UseAuthentication();

    app.UseCors("CorsPolicy");

    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });

    app.UseMvc();
    } // Configure()

    API Controller 代码

    [Route("api/[controller]")]
    [EnableCors("CorsPolicy")]
    [Authorize]
    public class ValuesController : Controller
    {
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
    return new string[] { "testvalue1", "testvalue2" };
    }
    }

    在 Xamarin 上为客户端移动

    var options = new OidcClientOptions
    {
    Authority = "https://ids4.syladebox.com",
    ClientId = "native.hybrid",
    ClientSecret = "secret",
    //Scope = "openid profile api1 offline_access",
    Scope = "openid profile offline_access",
    ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,

    RedirectUri = "com.mobiletest.nativeapp://signin-oidc",
    PostLogoutRedirectUri = "com.mobiletest.nativeapp://signout-callback-oidc",

    //Flow = OidcClientOptions.AuthenticationFlow.Hybrid,
    //Policy = policy,

    //Browser = new SFAuthenticationSessionBrowser()
    // new in iOS 12
    Browser = new ASWebAuthenticationSessionBrowser()
    //Browser = new PlatformWebView()
    };

    _client = new OidcClient(options);

    var result = await _client.LoginAsync(new LoginRequest());

    if (result.IsError)
    {
    OutputText.Text = result.Error;
    return;
    }

    if (result.AccessToken != null)
    {
    var client = new HttpClient();
    client.SetBearerToken(result.AccessToken);
    var response = await client.GetAsync("https://api.mydomain.com/api/values");
    if (!response.IsSuccessStatusCode)
    {
    OutputText.Text = response.ReasonPhrase;
    return;
    }

    var content = await response.Content.ReadAsStringAsync();
    OutputText.Text = JArray.Parse(content).ToString();
    }

    问题未解决:

    问题是无法代表我的用户调用 API。在以下情况下,它会返回“No authorized”或“Bad gateway”:

    response = await client.GetAsync("https://api.mydomain.com/api/values"); 

    这些错误取决于 IdentityServer 和 Api docker 中的环境变量。

    我当前的环境变量是:

    IDENTITY_ISSUER:“ https://ids.mydomain.com

    IDENTITY_REDIRECT:“com.mobiletest.nativeapp”

    IDENTITY_CORS_ORIGINS:“ https://ids.mydomain.com

    IDENTITY_AUTHORITY:“ http://identityserver:5000

    CLIENT_CORS_ORIGINS:“com.mobiletest.nativeapp”

    API 调用 ( https://api.mydomain.com/api/values) 返回“错误网关”。

    我认为 IDENTITY_ISSUER、IDENTITY_REDIRECT 是正确的,因为身份服务器连接成功。

    问题是来自其他环境变量(IDENTITY_CORS_ORIGINS、IDENTITY_AUTHORITY 和 CLIENT_CORS_ORIGINS)还是身份服务器/API 代码?

    1 月 26 日更新:

    为了确保我的 API 程序可以正常工作,我将 API 程序改成了最简单的:
            public void ConfigureServices(IServiceCollection services)
    {
    services.AddMvc();

    services.AddAuthentication("Bearer")
    .AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, options =>
    {
    options.Authority = Configuration["IDENTITY_AUTHORITY"];
    options.ApiName = "api";
    //options.ApiSecret = "secret";
    });

    // Add CORS policy for non-IdentityServer endpoints
    services.AddCors(options =>
    {
    options.AddPolicy("api", policy =>
    {
    policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
    });
    });
    } // ConfigureServices()

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
    app.UseAuthentication();

    app.UseCors("api");

    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });

    app.UseMvc();
    } // Configure()

    使用 api Controller :
        [Route("api/[controller]")]
    [Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme)]
    public class ValuesController : Controller
    {
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
    return new string[] { "testvalue1", "testvalue2" };
    }
    }

    我在第一次测试中使用了另一个身份服务器:

    demo.identityserver.io

    对于此测试,我进行以下配置:
      apiserver:
    ...
    ports:
    - 5001:80
    environment:
    ...
    IDENTITY_AUTHORITY: "https://demo.identityserver.io"
    #CLIENT_CORS_ORIGINS (omitted in the code)

    我在客户端代码中的 OidcClientOptions 是:
                var options = new OidcClientOptions
    {
    Authority = "https://demo.identityserver.io",
    ClientId = "native.hybrid",
    Scope = "openid profile email api offline_access",
    ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,

    RedirectUri = "com.mobiletest.nativeapp://callback",
    PostLogoutRedirectUri = "com.mobiletest.nativeapp://callback",
    Browser = new ASWebAuthenticationSessionBrowser()
    };

    登录功能在我的第一个主题中。
  • 与混合流的身份服务器连接正在工作
  • API调用成功!

  • 由于 demo.identityserver.io 是演示身份服务器,我怀疑它是否可以作为生产情况使用,然后我使用相同的 API 程序测试了另一个身份服务器(Okta):

    dev-xxxxxx.okta.com

    对于此测试,我进行以下配置:
      apiserver:
    ...
    ports:
    - 5001:80
    environment:
    ...
    IDENTITY_AUTHORITY: "https://dev-xxxxxx.okta.com"
    #CLIENT_CORS_ORIGINS (omitted in the code)

    我在客户端代码中的 OidcClientOptions 是:
                var options = new OidcClientOptions
    {
    Authority = "https://dev-xxxx.okta.com",
    ClientId = "xxxxxxxxxxxxxxxxxxx", // ClientId is hidden in this topic

    Scope = "openid profile email offline_access",
    ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,

    RedirectUri = "com.okta.dev-xxxxxx:/callback",
    PostLogoutRedirectUri = "com.okta.dev-xxxxxx:/callback",

    Browser = new ASWebAuthenticationSessionBrowser()
    };
  • 与混合流的身份服务器连接正在工作
  • API 调用不起作用,它返回消息“未经授权”。

  • 通过这两个测试,我无法判断我的 API 程序是否运行良好。

    你可以帮帮我吗?非常感谢你!

    最佳答案

    您是否尝试过使用 .AddJwtBearer()在您的 API 中,而不仅仅是 .AddAuthentication("Bearer") ?

    像这样的东西:

    services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
    options.Authority = "http://localhost:5000";
    options.RequireHttpsMetadata = false;
    options.Audience = "api1";
    });

    来源: http://docs.identityserver.io/en/latest/quickstarts/1_client_credentials.html

    虽然来源指的是客户端凭据,但我认为您可能希望使用 Jwt token ,而不管客户端类型如何。

    关于c# - 无法使用我的身份服务器从 native 应用程序调用 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54293098/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com