基于角色的身份验证不适用于使用 JWT 身份验证开发的基于 Cookie 的身份验证

时间:2020-12-21 03:11:17

标签: c# asp.net .net asp.net-core

我有一个 JWT 令牌从 API 返回到我的客户端应用程序,这是我的 JWT 代码:

var claims = new List<Claim>
                             {
                                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                                    new Claim(JwtRegisteredClaimNames.Sub, user.Email.ToString()),
                                    new Claim(ApplicationConstants.UserId, applicationUser.Id.ToString()),
                             };

                if (applicationUser.FirstName != null)
                {
                    claims.Add(new Claim(ApplicationConstants.FirstName, applicationUser.FirstName));
                }
                if (applicationUser.NormalizedUserName != null)
                {
                    claims.Add(new Claim(ApplicationConstants.UserName, applicationUser.NormalizedUserName));
                }

                //only if the user is vendor
                if (vendorUser != null) {
                    claims.Add(new Claim(RoleNameConstants.Vendor, vendorUser.Id.ToString()));
                    claims.Add(new Claim(ApplicationConstants.VendorId, vendorUser.VendorId.ToString()));
                }

                var claimsIdentity = new ClaimsIdentity(claims);

                //Adding UserClaims to JWT claims
                foreach (var item in userRoles)
                {
                    //claimsIdentity.AddClaim(new Claim(ApplicationConstants.Roles, item));
                    claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, item));
                }

                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(_jwtSettings.Secret);

                #region code smell - don't look
                //var claims = new List<Claim>
                //{
                //    new Claim(JwtRegisteredClaimNames.Sub, user.Email),
                //    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                //    new Claim(JwtRegisteredClaimNames.Email, user.Email),
                //    new Claim("id", user.Id)
                //};

                //var userClaims = await _userManager.GetClaimsAsync(user);
                //claims.AddRange(userClaims);

                //var userRoles = await _userManager.GetRolesAsync(user);

                //foreach (var userRole in userRoles)
                //{
                //    claims.Add(new Claim(ClaimTypes.Role, userRole));

                //    var role = await _roleManager.FindByNameAsync(userRole);
                //    if (role == null) continue;
                //    var roleClaims = await _roleManager.GetClaimsAsync(role);

                //    foreach (var roleClaim in roleClaims)
                //    {
                //        if (claims.Contains(roleClaim))
                //            continue;

                //        claims.Add(roleClaim);
                //    }
                //}
                #endregion

                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(claimsIdentity),
                    //Expires = DateTime.UtcNow.Add(_jwtSettings.TokenLifetime),
                    Expires = DateTime.UtcNow.AddDays(30),
                    SigningCredentials =
                    new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                };

                var token = tokenHandler.CreateToken(tokenDescriptor);

我正在使用它在 ASP.NET Identity 中构建基于 cookie 的身份验证,如下所示:

var stream = response.Token;
            var handler = new JwtSecurityTokenHandler();
            var jsonToken = handler.ReadToken(stream);
            var token = handler.ReadToken(stream) as JwtSecurityToken;

            //my code
            var claims = new List<Claim>();

            foreach (var tokenClaim in token.Claims)
            {
                var claim = new Claim(tokenClaim.Type, tokenClaim.Value);
                claims.Add(claim);
            }

            var identity = new ClaimsIdentity(
                claims, CookieAuthenticationDefaults.AuthenticationScheme, ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);

            //var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme, nameType, roleType);

            var principal = new ClaimsPrincipal(identity);

            var authProperties = new AuthenticationProperties
            {
                AllowRefresh = true,
                IsPersistent = userRequest == null ? false : userRequest.RememberMe,
                ExpiresUtc = token.ValidTo,
                IssuedUtc = DateTime.UtcNow
            };

            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                principal,
                authProperties);

            _httpContextAccessor.AddCookie("token", response.Token, token.ValidTo);  
            //_sessionManager.SetString("token", response.Token);

            HttpContext.User = principal;
            _httpContextAccessor.HttpContext.User = principal;

现在此代码可以正确地对用户进行身份验证,但是当我使用 [Authorize(Roles = "Vendor,VendorAdmin")] 属性时,这对于角色授权无法正常工作,即使用户拥有这些角色,它也不会让用户通过。

这是我的启动代码:

 services.AddAuthentication(x =>
            {

                x.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                x.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            }/*CookieAuthenticationDefaults.AuthenticationScheme*/) // Sets the default scheme    to cookies
           .AddCookie(options =>
           {
               options.LoginPath = "/account/login";
               options.AccessDeniedPath = "/home/error";
           });

1 个答案:

答案 0 :(得分:1)

我测试了你的场景。从 API 获取的声明中角色的键是字符串 role

enter image description here

但是 Roles 中的 [Authorize(Roles = "Vendor,VendorAdmin")] 需要密钥 http://schemas.microsoft.com/ws/2008/06/identity/claims/role

然后在基于 cookie 的身份验证项目中重新添加角色。

        foreach (var tokenClaim in token.Claims)
        {
            if (tokenClaim.Type == "role")
            {
                claims.Add(new Claim(ClaimTypes.Role, tokenClaim.Value));
                continue;
            }
            var claim = new Claim(tokenClaim.Type, tokenClaim.Value);
            claims.Add(claim);
        }

enter image description here

然后就可以访问授权的方法了。

    [Authorize(Roles = "Vendor")]
    public IActionResult Resource()
    {
        return Ok();
    }