【问题标题】:Multiple authenticaion schemes in asp .net core 2.0asp .net core 2.0 中的多种身份验证方案
【发布时间】:2018-03-23 07:43:01
【问题描述】:

我需要在 ASP.NET Core 2.0 MVC 应用程序中支持两种身份验证类型:

  1. AddIdentityServerAuthentication
  2. AddOpenIdConnect

ASP.NET Core 1.0 版本非常简单。但在 2.0 版 版本语法发生了变化。这是我的代码:

services.AddAuthentication(o =>
    {
        o.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        o.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        o.DefaultAuthenticateScheme = OpenIdConnectDefaults.AuthenticationScheme;
    }).AddIdentityServerAuthentication(options =>
    {
        options.Authority = PlatformConfiguration.IdentityServerUri;
        options.RequireHttpsMetadata = false;
        options.SaveToken = true;
        options.ApiSecret = "somesecret";
        options.ApiName = "some_api";
    })
    .AddCookie()
    .AddOpenIdConnect(o =>
    {
        o.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        o.Authority = PlatformConfiguration.IdentityServerUri;
        o.RequireHttpsMetadata = false;
        o.ClientId = "some_viewer";
        o.UseTokenLifetime = true;
        o.ResponseType = "id_token token";
        o.Scope.Add("openid");
        o.Scope.Add("roles");
        o.Scope.Add("profile");
        o.SaveTokens = true;
        o.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = JwtClaimTypes.Name,
            RoleClaimType = JwtClaimTypes.Role
        };
    });

services.AddAuthorization();

但是这样一来,Bearer 身份验证就不起作用了。由于默认方案:DefaultChallengeScheme、DefaultAuthenticateScheme。
如何定义几种认证方案?

【问题讨论】:

    标签: identityserver3 openid-connect identityserver4 asp.net-core-2.0


    【解决方案1】:

    我添加了属性

    [Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme + "," + OpenIdConnectDefaults.AuthenticationScheme)]
    

    现在我有两个身份验证方案。

    在 Startup 中使用此代码的更灵活的解决方案:

    if (UseAuthorization)
    {
        var policy = new AuthorizationPolicyBuilder(IdentityServerAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme)
            .RequireAuthenticatedUser()
            .Build();
    
        options.Filters.Add(new AuthorizeFilter(policy));
    }
    

    【讨论】:

      【解决方案2】:

      这是我在 ASP.NET 2.0 中使用 JWT 令牌对网页进行身份验证的示例

      您可以找到我使用的语法,即使它不包括您的所有需求,我希望它对您有所帮助。

      Asp 网络核心

      第一步是编写配置Jwt认证的方法:

      // Configure authentication with JWT (Json Web Token).
      public void ConfigureJwtAuthService(IServiceCollection services)
      {
        // Enable the use of an [Authorize(AuthenticationSchemes = 
        // JwtBearerDefaults.AuthenticationScheme)]
        // attribute on methods and classes to protect.
        services.AddAuthentication().AddJwtBearer(cfg =>
        {
          cfg.RequireHttpsMetadata = false;
          cfg.SaveToken = true;
          cfg.TokenValidationParameters = new TokenValidationParameters()
          {
            IssuerSigningKey = JwtController.SecurityKey,
            ValidAudience = JwtController.Audience,
            ValidIssuer = JwtController.Issuer,
            // When receiving a token, check that we've signed it.
            ValidateIssuerSigningKey = true,
            // When receiving a token, check that it is still valid.
            ValidateLifetime = true,
            // This defines the maximum allowable clock skew when validating 
            // the lifetime. As we're creating the tokens locally and validating
            // them on the same machines which should have synchronised time,
            // this can be set to zero.
            ClockSkew = TimeSpan.FromMinutes(0)
          };
        });
      }
      

      现在在 Startup.cs 的 ConfigureServices() 方法中,我们可以调用 ConfigureJwtAuthService() 方法来配置 Jwt 认证。

      这是完整的Startup.cs:

      using System;
      using Autofac;
      using ExpertCodeBlogWebApp.Controllers;
      using ExpertCodeBlogWebApp.Domain;
      using ExpertCodeBlogWebApp.Domain.Interfaces;
      using Microsoft.AspNetCore.Builder;
      using Microsoft.AspNetCore.Hosting;
      using Microsoft.AspNetCore.SpaServices.Webpack;
      using Microsoft.Extensions.Configuration;
      using Microsoft.Extensions.DependencyInjection;
      
      using Microsoft.IdentityModel.Tokens;
      
      namespace ExpertCodeBlogWebApp
      {
        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 IServiceProvider ConfigureServices(IServiceCollection services)
        {
          services.AddMvc();
      
          // Configure jwt autenticazione 
          ConfigureJwtAuthService(services);
      
          // Repositories
          services.AddScoped<IUserRepository, UserRepository>();
      
          // Create the Autofac container builder for dependency injection
          var builder = new ContainerBuilder();
      
          // Add any Autofac modules or registrations. 
          builder.RegisterModule(new AutofacModule());
      
          // Return ServiceProvider
          var serviceProvider = services.BuildServiceProvider();
          return serviceProvider;
        }
      
        // Configure authentication with JWT (Json Web Token).
        public void ConfigureJwtAuthService(IServiceCollection services)
        {
          // Enable the use of an [Authorize(AuthenticationSchemes = 
          // JwtBearerDefaults.AuthenticationScheme)]
          // attribute on methods and classes to protect.
          services.AddAuthentication().AddJwtBearer(cfg =>
          {
            cfg.RequireHttpsMetadata = false;
            cfg.SaveToken = true;
      
            cfg.TokenValidationParameters = new TokenValidationParameters()
            {
              IssuerSigningKey = JwtController.SecurityKey,
              ValidAudience = JwtController.Audience,
              ValidIssuer = JwtController.Issuer,
              // When receiving a token, check that we've signed it.
              ValidateIssuerSigningKey = true,
              // When receiving a token, check that it is still valid.
              ValidateLifetime = true,
              // This defines the maximum allowable clock skew when validating 
              // the lifetime.
              // As we're creating the tokens locally and validating them on the 
              // same machines which should have synchronised time, this can be 
              // set to zero.
              ClockSkew = TimeSpan.FromMinutes(0)
            };
          });
        }
      
        // This method gets called by the runtime. Use this method to configure 
        // the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
          if (env.IsDevelopment())
          {
            app.UseDeveloperExceptionPage();
            app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
            {
              HotModuleReplacement = true
            });
          }
          else
          {
            app.UseExceptionHandler("/Home/Error");
          }
      
          app.UseStaticFiles();
      
          app.UseMvc(routes =>
          {
            routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
      
            routes.MapSpaFallbackRoute(
              name: "spa-fallback",
              defaults: new { controller = "Home", action = "Index" });
            });
          }
        }
      
        // For dependency injection.
        public class AutofacModule : Module
        {
          // Dependency Injection with Autofact
          protected override void Load(ContainerBuilder builder)
          {
            builder.RegisterType<UserRepository>().As<IUserRepository>()
              .SingleInstance();
          }
        }
      }
      

      JwtController.cs

      using System;
      using System.IdentityModel.Tokens.Jwt;
      using System.Security.Claims;
      using System.Security.Principal;
      using System.Text;
      using System.Threading.Tasks;
      using AutoMapper;
      using ExpertCodeBlogWebApp.Domain;
      using ExpertCodeBlogWebApp.Domain.Interfaces;
      using ExpertCodeBlogWebApp.Domain.Models;
      using ExpertCodeBlogWebApp.ViewModels;
      using Microsoft.AspNetCore.Mvc;
      using Microsoft.Extensions.Logging;
      using Microsoft.IdentityModel.Tokens;
      using Newtonsoft.Json;
      
      namespace ExpertCodeBlogWebApp.Controllers
      {
      
      [Route("api/[controller]")]
      public class JwtController : Controller
      {
        #region Private Members
        // JWT-related members
        private TimeSpan TokenExpiration;
        private SigningCredentials SigningCredentials;
        // EF and Identity members, available through DI
        private MyDbContext DbContext;
        private IUserRepository _userRepository;
        private readonly ILogger _logger;
        #endregion Private Members
      
        #region Static Members
        private static readonly string PrivateKey = "my_PrivateKey";
        public static readonly SymmetricSecurityKey SecurityKey = 
          new SymmetricSecurityKey(Encoding.ASCII.GetBytes(PrivateKey));
        public static readonly string Issuer = "my_Issuer";
        public static readonly string Audience = "my_Audience";
        #endregion Static Members
      
        #region Constructor
        // I have used Autofac in the Startup.cs for dependency injection)
        public JwtController(
          MyDbContext dbContext,
          IUserRepository userRepository,
          ILogger<JwtController> logger)
        {
          _logger = logger;
          _userRepository = userRepository;
          // Instantiate JWT-related members
          TokenExpiration = TimeSpan.FromMinutes(10);
          SigningCredentials = new SigningCredentials(SecurityKey, 
            SecurityAlgorithms.HmacSha256);
          // Instantiate through Dependency Injection with Autofact
          DbContext = dbContext;
        }
        #endregion Constructor
      
        #region Public Methods 
        // Manages the request for a new authentication or the refresh of an 
        // already established one
        [HttpPost("token")]
        public async Task<IActionResult> 
          Authentication([FromBody]JwtRequestViewModel jwt)
        {
          if (ModelState.IsValid)
          {
            string grantType = jwt.GrantType; 
            if (grantType == "password")
            {
              string userName = jwt.UserName;
              string password = jwt.Password;
      
              // Password check required
              var user = await 
                _userRepository.GetUserInfoWithCheckPwd(userName, password);
      
              // Check if user is expired (check the ExpireDate property)
              if (UserExpired(user))
                return BadRequest($"Account of {user.Name} expired!");
      
              if (UserEnabled(user))
                return await GenerateToken(user);
              else
                return BadRequest("User name or password invalid.");
            }
          }
          else if (grantType == "refresh_token")
          {
            string userName = jwt.UserName;
      
            // Refresh token (no password check required)
            var user = await _userRepository.GetUserInfoByName(userName);
      
            // Check if user is expired (check the ExpireDate property)
            if (UserExpired(user))
              return BadRequest($"Account of {user.Name} expired!");
      
            string token = jwt.Token;
            if (token == user.Token)
            {
              // Generate token and send it via a json-formatted string
              return await GenerateToken(user);
            }
            else
            {
              return BadRequest("User token invalid.");
            }
          }
          else
            return BadRequest("Authentication type invalid.");
        }
        else
          return BadRequest("Request invalid.");
        }
        #endregion Public Methods
      
        #region Private Methods
        private bool UserExpired(Users utente)
        {
          if (utente != null)
            return utente.ExpireDate.CompareTo(DateTime.Now) < 0;
          return true;
        }
      
        private bool UserEnabled(Users utente)
        {
          if (utente != null)
            return utente.Enabled == true;
          return false;
        }
      
        private JsonSerializerSettings DefaultJsonSettings
        {
          get
          {
            return new JsonSerializerSettings()
            {
              Formatting = Formatting.Indented
            };
          }
        }
      
        private async Task<IActionResult> GenerateToken(Users user)
        {
          try
          {
            if (user != null)
            {
              var handler = new JwtSecurityTokenHandler();
              DateTime newTokenExpiration = DateTime.Now.Add(TokenExpiration);
      
              ClaimsIdentity identity = new ClaimsIdentity(
                new GenericIdentity(user.Name, "TokenAuth"),
                new[] { new Claim("ID", user.Id.ToString())}
              );
      
              var securityToken = handler.CreateToken(new SecurityTokenDescriptor
              {
                Issuer = JwtController.Issuer,
                Audience = JwtController.Audience,
                SigningCredentials = SigningCredentials,
                Subject = identity,
                Expires = newTokenExpiration
              });
              string encodedToken = handler.WriteToken(securityToken);
      
              // Update token data on database
              await _userRepository.UpdateTokenData(user.Name, encodedToken, 
                newTokenExpiration);
              // Build the json response 
              // (I use Automapper to maps an object into another object)
              var jwtResponse = Mapper.Map<JwtResponseViewModel>(user);
              jwtResponse.AccessToken = encodedToken;
              jwtResponse.Expiration = (int)TokenExpiration.TotalSeconds;
              return Ok(jwtResponse);
            }
            return NotFound();
            }
            catch(Exception e)
            {
              return BadRequest(e.Message);
            }
          }
          #endregion
        }
      }
      

      在我的项目中,我使用 Angular。 Angular 调用 JwtController 方法:

      login(userName: string, password: string)
      {
        return this.getLoginEndpoint(userName, password)
          .map((response: Response) => this.processLoginResponse(response));
      }
      
      getLoginEndpoint(userName: string, password: string): Observable<Response> 
      {
        // Body
        // JwtRequest is a model class that I use to send info to the controller
        let jwt = new JwtRequest(); 
        jwt.GrantType = "password";
        jwt.UserName = userName;
        jwt.Password = password;
        jwt.ClientId = "my_Issuer";
        // Post requiest (I use getAuthHeader that attach to the header the
        // authentication token, but it can also be omitted because it is ignored
        // by the JwtController
        return this.http.post(this.loginUrl, JSON.stringify(jwt), 
          this.getAuthHeader(true))
      }
      
      protected getAuthHeader(includeJsonContentType?: boolean): RequestOptions
      {
        // Hera I use this.authService.accessToken  that is a my service where
        // I have store the token received from the server
        let headers = new Headers({
          'Authorization': 'Bearer ' + this.authService.accessToken });
      
        if (includeJsonContentType)
          headers.append("Content-Type", "application/json");
      
        headers.append("Accept", `application/vnd.iman.v01+json, 
          application/json, text/plain, */*`);
        headers.append("App-Version", "01");
      
        return new RequestOptions({ headers: headers });
      }
      
      private processLoginResponse(response: Response)
      {
        // process the response..
      }
      

      在您希望只有经过身份验证的用户才能访问的控制器类(或方法)上(而不是在您的 JwtController 上,因为它的方法必须可供所有用户访问)您可以设置:

      [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
      

      要从 Angular 调用需要身份验证的控制器方法,您需要使用 getAuthHeader() 方法将令牌附加到标头中。

      希望这篇文章对你有帮助。

      【讨论】:

      • 好多了,谢谢。
      猜你喜欢
      • 2021-10-18
      • 1970-01-01
      • 1970-01-01
      • 2019-08-31
      • 2018-01-27
      • 2018-03-29
      • 2020-03-09
      • 1970-01-01
      • 2022-01-04
      相关资源
      最近更新 更多