我有一个MVC应用程序我正在添加API控制器。
我通过Cookie和不记名令牌(JWT)进行身份验证和授权。对于MVC控制器,它使用cookie。对于API控制器,一个令牌。
我正在尝试将数据链接在一起,这些数据都是基于用户ID键入的。
MVC App的实现
var userid = _userManager.GetUserId(User);
返回ASPNetUsers.Id值。因此,在使用应用程序时,所有数据都会被GUID写为OwnerID。
现在在我的API控制器上,我有完全相同的代码行。
var userid = _userManager.GetUserId(User);
但是,这是返回用户名而不是用户ID。
我需要它返回相同的值,即ASPNetUsers.Id上的guid。
有什么想法吗?你可以在下面看到我一直在尝试的一些事情。
控制器
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 UserManager<ApplicationUser> _userManager;
private readonly IAuthorizationService _authorizationService;
private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(User);
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()
{
var test = User.Identity.IsAuthenticated;
var userid = _userManager.GetUserId(User);
var user = GetCurrentUserAsync();
//Get only records where the OwnerId is not the logged in User.
//return _context.Races.Where(p => p.OwnerID == User.GetUserId()).ToList();
return _context.Races.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 == User.GetUserId()).FirstOrDefault(t => t.ID == id);
var item = _context.Races.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
//return _context.RaceEntries.Where(p => p.OwnerID == User.GetUserId())
.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 = User.GetUserId(),
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 == User.GetUserId())
var race = _context.Races
.FirstOrDefault(t => t.ID == id);
//var race = _context.Races.FirstOrDefault(t => t.ID == id);
if (race == null)
{
return NotFound();
}
//race.OwnerID = User.GetUserId();
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 == User.GetUserId()).FirstOrDefault(t => t.ID == id);
var race = _context.Races.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();
}
}
}
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.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.Configure<JWTSettings>(Configuration.GetSection("JWTSettings"));
services.AddAuthentication()
.AddCookie()
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.IncludeErrorDetails = true;
var secretKey = Configuration.GetSection("JWTSettings:SecretKey").Value;
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = Configuration.GetSection("JWTSettings:Issuer").Value,
ValidateAudience = true,
ValidAudience = Configuration.GetSection("JWTSettings:Audience").Value,
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
};
});
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");
});
}
}
}
编辑:这是生成令牌的TokenController
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.ASCII.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 new JsonResult(new Dictionary<string, object>
{
{ "access_token", new JwtSecurityTokenHandler().WriteToken(token) },
});
}
}
}
return BadRequest("Could not create token");
}
}
}
答案 0 :(得分:0)
感谢Kirk,在他提出问题之后我能够很快看到修复程序,然后我查看了我的令牌生成器控制器。
在我的令牌中,我正在生成声明
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
将其更改为
new Claim(JwtRegisteredClaimNames.Sub, user.Id),
解决了它。