I am working on ASP.NET Core Web API. I am trying to create a custom Authorize
attribute but I am stuck. I could not understand what I am missing. I have the following code for the Authorize
attribute and filter:
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute(params string[] claim) : base(typeof(AuthorizeFilter))
{
Arguments = new object[] { claim };
}
}
public class AuthorizeFilter : IAuthorizationFilter
{
readonly string[] _claim;
public AuthorizeFilter(params string[] claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
var claimsIndentity = context.HttpContext.User.Identity as ClaimsIdentity;
if (IsAuthenticated)
{
bool flagClaim = false;
foreach (var item in _claim)
{
if (context.HttpContext.User.HasClaim("Role", item))
flagClaim = true;
}
if (!flagClaim)
{
//if (context.HttpContext.Request.IsAjaxRequest())
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; //Set HTTP 403
//else
// context.Result = new RedirectResult("~/Login/Index");
}
}
else
{
//if (context.HttpContext.Request.IsAjaxRequest())
//{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; //Set HTTP 401 -
//}
//else
//{
// context.Result = new RedirectResult("~/Login/Index");
//}
}
return;
}
}
I have copied this code from somewhere and commented unnecessary lines.
Here is my controller class where I am trying to put this:
[Route("api/[controller]/[action]")]
[ApiController]
[Authorize]
public class JobController : ControllerBase
{
// GET: api/<JobController>
[HttpGet]
[ActionName("GetAll")]
public List<Job> Get()
{
return JobDataLog.GetAllJobQueue();
}
// GET api/<JobController>/5
[HttpGet("{ID}")]
[ActionName("GetByID")]
public Job Get(Guid ID)
{
return JobDataLog.GetJob(ID);
}
// GET api/<JobController>/5
[HttpGet]
[ActionName("GetCount")]
public int GetCount()
{
return JobDataLog.GetJobTotal();
}
}
Also the Configure and ConfigureService methods of Startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(60);
});
var tokenKey = Configuration.GetValue<string>("TokenKey");
var key = Encoding.ASCII.GetBytes(tokenKey);
services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; })
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddSingleton<IJWTAuthenticationManager>(new JWTAuthenticationManager(tokenKey));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCookiePolicy();
app.UseSession();
app.Use(async (context, next) =>
{
var JWToken = context.Session.GetString("JWToken");
if (!string.IsNullOrEmpty(JWToken))
{
context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
}
await next();
});
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
The problem is that even this controller has the Authorize
attribute, all the actions are being called even the Authorize
filter invalidates the authorization.
Also when I placed the following code in the OnAuthorization
method:
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
It blocked the access of all actions, including those which have an AllowAnnoynmous
attribute.
Please help me, I have been stuck on this for last 3 hours.
If you really want to use a custom AuthorizeAttribute
, here you go, this works. :)
You'll have a few squiggly lines, but VS will be able to automatically add the using
statements.
The original code had multiple problems:
Reponse.StatusCode
doesn't actually lead to a response being returned.HttpContext.User
wouldn't be populated in the first place, because ASP.NET Core only attempts to authenticate the user and populate the user's claims/identity if an endpoint is secured with the built-in AuthorizeAttribute
. The following code solves this by deriving from AuthorizeAttribute
.TypeFilterAttribute
and AuthorizeAttribute
, and the claims list would be always empty.public class MyAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
readonly string[] _requiredClaims;
public MyAuthorizeAttribute(params string[] claims)
{
_requiredClaims = claims;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var isAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
if (!isAuthenticated)
{
context.Result = new UnauthorizedResult();
return;
}
var hasAllRequredClaims = _requiredClaims.All(claim => context.HttpContext.User.HasClaim(x => x.Type == claim));
if (!hasAllRequredClaims)
{
context.Result = new ForbidResult();
return;
}
}
}
The reason why this works in such a crappy way is that the ASP.NET Core team doesn't want you to write custom Authorize Attributes . See this answer on the subject . The 'proper' way is to create policies, and assign your claim requirements to those policies. But I also think it's silly that authorization is so inflexible and lacking support for basic scenarios.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.