我有一个简单的WebApi项目,它使用IdentityServer4.AccessTokenValidation
验证开发地址为https://localhost:44347
的IdentityServer4
服务器发布的令牌
我通过向identityserver发送以下数据来获得令牌:
POST
https://localhost:44347/connect/token
client_id:x.api.client
client_secret:secret
response_type:code id_token
scope:X.api
grant_type:client_credentials
响应为:
{
"access_token": "THETOKEN",
"expires_in": 1209600,
"token_type": "Bearer"
}
并将令牌发送到WebAPi
POST
http://localhost:59062/identity
Authorization:Bearer THETOKEN
我得到了想要的结果,但是,添加了以下代码的注释部分,结果404未找到。
代码为:
public class Startup {
private const string API_NAME = "X.api";
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services) {
string connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddLogging(configure => configure.AddConsole());
services.AddDbContext<MyDataContext>(options => options.UseSqlServer(connectionString));
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddTransient<IUserStore<MyUser>, MyUserStore>();
services.AddTransient<IRoleStore<MyRole>, RoleStore>();
services.AddTransient<IPasswordHasher<MyUser>, MyHasher>();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options => {
options.Authority = "https://localhost:44347";
options.RequireHttpsMetadata = false;
options.ApiName = API_NAME;
});
////This commented part brokes API
//services.AddIdentity<MyUser, MyRole>(options => {
// options.Password.RequireDigit = true;
// options.Password.RequiredLength = 6;
// options.Password.RequireNonAlphanumeric = false;
// options.Password.RequireUppercase = false;
// options.Password.RequireLowercase = false;
// options.SignIn.RequireConfirmedEmail = false;
//})
//Bekaz we are not using IdentityUser as base
//.AddUserStore<MyUserStore>()
//.AddRoleStore<RoleStore>()
//.AddDefaultTokenProviders();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
}
API就像下面的代码(身份服务器的示例之一(一样简单
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
namespace Api.Controllers {
[Route("[controller]")]
[Authorize]
public class IdentityController : ControllerBase {
[HttpGet]
public IActionResult Get() {
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
}
我使用从IIdentity
继承的自定义User类、自定义Role
和UserRole
、自定义RoleStore
实现的IRoleStore<MyRole>
和自定义UserStore
实现的IUserStore<MyUser>, IUserPasswordStore<MyUser>
。
编辑,更多信息
这是我在控制台上得到的:
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/identity
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Route matched with {action = "Get", controller = "Identity"}. Executing action Api.Controllers.IdentityController.Get ()
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
Executing ChallengeResult with authentication schemes ().
[16:48:20 Information] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was challenged.
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12]
AuthenticationScheme: Identity.Application was challenged.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action Api.Controllers.IdentityController.Get () in 30.1049ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 103.2969ms 302
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/Account/Login?ReturnUrl=%2Fidentity
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 0.468ms 404
临时解决方案
authorization system
有问题,我终于把属性改成了我在这里创建的
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
它是有效的。但是如何以及为什么?我现在还没有。
此外,将AddAuthentication
部分更改为以下内容,如前所述,答案表明,不起作用,并且需要将(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)
传递给[Authorize]
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddIdentityServerAuthentication(options => {
options.Authority = "https://localhost:44347";
options.RequireHttpsMetadata = false;
options.ApiName = API_NAME;
});
改变秩序,终于奏效了。(先AddIdentity
,后AddAuthentication
(
services.AddIdentity<MyUser, MyRole>(options => {
options.Password.RequireDigit = true;
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.SignIn.RequireConfirmedEmail = false;
})
.AddUserStore<MyUserStore>()
.AddRoleStore<RoleStore>();
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddIdentityServerAuthentication(options => {
options.Authority = "https://localhost:44347";
options.RequireHttpsMetadata = false;
options.ApiName = API_NAME;
});
让我试着解释一下,这样其他可怜的灵魂就更容易理解了:(
当像上面的一样添加身份验证时
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
....
这意味着,放在方法或控制器类顶部的每个属性[Authorize]都将尝试根据默认身份验证模式(在本例中为JwtBearer(进行身份验证,并且它不会级联向下尝试使用可能声明的其他模式(如Cookie模式(进行身份认证。为了使AuthorizeAttribute根据cookie模式进行身份验证,必须像上面的代码一样指定它
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
这也将以另一种方式工作,即,如果cookie模式是默认的,则必须声明JwtBearer模式,以便为那些需要JwtBear令牌身份验证的方法或控制器进行授权
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]