ReactJS + .NET Framework 4.7.2 - 如何在 WebAPI 中使用 AuthorizeAttribute

ReactJS + .NET Framework 4.7.2 - How to use AuthorizeAttribute in WebAPI

我使用 JSON Web 令牌 (JWT) 创建了带有登录机制的简单 Web 应用程序。我在我的 Web API 中生成 TOKEN,然后使用 Redux 存储来存储它,所以我可以在任何地方访问它。看起来像这样,我正在使用 .subscribe() 来跟踪我的 loginStore:

中的任何更改
  const loginStore = createStore(loginUserReducer);
  //After I log in, I put TOKEN to storage and reload my app
  loginStore.subscribe(() => {
    loginStore.getState().then(x => {
      if(x) { 
        localStorage.setItem('TOKEN', x);
      } else {
        localStorage.clear();
      }
      window.location.reload();
    });
  });

我在 .Net Framework 4.7.2 中创建了我的 Web API。接下来,我想在我的 HomeController 中使用 AuthorizeAttribute 进行授权,以保护我的 API 免受任何不需要的电话:

public class HomeController : ApiController
{
    List<string> myList = new List<string>
    {
        "Element1",
        "Element2",
        "Element3"
    };

    [Authorize]//I use this for authorization -> using System.Web.Http;
    [HttpGet]
    [Route("api/mylist")]
    public List<string> MyList()
    {
        return this.myList;
    }
}

最后,我使用fetch()方法,尝试从API获取myList的数据,并显示在console.log()在前面。

  fetch('https://localhost:XXXXX/api/mylist', {
    method: 'GET',
    headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
    },
  }).then(x => {
    console.log(x.json());//Outcome from API
  }).catch(err => {
      console.log(err);
  });

我得到的是一条错误消息:

"Authorization has been denied for this request."

问题:如何将我的 token/user data/whatever 从 React 传递到 Web API,所以 AuthorizeAttribute 将允许它和我想要的 return 数据?

任何解决方案将不胜感激。

我现在没有使用任何用户角色,但是带有自定义属性和角色验证的解决方案会更好:)

好的!我想通了,实际上真的很容易!我会添加一个长答案以防有人需要它。

步骤 1:使用 System.IdentityModel.Tokens.JwtMicrosoft.AspNet.WebApi.CorsMicrosoft.AspNet.Cors 用于 TOKEN 代。

步骤 2:实施 static class TokenManager,您将 generate/validate TOKEN 并将您的用户角色放在那里。 (Implementation source)

public static class TokenManager
{
    private static string Secret = "my_secret_key";
    //Use
    private static JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();

    public static string GenerateToken(string username)
    {
        byte[] key = Convert.FromBase64String(Secret);
        var descriptor = GenerateTokenDescriptor(username, key);

        JwtSecurityToken token = handler.CreateJwtSecurityToken(descriptor);
        return handler.WriteToken(token);
    }

    private static SecurityTokenDescriptor GenerateTokenDescriptor(string username, byte[] key)
    {
        SymmetricSecurityKey securityKey = new SymmetricSecurityKey(key);

        SecurityTokenDescriptor descriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[] {
                new Claim(ClaimTypes.Name, username),
                new Claim(ClaimTypes.Role, "Role1"),//<-- User role!
                new Claim(ClaimTypes.Role, "Role2")}),//<-- User role!
            Expires = DateTime.UtcNow.AddMinutes(30),//Token takes only UTC time
            SigningCredentials = new SigningCredentials(securityKey,
            SecurityAlgorithms.HmacSha256Signature)
        };

        return descriptor;
    }

    public static ClaimsPrincipal GetPrincipal(string token)
    {
        try
        {
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
            JwtSecurityToken jwtToken = (JwtSecurityToken)tokenHandler.ReadToken(token);
            if (jwtToken == null)
                return null;
            byte[] key = Convert.FromBase64String(Secret);
            TokenValidationParameters parameters = new TokenValidationParameters()
            {
                RequireExpirationTime = true,
                ValidateIssuer = false,
                ValidateAudience = false,
                IssuerSigningKey = new SymmetricSecurityKey(key)
            };
            SecurityToken securityToken;
            ClaimsPrincipal principal = tokenHandler.ValidateToken(token,
                  parameters, out securityToken);
            return principal;
        }
        catch (Exception e)
        {
            return null;
        }
    }
}

注意:我使用以下代码生成了 my_secret_key

HMACSHA256 hmac = new HMACSHA256();
string key = Convert.ToBase64String(hmac.Key);

步骤 3:创建将用于授权的自定义属性

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    private readonly string[] allowedroles;
    public MyAuthorizeAttribute(params string[] roles)
    {
        this.allowedroles = roles;
    }
    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        //Default outcome 
        bool authorize = false;
        //Get TOKEN
        var authToken = actionContext.Request.Headers.Authorization?.Parameter;
        //Check if TOKEN has parameters
        if (authToken != null)
        {
            //Get roles from TOKEN
            List<string> userRoles = TokenManager.GetPrincipal(authToken).FindAll(ClaimTypes.Role).Select(x => x.Value).ToList();
            //Check if any of User Roles is allowed
            authorize = this.allowedroles.Any(x => userRoles.Any(y => y == x));
        }
        //return outcome
        return authorize;
    }
}

步骤 4:在 Controller

中使用您的属性
public class HomeController : ApiController
{
    List<string> myList = new List<string>
    {
        "Element1",
        "Element2",
        "Element3"
    };

    [MyAuthorizeAttribute("Role2")]//Add roles names in parameter
    [HttpGet]
    [Route("api/mylist")]
    public List<string> MyList()
    {
        return this.myList;
    }

    [HttpPost]
    [Route("api/login")]
    public HttpResponseMessage Login()
    {
        var myToken = TokenManager.GenerateToken("username");
        return Request.CreateResponse(HttpStatusCode.OK, myToken);
    }
}

步骤 5:将 TOKEN 保存在 LOCAL STORAGE:

fetch('https://localhost:XXXXX/api/login', {
        method: 'post',
        headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
        },
      }).then(x => {
          x.json().then(y => {
            localStorage.setItem('TOKEN', y);
            });
      }).catch(err => {
          console.log(err);
      });

第 6 步:最后,在您的 header 中传递您的 TOKEN要求:

  fetch('https://localhost:XXXXX/api/mylist', {
    method: 'GET',
    headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + localStorage.getItem('TOKEN'),
        'Access-Control-Allow-Origin': '*'
    },
  }).then(x => {
    console.log(x.json());//Outcome from API
  }).catch(err => {
      console.log(err);
  });

完成,应该工作正常。