ASP.NET Core 身份 [Authorize(Roles ="ADMIN")] 不起作用
Posted
技术标签:
【中文标题】ASP.NET Core 身份 [Authorize(Roles ="ADMIN")] 不起作用【英文标题】:ASP.NET Core Identity [Authorize(Roles ="ADMIN")] not work 【发布时间】:2021-01-11 16:00:48 【问题描述】:我正在使用 .NET 版本 5.0.100-rc.1.20452.10
、ASP.NET Core Web API、Microsoft SQL Server 2019、JWT 令牌。我有Startup.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using shadow.Data;
using shadow.Models;
using shadow.Services;
using System.IO;
using System.Text;
namespace shadow
public class Startup
public Startup(IConfiguration configuration)
Configuration = configuration;
public IConfiguration Configuration get;
public void ConfigureServices(IServiceCollection services)
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddHttpContextAccessor();
//services.AddCors();
services.AddCors(options =>
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
);
services.AddSingleton<IUriService>(o =>
var accessor = o.GetRequiredService<IHttpContextAccessor>();
var request = accessor.HttpContext.Request;
var uri = string.Concat(request.Scheme, "://", request.Host.ToUriComponent());
return new UriService(uri);
);
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
//options.Password.RequireDigit = true;
//options.Password.RequireLowercase = true;
options.Password.RequiredLength = 6;
).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
services
.AddAuthentication(auth =>
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
)
.AddJwtBearer(options =>
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = Configuration["AuthSettings:Audience"],
ValidIssuer = Configuration["AuthSettings:Issuer"],
RequireExpirationTime = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["AuthSettings:Key"])),
ValidateIssuerSigningKey = true
;
);
services.AddAuthorization(options =>
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
);
services.AddScoped<IUserService, UserService>();
services.AddTransient<IMailService, SendGridMailService>();
services.AddControllers();
services.AddRazorPages();
services.AddControllers(options => options.SuppressAsyncSuffixInActionNames = false);
services.AddMvc().AddJsonOptions(options =>
//options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.PropertyNamingPolicy = null;
);
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseHsts();
//app.UseHttpsRedirection();
app.UseRouting();
// app.UseCors(options => options.WithOrigins("http://localhost:4200")
//.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
// Server: https://shorten.news/static-file/content/10992.mp3
// Local: http://localhost:5000/static-file/content/10992.mp3
app.UseStaticFiles(new StaticFileOptions
FileProvider = new PhysicalFileProvider(
// Path.Combine(env.ContentRootPath, @"c:\audio\")),
// Path.Combine(@"c:\audio\")),
Path.Combine(@"D:\shadow_backend\Upload\files\")
),
RequestPath = "/static-file",
OnPrepareResponse = context =>
context.Context.Response.Headers["Access-Control-Allow-Origin"] = "*";
);
app.UseEndpoints(endpoints =>
endpoints.MapControllers();
);
app.Run(async (context) =>
await context.Response.WriteAsync("cound not find anything");
);
文件IUserService.cs
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using shadow.Models;
using shadow.Shared;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace shadow.Services
public interface IUserService
Task<UserManagerResponse> RegisterUserAsync(RegisterViewModel model);
Task<UserManagerResponse> LoginUserAsync(LoginViewModel model);
Task<UserManagerResponse> LogoutUserAsync(LoginViewModel model);
Task<UserManagerResponse> ConfirmEmailAsync(string userId, string token);
Task<UserManagerResponse> ForgetPasswordAsync(string email);
Task<UserManagerResponse> ResetPasswordAsync(ResetPasswordViewModel model);
Task<UserManagerResponse> ChangePasswordAsync(ChangePasswordViewModel model);
public class UserService : IUserService
private Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> _userManger;
private IConfiguration _configuration;
private IMailService _mailService;
public UserService(Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> userManager, IConfiguration configuration, IMailService mailService)
_userManger = userManager;
_configuration = configuration;
_mailService = mailService;
public async Task<UserManagerResponse> RegisterUserAsync(RegisterViewModel model)
if (model == null)
throw new NullReferenceException("Reigster Model is null");
if (model.Password != model.ConfirmPassword)
return new UserManagerResponse
Message = "Confirm password doesn't match the password",
IsSuccess = false,
;
var identityUser = new ApplicationUser
Email = model.Email,
UserName = model.Email,
About = model.About,
SecondMobile = model.SecondMobile,
Fullname = model.Fullname,
AliasName = model.AliasName,
Created = DateTime.Now,
Modified = DateTime.Now
;
var result = await _userManger.CreateAsync(identityUser, model.Password);
if (result.Succeeded)
var confirmEmailToken = await _userManger.GenerateEmailConfirmationTokenAsync(identityUser);
var encodedEmailToken = Encoding.UTF8.GetBytes(confirmEmailToken);
var validEmailToken = WebEncoders.Base64UrlEncode(encodedEmailToken);
string url = $"_configuration["AppUrl"]/api/auth/ConfirmEmail?userId=identityUser.Id&token=validEmailToken";
await _mailService.SendEmailAsync(identityUser.Email, "Confirm your email", $"<h1>Welcome to Trustee app</h1>" +
$"<p>Please confirm your email by <a href='url'>clicking here</a></p>");
return new UserManagerResponse
Message = "User created successfully!",
IsSuccess = true,
;
return new UserManagerResponse
Message = "User did not create",
IsSuccess = false,
Errors = result.Errors.Select(e => e.Description)
;
/// <summary>
/// Đăng nhập.
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public async Task<UserManagerResponse> LoginUserAsync(LoginViewModel model)
var user = await _userManger.FindByEmailAsync(model.Email);
if (user == null)
return new UserManagerResponse
Message = "There is no user with that Email address",
IsSuccess = false,
;
var result = await _userManger.CheckPasswordAsync(user, model.Password);
if (!result)
return new UserManagerResponse
Message = "Invalid password",
IsSuccess = false,
;
var claims = new[]
new Claim("Email", model.Email),
new Claim(ClaimTypes.NameIdentifier, user.Id),
;
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["AuthSettings:Key"]));
var token = new JwtSecurityToken(
issuer: _configuration["AuthSettings:Issuer"],
audience: _configuration["AuthSettings:Audience"],
claims: claims,
expires: DateTime.Now.AddDays(30),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
string tokenAsString = new JwtSecurityTokenHandler().WriteToken(token);
return new UserManagerResponse
Message = tokenAsString,
IsSuccess = true,
ExpireDate = token.ValidTo
;
// Đăng xuất.
public async Task<UserManagerResponse> LogoutUserAsync(LoginViewModel model)
var user = await _userManger.FindByEmailAsync(model.Email);
if (user == null)
return new UserManagerResponse
Message = "There is no user with that Email address",
IsSuccess = false,
;
var result = await _userManger.CheckPasswordAsync(user, model.Password);
if (!result)
return new UserManagerResponse
Message = "Invalid password",
IsSuccess = false,
;
var claims = new[]
new Claim("Email", model.Email),
new Claim(ClaimTypes.NameIdentifier, user.Id),
;
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["AuthSettings:Key"]));
var token = new JwtSecurityToken(
issuer: _configuration["AuthSettings:Issuer"],
audience: _configuration["AuthSettings:Audience"],
claims: claims,
expires: DateTime.Now.AddDays(30),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
string tokenAsString = new JwtSecurityTokenHandler().WriteToken(token);
return new UserManagerResponse
Message = tokenAsString,
IsSuccess = true,
ExpireDate = token.ValidTo
;
public async Task<UserManagerResponse> ConfirmEmailAsync(string userId, string token)
var user = await _userManger.FindByIdAsync(userId);
if (user == null)
return new UserManagerResponse IsSuccess = false, Message = "User not found" ;
var decodedToken = WebEncoders.Base64UrlDecode(token);
string normalToken = Encoding.UTF8.GetString(decodedToken);
var result = await _userManger.ConfirmEmailAsync(user, normalToken);
if (result.Succeeded)
return new UserManagerResponse Message = "Email confirmed successfully!", IsSuccess = true ;
return new UserManagerResponse
IsSuccess = false,
Message = "Email did not confirm",
Errors = result.Errors.Select(e => e.Description)
;
public async Task<UserManagerResponse> ForgetPasswordAsync(string email)
var user = await _userManger.FindByEmailAsync(email);
if (user == null)
return new UserManagerResponse IsSuccess = false, Message = "No user associated with email", ;
var token = await _userManger.GeneratePasswordResetTokenAsync(user);
var encodedToken = Encoding.UTF8.GetBytes(token);
var validToken = WebEncoders.Base64UrlEncode(encodedToken);
string url = $"_configuration["AppUrl"]/ResetPassword?email=email&token=validToken";
await _mailService.SendEmailAsync(email, "Reset Password", "<h1>Follow the instructions to reset your password</h1>" +
$"<p>To reset your password <a href='url'>Click here</a></p>");
return new UserManagerResponse
IsSuccess = true,
Message = "Reset password URL has been sent to the email successfully!"
;
public async Task<UserManagerResponse> ResetPasswordAsync(ResetPasswordViewModel model)
var user = await _userManger.FindByEmailAsync(model.Email);
if (user == null)
return new UserManagerResponse IsSuccess = false, Message = "No user associated with email", ;
if (model.NewPassword != model.ConfirmPassword)
return new UserManagerResponse IsSuccess = false, Message = "Password doesn't match its confirmation", ;
var decodedToken = WebEncoders.Base64UrlDecode(model.Token);
string normalToken = Encoding.UTF8.GetString(decodedToken);
var result = await _userManger.ResetPasswordAsync(user, normalToken, model.NewPassword);
if (result.Succeeded)
return new UserManagerResponse Message = "Password has been reset successfully!", IsSuccess = true ;
return new UserManagerResponse
Message = "Something went wrong",
IsSuccess = false,
Errors = result.Errors.Select(e => e.Description)
;
public async Task<UserManagerResponse> ChangePasswordAsync(ChangePasswordViewModel model)
var tokenString = model.Token;
var jwtEncodedString = tokenString.Substring(7); // trim 'Bearer ' from the start since its just a prefix for the token string
var token = new JwtSecurityToken(jwtEncodedString: jwtEncodedString);
string email = token.Claims.First(c => c.Type == "Email").Value;
Console.WriteLine("email => " + email);
var user = await _userManger.FindByEmailAsync(email);
if (user == null)
return new UserManagerResponse IsSuccess = false, Message = "No user associated with email", ;
if (model.NewPassword != model.ConfirmPassword)
return new UserManagerResponse IsSuccess = false, Message = "Password doesn't match its confirmation", ;
var token2 = await _userManger.GeneratePasswordResetTokenAsync(user);
var result = await _userManger.ResetPasswordAsync(user, token2, model.NewPassword);
if (result.Succeeded)
return new UserManagerResponse Message = "Password has been changed successfully!", IsSuccess = true ;
return new UserManagerResponse
Message = "Something went wrong",
IsSuccess = false,
Errors = result.Errors.Select(e => e.Description)
;
文件appsettings.json
"ConnectionStrings":
"DefaultConnection": "Server=.;Database=foo;User Id=sa; Password=SecrEt_STring;Trusted_Connection=False;MultipleActiveResultSets=True"
,
"Logging":
"LogLevel":
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
,
"AllowedHosts": "*",
"AuthSettings":
"Key": "This is the key that we will use in the encryption",
"Audience": "http://example.io",
"Issuer": "http://example.io"
,
"SendGridAPIKey": "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",
"AppUrl": "http://localhost:5002"
在控制器中
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using shadow.Data;
using shadow.DTO;
using shadow.Models;
using shadow.Services;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace shadow.Controllers
[Route("[controller]")]
[ApiController]
public class AssetItemController : ControllerBase
private IUserService _userService;
private IMailService _mailService;
private IConfiguration _configuration;
private ApplicationDbContext _db;
public AssetItemController(IUserService userService, IMailService mailService, IConfiguration configuration, ApplicationDbContext context /*, SignInManager<IdentityUser> signInManager */)
_userService = userService;
_mailService = mailService;
_configuration = configuration;
_db = context;
// _signInManager = signInManager;
// Admin liệt kê tất cả các tài sản.
[HttpGet]
[Route("all")]
[Authorize(Roles ="ADMIN")]
public async Task<ActionResult<AssetItem>> GetAllAssetItems()
var a = await Task.Run(() => _db.AssetItems);
if (a != null)
return Ok(a.ToList());
else
return NoContent();
// Liệt kê tất cả các tài sản theo user_id.
[HttpGet]
[Route("user/userId")]
public async Task<ActionResult<IEnumerable<AssetItem>>> GetAllAssetItemsByUserId(string userId)
var assetListByUserId = await Task.Run(() => _db.AssetItems.Where(x => x.UserId == userId).ToList());
if (assetListByUserId != null)
return Ok(assetListByUserId);
else
return NoContent();
// Xem chi tiet asset_item theo id cua no.
[HttpGet]
[Route("assetItemId")]
public async Task<ActionResult<IEnumerable<AssetItem>>> ViewAssetItemById(int assetItemId)
var assetListByUserId = await Task.Run(() => _db.AssetItems.Where(x => x.Id == assetItemId).FirstOrDefault());
if (assetListByUserId != null)
return Ok(assetListByUserId);
else
return NoContent();
// Xóa tài sản.
[HttpDelete("id")]
public async Task<ActionResult<string>> DeleteAssetItem(int id)
var item = _db.AssetItems.Find(id);
if (item != null)
_db.AssetItems.Remove(item);
await _db.SaveChangesAsync();
return Ok("ok");
else
return NotFound();
// POST http://localhost:5002/AssetItem
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
[Authorize]
public async Task<ActionResult<AssetItem>> Add([FromForm] AssetItemDTO input)
if (ModelState.IsValid)
AssetItem assetItem = new AssetItem();
if (input.AssetTypeId != null)
assetItem.AssetTypeId = input.AssetTypeId;
if (input.UserId != null)
assetItem.UserId = input.UserId;
if (input.TrustedPersonId != null)
assetItem.TrustedPersonId = input.TrustedPersonId;
if (input.TrustedContent != null)
assetItem.TrustedContent = input.TrustedContent;
if (input.Description != null)
assetItem.Description = input.Description;
assetItem.Created = DateTime.Now;
if (input.file != null)
if (IsImageFile(input.file))
string imgPath = await WriteFile(input.file);
assetItem.ImagePath = imgPath;
else
return BadRequest(new message = "Invalid file extension" );
_db.AssetItems.Add(assetItem);
await _db.SaveChangesAsync();
return Ok(assetItem);
else
return BadRequest(ModelState);
// Edit 1 tài sản.
// PUT http://localhost:5002/AssetItem
[HttpPut("id")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
[Authorize]
public async Task<ActionResult<AssetItem>> Edit([FromForm] AssetItemDTO input, int id)
if (ModelState.IsValid)
var foo = _db.AssetItems.Find(id);
if (!String.IsNullOrEmpty(input.TrustedContent))
foo.TrustedContent = input.TrustedContent;
else
foo.TrustedContent = null;
if (input.file != null)
string imgPath = await WriteFile(input.file);
foo.ImagePath = imgPath;
else
foo.ImagePath = null;
if (!String.IsNullOrEmpty(input.Description))
foo.Description = input.Description;
else
foo.Description = null;
foo.AssetTypeId = input.AssetTypeId;
if (input.TrustedPersonId != null)
foo.TrustedPersonId = input.TrustedPersonId;
// Hiện chưa ghi nhận user_modified. Cần xem lại Requirement.
if (!String.IsNullOrEmpty(input.UserModified))
foo.UserModified = input.UserModified;
else
foo.UserModified = null;
foo.Modified = DateTime.Now;
_db.Update(foo);
await _db.SaveChangesAsync();
return Ok(foo);
else
return BadRequest(ModelState);
private bool IsImageFile(IFormFile file)
try
var extension = "." + file.FileName.Split('.')[file.FileName.Split('.').Length - 1];
return (extension == ".png" || extension == ".jpg" || extension == ".bmp" || extension == ".gif" || extension == ".tif");
catch
return false;
private async Task<string> WriteFile(IFormFile file)
string fileName = "";
try
var extension = "." + file.FileName.Split('.')[file.FileName.Split('.').Length - 1];
fileName = DateTime.Now.Ticks + extension; //Create a new Name for the file due to security reasons.
var pathBuilt = Path.Combine(Directory.GetCurrentDirectory(), "Upload\\file");
if (!Directory.Exists(pathBuilt))
Directory.CreateDirectory(pathBuilt);
var path = Path.Combine(Directory.GetCurrentDirectory(), "Upload\\files", fileName);
using (var stream = new FileStream(path, FileMode.Create))
await file.CopyToAsync(stream);
return fileName;
catch (Exception e)
//
return fileName;
数据库
select * from AspNetUsers;
select * from AspNetUserRoles;
select * from AspNetRoles;
结果
添加[Authorize(Roles ="ADMIN")]
时,显示403 Forbidden
。
如何使按角色授权工作?
【问题讨论】:
能否提供生成 JWT 的方法的代码?另外,请查看 this link 以查看您的令牌是否确实包含角色信息。 我在问题中添加了更多内容。 请看我的回答。 我离职了,我会在周一反馈。 周末愉快! 【参考方案1】:您实际上并未向令牌添加角色。每个角色都应该是一个声明,就像这样。
new Claim(ClaimTypes.Role, "<role name">));
例如。
var claims = new[]
new Claim("Email", model.Email),
new Claim(ClaimTypes.NameIdentifier, user.Id),
;
var roles = await _userManger.GetRolesAsync(user); // note: you have a typo in "_userManger"
var claimsWithRoles = roles.Select(role => new Claim(ClaimTypes.Role, role));
var allClaims = claims.Concat(claimsWithRoles);
然后将 allClaims
添加到 JWT,如下所示:
var token = new JwtSecurityToken(
issuer: _configuration["AuthSettings:Issuer"],
audience: _configuration["AuthSettings:Audience"],
claims: allClaims , // note how we add all claims, including the ones for roles, here
expires: DateTime.Now.AddDays(30),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
让我知道它是如何工作的。
编辑:抱歉,忘记AddRange
返回无效;
【讨论】:
我发现错误user-images.githubusercontent.com/1328316/… 已修复。请再检查一次。 试试claims.AddRange(roles.Select(role => new Claim(ClaimsIdentity.DefaultRoleClaimType, role)));
我发现错误user-images.githubusercontent.com/1328316/…
@DoNhuVy 我认为这应该不是问题。您需要使用实际的 256 位密钥来验证签名,而不是预设的 your-256-bit-secret
值。在调试器中通常不需要这样做,但您可以粘贴您的密钥并对其进行测试。但是,您的令牌现在包含适当的角色,因此您应该能够使用端点。快速说明:可能希望从主要问题的appsettings.json
代码中删除 Sendgrid API 密钥。以上是关于ASP.NET Core 身份 [Authorize(Roles ="ADMIN")] 不起作用的主要内容,如果未能解决你的问题,请参考以下文章
选择 webApi 模板时如何将 ASP.Net 身份添加到 Asp.Net Core?
无法更改 Asp 身份表名称.....Asp .Net Core 2?