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.Jwt、Microsoft.AspNet.WebApi.Cors和 Microsoft.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);
});
完成,应该工作正常。
我使用 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.Jwt、Microsoft.AspNet.WebApi.Cors和 Microsoft.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);
});
完成,应该工作正常。