我想在程序中同时使用基于cookie的身份验证和jwt
,使用身份验证用户通过登录名访问mvc
控制器,并使用JWT访问WebApi资源。
我尝试使用其中两个:首先,我的客户端可以使用用户名和密码登录并通过cookie进行身份验证。使用带有令牌载体的WebApi从应用程序获得第二次访问资源,但出现错误!
在我的startup.cs
文件中,我有:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
options.ConsentCookie.Name = "Cookie";
});
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "Cookie";
options.ClaimsIssuer = Configuration["Authentication:ClaimsIssuer"];
});
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddDefaultTokenProviders();
services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 5;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = false;
//Token
});
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.Cookie.Name = "Cookie";
options.ClaimsIssuer = Configuration["Authentication:ClaimsIssuer"];
})
.AddMicrosoftAccount(microsoftOptions =>
{
microsoftOptions.ClientId = Configuration["Authentication:Microsoft:ApplicationId"];
microsoftOptions.ClientSecret = Configuration["Authentication:Microsoft:Password"];
})
.AddGoogle(googleOptions =>
{
googleOptions.ClientId = "XXXXXXXXXXX.apps.googleusercontent.com";
googleOptions.ClientSecret = "g4GZ2#...GD5Gg1x";
googleOptions.Scope.Add("https://www.googleapis.com/auth/plus.login");
googleOptions.ClaimActions.MapJsonKey(ClaimTypes.Gender, "gender");
googleOptions.SaveTokens = true;
googleOptions.Events.OnCreatingTicket = ctx =>
{
List<AuthenticationToken> tokens = ctx.Properties.GetTokens()
as List<AuthenticationToken>;
tokens.Add(new AuthenticationToken()
{
Name = "TicketCreated",
Value = DateTime.UtcNow.ToString()
});
ctx.Properties.StoreTokens(tokens);
return Task.CompletedTask;
};
})
.AddJwtBearer(options =>
{
options.ClaimsIssuer = Configuration["Authentication:ClaimsIssuer"];
options.SaveToken = true;
options.Authority = Configuration["Authentication:Authority"];
options.Audience = Configuration["Authentication:Audience"];
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidIssuer = Configuration["Authentication:ValidIssuer"],
ValidateAudience = true,
ValidAudience = Configuration["Authentication:ValidAudience"],
ValidateLifetime = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Authentication:SecurityKey"]))
};
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSession();
services.AddSingleton<IConfiguration>(Configuration);
}
我在此控制器中获得了一个令牌:
[AllowAnonymous]
[HttpPost]
public async Task<IActionResult> GetToken(TokenLoginModel model)
{
if (!ModelState.IsValid) return BadRequest("Token failed to generate");
var user = await _usermanager.FindByNameAsync(model.UserName);
//var user = true;// (model.Password == "password" && model.Username == "username");
if (user != null && await _usermanager.CheckPasswordAsync(user, model.Password))
{
var claims = new[]{
new Claim("ClaimsIssuer", _configuration.GetSection("Authentication:ClaimsIssuer").Value),
new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Sub,user.UserName),
new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())
};
string SecurKey = Startup.StaticConfig.GetSection("Authentication:SecurityKey").Value;
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurKey));
var token = new JwtSecurityToken(
issuer: _configuration.GetSection("Authentication:ValidIssuer").Value,
audience: _configuration.GetSection("Authentication:Audience").Value,
expires: DateTime.UtcNow.AddDays(30),
claims: claims,
signingCredentials: new Microsoft.IdentityModel.Tokens.SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
});
}
return Unauthorized();
}
我实现了创建令牌的控件,但是当我尝试对此进行授权时,出现此错误:
An unhandled exception occurred while processing the request.
HttpRequestException: Response status code does not indicate success: 404 (Not Found).
System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
IOException: IDX20804: Unable to retrieve document from: 'https://localhost:44383/oauth2/default/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string address, CancellationToken cancel)
InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://localhost:44383/oauth2/default/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.GetConfigurationAsync(CancellationToken cancel)
答案 0 :(得分:1)
为了增加对JWT的支持,我们添加了AddCookie和AddJwtBearer。让网站在标头中要求令牌将是一件令人头疼的事情,尤其是对于并非纯粹是SPA或API的项目。因此,我真正想要的是同时支持Cookie和JWT。
在startup.cs中,您拥有:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DualAuthContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<DualAuthContext>()
.AddDefaultTokenProviders();
// Enable Dual Authentication
services.AddAuthentication()
.AddCookie(cfg => cfg.SlidingExpiration = true)
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Tokens:Issuer"],
ValidAudience = Configuration["Tokens:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
};
});
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc();
}
在“配置”方法中:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, DataSeeder seeder)
{
...
app.UseAuthentication();
}
在控制器中使用了JWT之后,应将JWT Bearer AuthenticationSchemes添加到Authorize属性,如下所示:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("/api/customers")]
public class ProtectedController : Controller
{
public ProtectedController()
{
}
public IActionResult Get()
{
return Ok(new[] { "One", "Two", "Three" });
}
}
引用:Two AuthorizationSchemes in ASP.NET Core 2
使用起来非常简单而且很有帮助。
答案 1 :(得分:0)
以下是我使用OpenIdConnect进行的配置 在您的startup.cs
配置
app.UseCookiePolicy();
ConfigureServices
services
.AddIdentity<User, ApplicationRole>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 4;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
//lock out attempt
options.Lockout.AllowedForNewUsers = true;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 3;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
//The default value is 14 days.
services.ConfigureApplicationCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromHours(1);
});
// Configure Identity to use the same JWT claims as OpenIddict instead
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
// which saves you from doing the mapping in your authorization controller.
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
services.AddOpenIddict()
// Register the OpenIddict core services.
.AddCore(options =>
{
// Register the Entity Framework stores and models.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
})
// Register the OpenIddict server handler.
.AddServer(options =>
{
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
options.UseMvc();
// Enable the token endpoint.
options.EnableTokenEndpoint("/connect/token")
.EnableAuthorizationEndpoint("/connect/authorize")
.EnableLogoutEndpoint("/connect/logout")
.EnableIntrospectionEndpoint("/connect/introspect")
.EnableUserinfoEndpoint("/connect/userinfo");
// Enable the password and the refresh token flows.
options.AllowPasswordFlow()
.AllowRefreshTokenFlow();
// Accept anonymous clients (i.e clients that don't send a client_id).
options.AcceptAnonymousClients();
// During development, you can disable the HTTPS requirement.
options.DisableHttpsRequirement();
// Note: to use JWT access tokens instead of the default
// encrypted format, the following lines are required:
//
options.UseJsonWebTokens();
options.AddEphemeralSigningKey();
options.SetAccessTokenLifetime(TimeSpan.FromMinutes(60))
.SetRefreshTokenLifetime(TimeSpan.FromMinutes(60));
});
// Register the OpenIddict validation handler.
// Note: the OpenIddict validation handler is only compatible with the
// default token format or with reference tokens and cannot be used with
// JWT tokens. For JWT tokens, use the Microsoft JWT bearer handler.
//.AddValidation();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
services.AddAuthentication()
.AddJwtBearer(options =>
{
options.Authority = configuration["Authentication:Authority"];
options.Audience = "resource_server";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = OpenIdConnectConstants.Claims.Subject,
RoleClaimType = OpenIdConnectConstants.Claims.Role
};
});
// Alternatively, you can also use the introspection middleware.
// Using it is recommended if your resource server is in a
// different application/separated from the authorization server.
//
// services.AddAuthentication()
// .AddOAuthIntrospection(options =>
// {
// options.Authority = new Uri("http://localhost:54895/");
// options.Audiences.Add("resource_server");
// options.ClientId = "resource_server";
// options.ClientSecret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd";
// options.RequireHttpsMetadata = false;
// });