ASP.NET Core Jwt implement signinmanager claims

2019-08-20 08:06发布

问题:

I have implemented Jwt as a way to authenticate my user. However, I am stuck on how I can do certain things on my application with regards to roles. Currently my Jwt Token contains the users email, phone , id and a list of roles that they have.

What I do with that token is like this:

[TypeFilter(typeof(ValidateRolesFilter), Arguments = new object[] {
        ApplicationGlobals.ApplicationSecretKey, RoleGlobals.SystemAdministrator
})]
public IActionResult Index()
{
    return View();
}

My Typefilter contains a rest request that sends the token to another application to verify if my user can access that Function. However, I am stuck when it comes to the view. I want to segment certain containers to be allowed to be viewed by certain users with certain roles.

I have an idea that if I were to add my users claims to the signinmanager just like a non jwt application, i would be able to get the claims from the httpcontext. However, I don't know if what I have can work with an application that uses jwt.

public async Task SignInUserAsync(TIdentityUser user, bool isPersistent, IEnumerable<Claim> customClaims)
{
    var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
    var identity = claimsPrincipal.Identity as ClaimsIdentity;
    var claims = (from c in claimsPrincipal.Claims select c).ToList();
    var savedClaims = claims;
    foreach (var item in claims)
    {
        identity.RemoveClaim(item);
    }
    if (customClaims != null)
    {
        identity.AddClaim(savedClaims[0]);
        identity.AddClaim(savedClaims[1]);
        identity.AddClaim(savedClaims[2]);
        identity.AddClaims(customClaims);
    }
    await _signInManager.Context.SignInAsync(IdentityConstants.ApplicationScheme,
        claimsPrincipal,
        new AuthenticationProperties { IsPersistent = isPersistent });
}

回答1:

JSON Web Tokens consist of three parts separated by dots (.), which are: Header,Payload,Signature .Therefore, a JWT typically looks like xxxxx.yyyyy.zzzzz .The second part of the token is the payload, which contains the claims.

You can decode the access token to get the claims which related to your roles :

How to decode JWT Token? .

Decoding and verifying JWT token using System.IdentityModel.Tokens.Jwt

If you are using Owin OpenID Connect middlerware to autheticate user from identity provider like Azure AD , Idenity server 4.... You can add additional claims to principal under OnTokenValidated event .

Edit :

You can also add the claims(decode and get the claims) to user context before sign- in :

 var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
 identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, loginData.Username));
 identity.AddClaim(new Claim(ClaimTypes.Name, loginData.Username));
 //add your custom claims 
 ....

 var principal = new ClaimsPrincipal(identity);
 await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = loginData.RememberMe });

Reference : http://future-shock.net/blog/post/creating-a-simple-login-in-asp.net-core-2-using-authentication-and-authorization-not-identity

Then you can access the claims in view like :

@foreach (var item in Context.User.Claims)
{
    <p>@item.Value</p> 
}; 


回答2:

I am recently doing a cooperative project on JWT. I wrote a middlware, when ever the user request to the api, It is checked by the Authentication middleware. I read the userRole from db and put it in the identity priciple I am sharing the middleware codes.

In here I read the JWT middle part to extract the user information

public class AuthenticationMiddleware
{
    private readonly RequestDelegate _next;

    // Dependency Injection
    public AuthenticationMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        string authHeader = context.Request.Headers["Authorization"];

        if (authHeader != null)
        {              
            int startPoint = authHeader.IndexOf(".") + 1;               
            int endPoint = authHeader.LastIndexOf(".");

            var tokenString = authHeader.Substring(startPoint, endPoint - startPoint).Split(".");
            var token = tokenString[0].ToString()+"==";

            var credentialString = Encoding.UTF8
                .GetString(Convert.FromBase64String(token));

            // Splitting the data from Jwt
            var credentials = credentialString.Split(new char[] { ':',',' });

            // Trim this string.
            var userRule = credentials[5].Replace("\"", ""); 
            var userName = credentials[3].Replace("\"", "");

             // Identity Principal
            var claims = new[]
            {
                new Claim("name", userName),
                new Claim(ClaimTypes.Role, userRule),

            };
            var identity = new ClaimsIdentity(claims, "basic");
            context.User = new ClaimsPrincipal(identity);
        }
        await _next(context);
    }


}

In startup.cs you need to call this middleware in the configure method

 app.UseMiddleware<AuthenticationMiddleware>();

In the controller

 [HttpGet("GetUsers")]
  [Authorize(Roles = "admin")]
    public ActionResult GetUsers()
    {
        var users = _authRepository.GetUsers();
        return Ok(users);
    }

if You need any help please give a comment. This implementation really worked for me. Check my repositories on the subject: https://github.com/hidayatarg/Asp.net-Core-2.1-Jwt-Authentication-Middleware https://github.com/hidayatarg/Decode-JWT-Token