冠军

导航

eShopOnContainer 中 unauthorized_client error 登录错误处理

在准备好 eShopOnContainer 环境,运行起来之后,不幸的是,我遇到了不能登录的错误。

从错误信息中,可以看到 unauthorized_client 的内容。这是为什么呢?

从 eShopOnContainers 的 Wiki 上,可以看到找到这篇文章:unauthorized_client error on Login

这里面介绍了导致该问题的几个原因。

其中的一个是不能使用 localhost 作为主机名来访问 eShop 站点,因为 eShop 在 Identity 服务器上注册的客户端使用了 docker 中的名称 host.docker.internal

那么,注册和访问的信息来自哪里呢?

在访问的时候,使用了来自 src/.env 文件中配置的环境变量信息, docker-compose 将会自动使用该文件来获得环境变量的配置信息。见 Environment variables in Compose

You can set default values for environment variables using a .env file,
which Compose automatically looks for in project directory (parent folder of your Compose file).
Values set in the shell environment override those set in the .env file.

该文件开始部分内容如下:

# Compose supports declaring default environment variables in an environment file named .env placed in the folder docker-compose command is executed from (current working directory).
# Compose expects each line in an env file to be in VAR=VAL format. Lines beginning with # (i.e. comments) are ignored, as are blank lines.
# Note: Values present in the environment at runtime will always override those defined inside the .env file. Similarly, values passed via command-line arguments take precedence as well.

# The IP below should be swapped to your real IP or DNS name, like 192.168.88.248, etc. if testing from remote browsers or mobile devices

# Use this values to run the app locally in Windows
ESHOP_EXTERNAL_DNS_NAME_OR_IP=host.docker.internal
ESHOP_STORAGE_CATALOG_URL=http://host.docker.internal:5202/c/api/v1/catalog/items/[0]/pic/

# Use this values to run the app locally in Mac
# ESHOP_EXTERNAL_DNS_NAME_OR_IP=docker.for.mac.localhost
# ESHOP_STORAGE_CATALOG_URL=http://docker.for.mac.localhost:5202/c/api/v1/catalog/items/[0]/pic/

# Use this values to run the app locally in Linux
# ESHOP_EXTERNAL_DNS_NAME_OR_IP=docker.for.linux.localhost
# ESHOP_STORAGE_CATALOG_URL=http://docker.for.linux.localhost:5202/c/api/v1/catalog/items/[0]/pic/

可以看到在 Windows 环境下,访问地址将会使用 host.docker.internal,而在 mac OS 下,则应该使用 docker.for.mac.localhost

在 eShop MVC 项目的配置文件 eShopOnContainers/src/Web/WebMVC/appsettings.json 中,可以看到使用的默认地址

{
  "CatalogUrl": "http://localhost:5101",
  "OrderingUrl": "http://localhost:5102",
  "BasketUrl": "http://localhost:5103",
  "IdentityUrl": "http://localhost:5105",
  "CallBackUrl": "http://localhost:5100/",

在 .NET 中,它们可以被环境变量所覆盖。在 docker-compose 所使用的 docker-compose.override.yml 文件,它们被容器定义的环境变量所覆盖。

  webmvc:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://0.0.0.0:80
      - PurchaseUrl=http://webshoppingapigw
      - IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105
      - SignalrHubUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5202
      - IdentityUrlHC=http://identity-api/hc
      - UseCustomizationData=True
      - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
      - OrchestratorType=${ORCHESTRATOR_TYPE}
      - UseLoadTest=${USE_LOADTEST:-False}
    ports:
      - "5100:80"

在 Identity 中定义的时候,

eShopOnContainers/src/Services/Identity/Identity.API/Configuration/Config.cs 中,预先定义来客户端的配置信息。以 MVC 项目为例:

new Client
{
    ClientId = "mvc",
    ClientName = "MVC Client",
    ClientSecrets = new List<Secret>
    {
        
        new Secret("secret".Sha256())
    },
    ClientUri = $"{clientsUrl["Mvc"]}",                             // public uri of the client
    AllowedGrantTypes = GrantTypes.Hybrid,
    AllowAccessTokensViaBrowser = false,
    RequireConsent = false,
    AllowOfflineAccess = true,
    AlwaysIncludeUserClaimsInIdToken = true,
    RedirectUris = new List<string>
    {
        $"{clientsUrl["Mvc"]}/signin-oidc"
    },
    PostLogoutRedirectUris = new List<string>
    {
        $"{clientsUrl["Mvc"]}/signout-callback-oidc"
    },
    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        IdentityServerConstants.StandardScopes.OfflineAccess,
        "orders",
        "basket",
        "webshoppingagg",
        "orders.signalrhub",
        "webhooks"
    },
    AccessTokenLifetime = 60*60*2, // 2 hours
    IdentityTokenLifetime= 60*60*2 // 2 hours
},

这个 clientsUrl 来自 src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs 中,这些信息来自配置文件。

public async Task SeedAsync(ConfigurationDbContext context, IConfiguration configuration)
{

    //callbacks urls from config:
    var clientUrls = new Dictionary<string, string>();

    clientUrls.Add("Mvc", configuration.GetValue<string>("MvcClient"));
    clientUrls.Add("Spa", configuration.GetValue<string>("SpaClient"));
    clientUrls.Add("Xamarin", configuration.GetValue<string>("XamarinCallback"));
    clientUrls.Add("BasketApi", configuration.GetValue<string>("BasketApiClient"));
    clientUrls.Add("OrderingApi", configuration.GetValue<string>("OrderingApiClient"));
    clientUrls.Add("MobileShoppingAgg", configuration.GetValue<string>("MobileShoppingAggClient"));
    clientUrls.Add("WebShoppingAgg", configuration.GetValue<string>("WebShoppingAggClient"));
    clientUrls.Add("WebhooksApi", configuration.GetValue<string>("WebhooksApiClient"));
    clientUrls.Add("WebhooksWeb", configuration.GetValue<string>("WebhooksWebClient"));

而它们又会被 docker-compose 所配置的环境变量所覆盖掉。见 docker-compose.override.yml 文件中,针对 Identity-API 的配置:

  identity-api:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://0.0.0.0:80
      - SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104
      - XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback
      - ConnectionString=${ESHOP_AZURE_IDENTITY_DB:-Server=sqldata;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word}
      - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100
      - BasketApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
      - OrderingApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102
      - MobileShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5120
      - WebShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5121
      - WebhooksApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5113
      - WebhooksWebClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5114
      - UseCustomizationData=True
      - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
      - OrchestratorType=${ORCHESTRATOR_TYPE}
    ports:
      - "5105:80"

所以,一定要使用正确的地址访问网站,才能成功登录,直接使用 localhost 虽然可以看到首页,在登录的时候,却会因为地址的问题而出现错误。

如果注册的地址出现问题,能不能修改一下呢?这些注册信息最终会保存到数据库中,在需要的情况下,直接修改数据库中的信息也是可以的。

甚至并不需要你安装特别的工具,在 SQLServer 的镜像中,已经提供了 sqlcmd 这个命令行工具。可以使用它来完成修改工作。

首先,我们可以连接到运行的容器中,然后使用 sqlcmd 登录到运行在此容器中的数据库,最后,就可以执行 SQL 命令了。

  • 连接到容器的时候,指定运行的 bash
  • 示例中 SQLServer 数据库的默认登录帐号是 sa/Pass@word,它可以在 docker-compose 定义中找到。
  • 在 sqlcmd 中,输入一条 sql 命令之后,需要使用 go 来执行。
docker exec -it src_sqldata_1 "bash"

/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P Pass@word 

use [Microsoft.eShopOnContainers.Service.IdentityDb]
go

1> select id, left(redirectUri, 100), Clientid from dbo.ClientRedirectUris
2> go
id                                                                                                                   Clientid   
----------- ---------------------------------------------------------------------------------------------------- -----------
          1 http://host.docker.internal:5104/                                                            1
          2 http://10.121.122.162:5105/xamarincallback                                             2
          3 http://host.docker.internal:5100/signin-oidc                                           3
          4 http://host.docker.internal:5114/signin-oidc                                            4
          5 http://host.docker.internal:5100/signin-oidc                                           5
          6 http://host.docker.internal:5103/swagger/oauth2-redirect.html                 6
          7 http://host.docker.internal:5102/swagger/oauth2-redirect.html                 7
          8 http://host.docker.internal:5120/swagger/oauth2-redirect.html                 8
          9 http://host.docker.internal:5121/swagger/oauth2-redirect.html                  9
         10 http://host.docker.internal:5113/swagger/oauth2-redirect.html                  10
(10 rows affected)

insert dbo.ClientRedirectUris ( redirectUri, ClientId) values ( 'http://docker.for.mac.localhost:5104/', 1)
(1 rows affected)

1> select id, left(redirectUri, 100), Clientid from dbo.ClientRedirectUris
2> go
id                                                                                                               Clientid   
----------- ---------------------------------------------------------------------------------------------------- -----------
          1 http://host.docker.internal:5104/                                                                              1
          2 http://10.121.122.162:5105/xamarincallback                                                               2
          3 http://host.docker.internal:5100/signin-oidc                                                             3
          4 http://host.docker.internal:5114/signin-oidc                                                              4
          5 http://host.docker.internal:5100/signin-oidc                                                             5
          6 http://host.docker.internal:5103/swagger/oauth2-redirect.html                                   6
          7 http://host.docker.internal:5102/swagger/oauth2-redirect.html                                   7
          8 http://host.docker.internal:5120/swagger/oauth2-redirect.html                                   8
          9 http://host.docker.internal:5121/swagger/oauth2-redirect.html                                    9
         10 http://host.docker.internal:5113/swagger/oauth2-redirect.html                                  10
       1002 http://docker.for.mac.localhost:5104/                                                                    1

(11 rows affected)

> insert dbo.ClientRedirectUris ( redirectUri, ClientId) values ( 'http://docker.for.mac.localhost:5100/signin-oidc', 3)
> go
(1 rows affected)

1> select id, left(redirectUri, 100), Clientid from dbo.ClientRedirectUris
2> go
id                                                                                                               Clientid   
----------- ---------------------------------------------------------------------------------------------------- -----------
          1 http://host.docker.internal:5104/                                                                              1
          2 http://10.121.122.162:5105/xamarincallback                                                                     2
          3 http://host.docker.internal:5100/signin-oidc                                                                   3
          4 http://host.docker.internal:5114/signin-oidc                                                                   4
          5 http://host.docker.internal:5100/signin-oidc                                                                   5
          6 http://host.docker.internal:5103/swagger/oauth2-redirect.html                                                  6
          7 http://host.docker.internal:5102/swagger/oauth2-redirect.html                                                  7
          8 http://host.docker.internal:5120/swagger/oauth2-redirect.html                                                  8
          9 http://host.docker.internal:5121/swagger/oauth2-redirect.html                                                  9
         10 http://host.docker.internal:5113/swagger/oauth2-redirect.html                                                 10
       1002 http://docker.for.mac.localhost:5104/                                                                          1
       1003 http://docker.for.mac.localhost:5100/signin-oidc                                                               3

(12 rows affected)

https://github.com/dotnet-architecture/eShopOnContainers/wiki/unauthorized_client-error-on-login

posted on 2023-03-17 20:51  冠军  阅读(67)  评论(0编辑  收藏  举报