不记名身份验证来自邮递员,而不是来自 android 应用程序

Bearer authentication working from postman but not from android app

我遇到了一个奇怪的问题,它有点复杂,但我会尽力解释它。

我有一个 运行 在 ASP.Net Core 2.2 上运行的 CMS。我最近在 cookie 身份验证中添加了使用 JWT 的 Bearer 身份验证。 Startup.cs 文件如下所示:

services.AddAuthentication()
    .AddCookie(cfg =>
    {
        cfg.SlidingExpiration = true;
        cfg.ExpireTimeSpan = TimeSpan.FromMinutes(20);
        cfg.LoginPath = new PathString("/account/login");
        cfg.AccessDeniedPath = new PathString("/Account/AccessDenied");
    })
    .AddJwtBearer(cfg =>
    {
        cfg.RequireHttpsMetadata = false;
        cfg.SaveToken = true;
        cfg.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
    });

[...]

services.ConfigureApplicationCookie(options =>
{
    options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
    options.SlidingExpiration = true;
});

services.AddMvc(config =>
{
    var policy = new AuthorizationPolicyBuilder() //makes every controller an every action requiring authorization except if labled by [AllowAnnonymous]
        .RequireAuthenticatedUser()
        .Build();
    config.Filters.Add(new AuthorizeFilter(policy));
})
.AddViewLocalization(
    options => { options.ResourcesPath = "Resources"; })
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

所以,如你所见,cookie认证是主要用途,我想在Bearer上使用的每个功能API,我注释如下:

[HttpGet]
[Produces("application/json")]
[Route("api/[controller]/[action]/")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public ActionResult GetUserDetails()
{
    ...
}

几个月来,这在不同的 .NET 应用程序中运行良好,当然还有邮递员。但是现在我想连接一个 Android 应用程序(使用 Java 和改造)和 none 的功能工作,即使我在HTTP 请求。一旦我删除了 Authorize 注释并将其替换为 AllowAnonymous 它就起作用了,奇怪的部分来了:我可以在请求中看到 Authorization 参数 header.

这里是一些截图:

Authorization 活动的 Postman 调用的函数 - 成功返回:

从 android 应用程序调用的函数 Authorization 处于活动状态。控制器操作甚至没有被击中,而是请求被重定向到 CMS 的登录页面(顺便说一句,同样的事情发生了,当我从邮递员那里拿令牌到 android 应用程序和 运行请求与我用于邮递员的令牌完全相同):

现在,当我删除控制器上的 Authorize 属性并添加一个 AllowAnonymous 时,它就像一个魅力,但我可以看到即使 header 包含Authorization 属性,用户显示为未经身份验证:

我已经坚持了几个小时了,我试图改变 Startup.cs 文件中的一些东西,我阅读了大量关于改造、oAuth2、Bearer 等的文章,但不幸的是找不到到目前为止有任何帮助。

所以对我来说,这种行为看起来很奇怪,我的问题是,我是否在这里遗漏了一些东西,header 中除了 Authorization 之外的任何特殊信息或其他什么? 任何输入将不胜感激。

编辑 1:

这是 Java (Android) 应用程序的代码:

//Endpoint
public interface TradeEndPoint {
    @Headers({ "Content-Type: application/json;charset=UTF-8"})
    @GET("Trade/getAllActiveItemsApi")
    Call<List<TradeItem>> getAllActiveItems(@Header("Authorization") String authHeader);
}

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(baseUrl.getStringValue())
        .addConverterFactory(GsonConverterFactory.create())
        .build();


TradeEndPoint te = retrofit.create(TradeEndPoint.class);

final Call<List<TradeItem>> getItems = te.getAllActiveItems("Bearer " + getAccessToken(context));
getItems.enqueue(new Callback<List<TradeItem>>() {
    @Override
    public void onResponse(Call<List<TradeItem>> call, Response<List<TradeItem>> response) {
        TradeItemsLoaded til = (TradeItemsLoaded) fragment;
        til.onTradeItemsLoaded(response);
    }

    @Override
    public void onFailure(Call<List<TradeItem>> call, Throwable t) {
        t.printStackTrace();
    }
});

函数 getAccessToken(context) returns 将标记作为字符串。

这是提琴手的截图session。 这是来自 android 应用程序

这来自 Postman(或 Firefox 的等效扩展,称为 RESTer)

所以是的,在 header 中存在差异,但如果有的话,哪一个是重要的?

我找到了解决方案。

Startup.cs 文件中有以下表达式:

services.AddMvc(config =>
    {
        var policy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
        config.Filters.Add(new AuthorizeFilter(policy));
    })
    .AddViewLocalization(
        options => { options.ResourcesPath = "Resources"; })
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

事实证明,授权过滤器在使用 postman 和 .NET 时确实有效,但由于某种原因,当从 android 应用程序使用 Bearer 身份验证时会出现问题。

我删除了这个,现在看起来像这样:

services.AddMvc()
    .AddViewLocalization(
        options => { options.ResourcesPath = "Resources"; })
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

现在我需要用 [Authorize] 注释每个控制器,但至少它可以工作(也许你也可以让授权过滤器与 android 一起工作,但无论如何我都没有使用它,所以这没什么大不了的)。