c# - JWT 授权重定向到登录

标签 c# asp.net-web-api asp.net-core jwt asp.net-core-2.0

我使用标准身份开发了一个带有个人用户帐户的 Core 2 MVC 应用程序。 MVC 应用程序一切正常。

我正在尝试添加公共(public) API 并使用 JWT 授权请求。

但是,当它试图授权用户时出了点问题。当我向 Controller 提交请求时,它会将我重定向到我的登录页面,因此响应正文是 HTML。

我需要授权并返回数据(如果 token 无效则不需要)。

我做错了什么? token 生成一切正常。

token Controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using TechsportiseOnline.Models.AccountViewModels;
using Microsoft.AspNetCore.Identity;
using TechsportiseOnline.Models;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using TechsportiseOnline.Helpers;

namespace TechsportiseOnline.Controllers
{
    [Produces("application/json")]
    [Route("api/Token")]
    public class TokenController : Controller
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly IConfiguration _configuration;
        private readonly IOptions<JWTSettings> _jwtConfig;

        public TokenController(
              UserManager<ApplicationUser> userManager,
              SignInManager<ApplicationUser> signInManager,
              IConfiguration configuration,
              IOptions<JWTSettings> jwtConfig)
                    {
                        _userManager = userManager;
                        _signInManager = signInManager;
                        _configuration = configuration;
                        _jwtConfig = jwtConfig;


        }

        [AllowAnonymous]
        [HttpPost]
        public async Task<IActionResult> GenerateToken([FromBody] LoginViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = await _userManager.FindByEmailAsync(model.Email);

                if (user != null)
                {
                    var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false);
                    if (result.Succeeded)
                    {

                        var claims = new[]
                        {
                          new Claim(JwtRegisteredClaimNames.Sub, user.Email),
                          new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                        };

                        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.Value.SecretKey.ToString()));
                        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                        var token = new JwtSecurityToken(_jwtConfig.Value.Issuer.ToString(),
                          _jwtConfig.Value.Audience.ToString(),
                          claims,
                          expires: DateTime.Now.AddDays(30),
                          signingCredentials: creds);

                        return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
                    }
                }
            }

            return BadRequest("Could not create token");
        }
    }
}

Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using TechsportiseOnline.Data;
using TechsportiseOnline.Models;
using TechsportiseOnline.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using TechsportiseOnline.Authorization;
using TechsportiseOnline.Helpers;
using Swashbuckle.AspNetCore.Swagger;
using System.IO;
using Microsoft.Extensions.PlatformAbstractions;
using static TechsportiseOnline.Helpers.Swagger;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace TechsportiseOnline
{
    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<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("TechsportiseDB")));
            //options.UseInMemoryDatabase("Teschsportise"));


            services.Configure<JWTSettings>(Configuration);

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                      .AddJwtBearer(cfg =>
                      {
                          cfg.IncludeErrorDetails = true;
                          cfg.RequireHttpsMetadata = false;
                          cfg.SaveToken = true;

                          var secretKey = Configuration.GetSection("JWTSettings.SecretKey").Value;
                          var issuer = Configuration.GetSection("JWTSettings.Issuer").Value;
                          var audience = Configuration.GetSection("JWTSettings.Audience").Value;
                          var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

                          cfg.TokenValidationParameters = new TokenValidationParameters()
                          {
                              ValidateIssuer = true,
                              ValidIssuer = Configuration.GetSection("JWTSettings.Issuer").Value,
                              ValidateAudience = true,
                              ValidAudience = Configuration.GetSection("JWTSettings.Audience").Value,
                              IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWTSettings:SecretKey"]))
                          };

                      });

            services.Configure<JWTSettings>(Configuration.GetSection("JWTSettings"));

            services.AddIdentity<ApplicationUser, IdentityRole>(config =>
                {
                    config.SignIn.RequireConfirmedEmail = true;
                })
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.Configure<IdentityOptions>(options =>
            {
                // Password settings
                options.Password.RequireDigit = true;
                options.Password.RequiredLength = 6;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequireLowercase = false;
                options.Password.RequiredUniqueChars = 2;

                // Lockout settings
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
                options.Lockout.MaxFailedAccessAttempts = 10;
                options.Lockout.AllowedForNewUsers = true;

                // User settings
                options.User.RequireUniqueEmail = true;
            });

            services.Configure<AuthMessageSenderOptions>(Configuration);



            services.ConfigureApplicationCookie(options =>
            {
                // Cookie settings
                options.Cookie.HttpOnly = true;
                options.Cookie.Expiration = TimeSpan.FromDays(150);
                options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login
                options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout
                options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied
                options.SlidingExpiration = true;
            });

            // Add application services.
            services.AddTransient<IEmailSender, Email>();
            //services.AddTransient<ICreateContact>();
            //services.AddTransient<IUpdateContact>();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info { Title = "Techsportise API", Version = "v1" });
                c.OperationFilter<AddRequiredHeaderParameter>();
                var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "Techsportise.xml");
                c.IncludeXmlComments(filePath);
            });

            services.AddMvc();

            var skipSSL = Configuration.GetValue<bool>("LocalTest:skipSSL");
            // requires using Microsoft.AspNetCore.Mvc;
            services.Configure<MvcOptions>(options =>
            {
                // Set LocalTest:skipSSL to true to skip SSL requrement in 
                // debug mode. This is useful when not using Visual Studio.
                if (!skipSSL)
                {
                    options.Filters.Add(new RequireHttpsAttribute());
                }
            });


            services.AddMvc(config =>
            {
                var policy = new AuthorizationPolicyBuilder()
                                 .RequireAuthenticatedUser()
                                 .Build();
                config.Filters.Add(new AuthorizeFilter(policy));
            });

            services.AddScoped<IAuthorizationHandler,
                      OwnerRaceAuthorizationHandler>();

            services.AddSingleton<IAuthorizationHandler,
                                  AdminRaceAuthorizationHandler>();

            services.AddScoped<IAuthorizationHandler,
                      OwnerRaceEntriesAuthorizationHandler>();

            services.AddSingleton<IAuthorizationHandler,
                                  AdminRaceEntriesAuthorizationHandler>();

        }

        // 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.UseBrowserLink();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseAuthentication();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "Techsportise API V1");
            });


        }
    }
}

RacesController

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TechsportiseOnline.Data;
using TechsportiseOnline.Helpers;
using TechsportiseOnline.Models;

namespace TechsportiseOnline.Controllers
{

    /// <summary>
    /// This class is used as an API for Races
    /// </summary>
    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    [Route("api/[controller]")]
    public class RaceController : Controller
    {



        private readonly ApplicationDbContext _context;
        private readonly IAuthorizationService _authorizationService;
        private readonly UserManager<ApplicationUser> _userManager;

        public RaceController(ApplicationDbContext context,
           IAuthorizationService authorizationService,
           UserManager<ApplicationUser> userManager)
        {
            _context = context;
            _userManager = userManager;
            _authorizationService = authorizationService;
        }

        /// <summary>
        /// Get all Races
        /// </summary>
        /// <remarks>
        /// Gets all Races which have been created by the user
        /// </remarks>
        /// <returns>All created Races</returns>
        [HttpGet]
        public IEnumerable<Race> GetAll()
        {
            //Get only records where the OwnerId is not the logged in User.
            return _context.Races.Where(p => p.OwnerID == _userManager.GetUserId(User)).ToList();
        }

        /// <summary>
        /// Get a single Race
        /// </summary>
        /// <remarks>
        /// Gets the details from a single Race from it's ID
        /// </remarks>
        /// <param name="id">Race ID</param>
        /// <returns>Single Race</returns>
        [HttpGet("{id}", Name = "GetRace")]
        public IActionResult GetById(long id)
        {
            //Only return the data when it is owned by the same Id
            var item = _context.Races.Where(p => p.OwnerID == _userManager.GetUserId(User)).FirstOrDefault(t => t.ID == id);


            if (item == null)
            {
                return NotFound();
            }

            return new ObjectResult(item);
        }

        /// <summary>
        /// Get all entries for a Race
        /// </summary>
        /// <remarks>
        /// Gets the all the entries from the race ID
        /// </remarks>
        /// <param name="id">Race ID</param>
        /// <returns>All Entries from the given Race ID</returns>
        [HttpGet("{id}/entries", Name = "GetEntriesByRaceID")]
        public IEnumerable<RaceEntry> GetAllEntries(long id)
        {
            //Only return the data when it is owned by the same Id
            //Get only records where the OwnerId is not the logged in User.
            return _context.RaceEntries.Where(p => p.OwnerID == _userManager.GetUserId(User))
                                        .Where(p => p.RaceID == id)
                                        .ToList();
        }



        ///// <summary>
        ///// Get all timings for a Race
        ///// </summary>
        ///// <remarks>
        ///// Gets the all the timings from the race ID
        ///// </remarks>
        ///// <param name="id">Race ID</param>
        ///// <returns>All timings from the given Race ID</returns>
        //[HttpGet("{id}/timings", Name = "GetTimingsByRaceID")]
        //public IEnumerable<Timing> GetAllTimings(long id)
        //{

        //    //Only return the data when it is owned by the same Id
        //    //Get only records where the OwnerId is not the logged in User.
        //    return _context.Timings.Where(p => p.OwnerId == User.GetUserId())
        //                                .Where(p => p.RaceId == id)
        //                                .ToList();
        //}

        ///// <summary>
        ///// Get the results for a Race
        ///// </summary>
        ///// <remarks>
        ///// Gets the all the results from the race ID
        ///// </remarks>
        ///// <param name="id">Race ID</param>
        ///// <returns>All results from the given Race ID</returns>
        //[HttpGet("{id}/results", Name = "GetResultsByRaceID")]
        //public IEnumerable<Results> GetAllResults(long id)
        //{
        //    List<Results> raceresults = new List<Results>();
        //    var raceid = id;

        //    foreach (var raceentry in _context.RaceEntries.Where(p => p.OwnerId == User.GetUserId())
        //                                                    .Where(p => p.RaceID == id))
        //    {
        //        var raceresult = new Results();
        //        var racedetails = _context.Races.Where(t => t.OwnerId == User.GetUserId())
        //                                            .FirstOrDefault(t => t.Id == raceid);
        //        var timingdetails = _context.Timings.Where(t => t.OwnerId == User.GetUserId())
        //                                                .FirstOrDefault(t => t.BibNumber == raceentry.BibNumber);

        //        var race = _context.Races.Where(t => t.OwnerId == User.GetUserId())
        //                             .FirstOrDefault(t => t.Id == id);


        //        raceresult.AthleteUserID = raceentry.AthleteUserId;
        //        raceresult.Category = "Category";
        //        raceresult.CategoryPosition = 1;
        //        raceresult.ChipTime = DateTime.Now; //timingdetails.EndTime - timingdetails.StartTime;
        //        raceresult.Club = raceentry.Club;
        //        raceresult.ClubPosition = 1;
        //        raceresult.EntryId = raceentry.Id;
        //        raceresult.FirstName = raceentry.FirstName;
        //        raceresult.Gender = raceentry.Gender;
        //        raceresult.GenderPosition = 1;
        //        raceresult.GunTime = DateTime.Now; //race.RaceStartTime - timingdetails.EndTime;
        //        raceresult.LastName = raceentry.LastName;
        //        raceresult.OverallPosition = 0;
        //        raceresult.RaceDate = race.RaceDate;
        //        raceresult.RaceID = raceid;
        //        raceresult.RaceName = race.Name;
        //        raceresult.ResultId = 1;
        //        raceresult.Team = raceentry.Team;
        //        raceresult.TeamPosition = 1;
        //        raceresults.Add(raceresult);
        //        //build result object
        //    }
        //    //Only return the data when it is owned by the same Id
        //    //Get only records where the OwnerId is not the logged in User.
        //    return raceresults.ToList();
        //}

        ///// <summary>
        ///// Publish the results of a Race
        ///// </summary>
        ///// <remarks>
        ///// Publishes the results as Provisional or Final. Final will submit them to RunBritain/PO10
        ///// </remarks>
        ///// <returns>The JSON for the created Race</returns>
        //[HttpPost("{id}/publish", Name = "PublishResults")]
        //public IActionResult Publish([FromBody] Race item)
        //{
        //    if (item == null)
        //    {
        //        return BadRequest();
        //    }

        //    _context.Races.Add(item);

        //    //Set Owner ID
        //    item.OwnerId = User.GetUserId();

        //    _context.SaveChanges();

        //    return CreatedAtRoute("GetRace", new { id = item.Id }, item);
        //}

        /// <summary>
        /// Creates a Race
        /// </summary>
        /// <remarks>
        /// Creates a Race which can have entrants and timings assigned to it.
        /// </remarks>
        [HttpPost]
        public IActionResult Create([FromBody] RacePost item)
        {
            if (item == null)
            {
                return BadRequest();
            }

            if (item.Name == null)
            {
                return BadRequest("The Race must have a Name");
            }

            var raceitem = new Race
            {
                CurrentEntries = item.CurrentEntries,
                Description = item.Description,
                MaxEntries = item.MaxEntries,
                Name = item.Name,
                ContactName = item.ContactName,
                ContactEmail = item.ContactEmail,
                ContactNumber = item.ContactNumber,
                OwnerID = _userManager.GetUserId(User),
                RaceDate = item.RaceDate,
                RaceStartTime = item.RaceStartTime,
                IsCompleted = item.IsCompleted,
                IsPublished = item.IsPublished,
                IsOpenForEntries = item.IsOpenForEntries,
                LastUpdated = DateTime.Now
            };

            _context.Races.Add(raceitem);

            _context.SaveChanges();

            return CreatedAtRoute("GetRace", new { id = raceitem.ID }, raceitem);
        }

        /// <summary>
        /// Update a Race
        /// </summary>
        /// <remarks>
        /// Update's a Race's details
        /// </remarks>
        /// <param name="id">Race ID</param>
        /// <returns>The JSON for the updated Race</returns>
        [HttpPut("{id}")]
        public IActionResult Update(long id, [FromBody] Race item)
        {
            if (item == null)
            {
                return BadRequest();
            }

            if (item.Name == null)
            {
                return BadRequest("The Race must have a Name");
            }

            var race = _context.Races.Where(t => t.OwnerID == _userManager.GetUserId(User))
                                     .FirstOrDefault(t => t.ID == id);
            //var race = _context.Races.FirstOrDefault(t => t.ID == id);
            if (race == null)
            {
                return NotFound();
            }

            race.OwnerID = _userManager.GetUserId(User);
            race.Name = item.Name;
            race.ContactName = item.ContactName;
            race.ContactEmail = item.ContactEmail;
            race.ContactNumber = item.ContactNumber;
            race.RaceDate = item.RaceDate;
            race.RaceStartTime = item.RaceStartTime;
            race.Description = item.Description;
            race.MaxEntries = item.MaxEntries;
            race.CurrentEntries = item.CurrentEntries;
            race.IsCompleted = item.IsCompleted;
            race.IsPublished = item.IsPublished;
            race.IsOpenForEntries = item.IsOpenForEntries;
            race.LastUpdated = DateTime.Now;


            _context.Races.Update(race);
            _context.SaveChanges();
            return new NoContentResult();
        }


        /// <summary>
        /// Delete a Race
        /// </summary>
        /// <remarks>
        /// Deletes a Race. Note: This will orphan any related result data and is not recommended!
        /// </remarks>
        /// <param name="id">Race ID</param>
        /// <returns></returns>
        [HttpDelete("{id}")]
        public IActionResult Delete(long id)
        {
            var race = _context.Races.Where(p => p.OwnerID == _userManager.GetUserId(User)).FirstOrDefault(t => t.ID == id);
            //var race = _context.Races.FirstOrDefault(t => t.Id == id);

            if (race == null)
            {
                return NotFound();
            }

            var raceid = race.ID;

            ////Delete associated race entries
            //foreach (var raceentry in _context.RaceEntries.Where(p => p.OwnerId == User.GetUserId())
            //                                                .Where(p => p.RaceID == raceid))
            //{
            //    _context.RaceEntries.Remove(raceentry);
            //}

            ////Delete associated race timings
            //foreach (var timing in _context.Timings.Where(p => p.OwnerId == User.GetUserId())
            //                                                .Where(p => p.RaceId == raceid))
            //{
            //    _context.Timings.Remove(timing);
            //}

            //Delete/Save the deletion of the race
            _context.SaveChanges();
            return new NoContentResult();
        }
    }
}

最佳答案

我遇到了同样的问题,在任何授权请求中,我都被重定向到/account/login。我找到了添加方案以进行身份​​验证的解决方案。

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    [HttpPost]
    public async Task<IActionResult> Like([FromBody]int contentId)
    {
        var userId = await UserId();
        return Json(_content.IsLiked(contentId, true, userId));
    }

如果没有

,相同的代码将无法工作
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

关于c# - JWT 授权重定向到登录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46690685/

相关文章:

asp.net-core - asp.net core 中的 ContentDispositionHeaderValue.FileName 与 IFormFile.FileName

c# - 注册表中的 RUNASADMIN 似乎在 Windows 7 中不起作用

c# - 从 mvc Controller 设置一个 javascript 变量

c# - WebAPI 和 Angular JS Excel 文件下载 - 文件已损坏

c# - ASP.NET Core 2.0 类库中 Microsoft.AspNet.WebApi.Client 的替代方案

c# - 在 ASP.NET Core 中通过代码优先在 SQL 中使用 FileStream

c# - 使用 DotNetOpenAuth 保护的 WCF 4 Soap 和 Json 端点

c# - PowerShell 和 C# 在枚举集合时的区别

c# - ASP.NET 网络 API : How to create a persistent collection across requests?

asp.net-core - 使用库中的可配置路径映射 Controller 路由