ASP.Net Core MVC / API / SignalR-更改身份验证方案(Cookie和JWT)

时间:2018-12-26 21:09:48

标签: c# authentication asp.net-core-mvc jwt asp.net-core-2.2

我有一个.Net Core 2.2 Web应用程序MVC,其中添加了API控制器和SignalR集线器。另一方面,我有一个移动应用程序,它调用中心方法。从应用程序调用集线器之前,我正在通过API调用对用户进行身份验证-返回JWT令牌-并将此令牌用于将来的请求,这样我就可以在集线器方法中使用Context.User.Identity.Name

public static async Task<string> GetValidToken(string userName, string password)
{
   using (var client = new HttpClient())
   {
     client.BaseAddress = new Uri(_API_BASE_URI);
     client.DefaultRequestHeaders.Accept.Clear();
     client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

     LoginViewModel loginVM = new LoginViewModel() { Email = userName, Password = password, RememberMe = false };
     var formContent = Newtonsoft.Json.JsonConvert.SerializeObject(loginVM);
     var content = new StringContent(formContent, Encoding.UTF8, "application/json");
     HttpResponseMessage responseMessage;
     try
     {
        responseMessage = await client.PostAsync("/api/user/authenticate", content);
        var responseJson = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); ;
        var jObject = JObject.Parse(responseJson);
        _TOKEN = jObject.GetValue("token").ToString();
        return _TOKEN;
     }catch
        [...]

然后使用令牌:

_connection = new HubConnectionBuilder().WithUrl(ApiCommunication._API_BASE_URI + "/network", options =>
{
  options.AccessTokenProvider = () => Task.FromResult(token);
}).Build();

到目前为止,一切都很好。它在我的移动应用程序上按预期工作。但是为了使其正常工作,我必须在服务器端(Startup.cs)上设置以下代码:

services.AddAuthentication(options =>
{
   options .DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
   options .DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
 })
 .AddJwtBearer(x =>
 {
    x.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            ...

这使我无法再使用cookie身份验证,因此mvc Web应用程序无法正常运行,因为它无法在请求中获取当前已身份验证的用户。

删除行:

options .DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options .DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

使网络应用正常运行,但不再使移动应用正常运行(由于Context.User.Identity.Name等于null,集线器调用失败)。

我一直在寻找有关如何处理不同方案(在我的情况下为cookie + jwt)的方法,根据我的理解,这是设计使然无法实现的。

是否有可能使用双重方案的解决方法,或者我丢失了某些东西?

我想也许我应该托管两个单独的项目,并使用一个Cookie身份验证,另一个使用JWT?

谢谢。

1 个答案:

答案 0 :(得分:3)

有多种方法可以解决您遇到的问题,但首先让我们看一下为什么它当前不起作用。

DefaultAuthenticateScheme的含义

当您为DefaultAuthenticateScheme的{​​{1}}属性设置一个值时,您指示认证中间件尝试针对该特定方案对每个HTTP请求进行认证。我假设您使用ASP.NET Identity进行基于cookie的身份验证,并且在调用AuthenticationOptions时,它将cookie身份验证方案注册为默认身份验证方案;您可以在source code on GitHub中看到它。

但是,这并不意味着您不能在应用程序中使用任何其他身份验证方案。

授权系统默认策略

如果要使用Cookie或JWT进行身份验证的客户端可以访问应用程序的所有受保护端点,则一种选择是使用授权系统默认策略。当您使用AddIdentity类的“空”实例时,将使用该特殊策略-作为装饰控制器/动作的属性,或在应用程序级别全局使用AuthorizeAttribute

默认策略设置为仅要求经过身份验证的用户,但未定义需要“尝试”哪些身份验证方案以对请求进行身份验证。结果是它依赖于已经执行过的身份验证过程。它说明了您在两种方案中一次只能起作用的情况下遇到的行为。

我们可以通过一些代码来更改默认策略:

new AuthorizeFilter(new AuthorizeAttribute())

特定的授权政策

如果您遇到这样的情况,即您要求某些终结点只能由经过cookie验证的客户端访问,而另一些需要通过JWT进行访问的客户端,则可以利用授权策略。

它们的工作原理与默认策略完全相同,希望您能在适用的端点基础上进行选择。您可以像这样添加策略:

services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes("<your-cookie-authentication-scheme", "your-jwt-authentication-scheme")
        .Build();
})

然后可以通过用services.AddAuthorization(options => { options.AddPolicy("Cookies", new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .AddAuthenticationSchemes("<your-cookie-authentication-scheme") .Build()); options.AddPolicy("JWT", new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .AddAuthenticationSchemes("<your-jwt-authentication-scheme") .Build()); }) 装饰它们来在适当的端点中引用这些策略。附带说明一下,如果您的策略之间唯一的区别是身份验证方案,则无需创建策略即可获得相同的结果,并且可以在[Authorize(Policy = "<policy-name>")]属性中使用{{1 }}属性。

当您有更复杂的规则时,例如特定声明需要此特定值的政策非常有用。

希望这对您有所帮助,让我知道您的情况!