使用 URL 参数授权操作
Authorize an Action using URL parameters
我有一个 MVC 5 应用程序,它的控制器带有 [Authorize]
属性。但是,要求指出客户端可能无法登录到 Web 应用程序(因为此 Web 应用程序将部署在标牌播放器上,而不是 Web 浏览器上,该播放器接受 URLs 作为输入以访问网络应用程序)。
作为回应,我正在考虑使用客户端可以使用的 URL 来授权他们的操作,而不是登录...格式为:
http://{website}/Action?token={/* randomly generated key for this user */}
如何在不更改带有 [Authorize]
属性的当前代码的情况下实现它?
我确实稍微更改了我的代码。我没有用 [Authorize]
装饰我的控制器,而是创建了一个名为 [TokenAuthorize]
的 。
我创建了一个 SecurityManager
class 来生成和验证令牌。这改编自 Primary Objects, although I chose not to use any of their client-side code or hash using ip
or userAgent
. Also, I created a TokenIdentity
class to create an Identity
for authorizing an action. This was adapted from Steve's Coding Blog. Jelle's link 也有助于使用 GenericPrincipal
通过令牌授权用户:HttpContext.Current.User = new GenericPrincipal(new TokenIdentity(username), roles.ToArray());
执行此操作后,我能够使用名为 token
的 url 参数授权操作:http://{website}/Action?token={/* randomly generated key for this user */}
public class TokenAuthorize : AuthorizeAttribute
{
private const string SecurityToken = "token";
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Authorize(filterContext))
{
return;
}
HandleUnauthorizedRequest(filterContext);
}
private bool Authorize(AuthorizationContext actionContext)
{
try
{
HttpContextBase context = actionContext.RequestContext.HttpContext;
string token = context.Request.Params[SecurityToken];
// check if the token is valid. if so, authorize action.
bool isTokenAuthorized = SecurityManager.IsTokenValid(token);
if (isTokenAuthorized) return true;
// if the token is not valid, check if the user is authorized by default.
bool isDefaultAuthorized = AuthorizeCore(context);
return isDefaultAuthorized;
}
catch (Exception)
{
return false;
}
}
}
SecurityManager.cs (adapted from Primary Objects)
public class SecurityManager
{
private const string Alg = "HmacSHA256";
private const string Salt = "rz8LuOtFBXphj9WQfvFh";
// Generates a token to be used in API calls.
// The token is generated by hashing a message with a key, using HMAC SHA256.
// The message is: username
// The key is: password:salt
public static string GenerateToken(string username, string password)
{
string hash = string.Join(":", new string[] { username });
string hashLeft;
string hashRight;
using (HMAC hmac = HMAC.Create(Alg))
{
hmac.Key = Encoding.UTF8.GetBytes(GetHashedPassword(password));
hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
hashLeft = Convert.ToBase64String(hmac.Hash);
hashRight = string.Join(":", username);
}
return Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(":", hashLeft, hashRight)));
}
// used in generating a token
private static string GetHashedPassword(string password)
{
string key = string.Join(":", new string[] { password, Salt });
using (HMAC hmac = HMAC.Create(Alg))
{
// Hash the key.
hmac.Key = Encoding.UTF8.GetBytes(Salt);
hmac.ComputeHash(Encoding.UTF8.GetBytes(key));
return Convert.ToBase64String(hmac.Hash);
}
}
// Checks if a token is valid.
public static bool IsTokenValid(string token)
{
var context = new ApplicationDbContext();
try
{
// Base64 decode the string, obtaining the token:username.
string key = Encoding.UTF8.GetString(Convert.FromBase64String(token));
// Split the parts.
string[] parts = key.Split(':');
if (parts.Length != 2) return false;
// Get the username.
string username = parts[1];
// Get the token for said user
// var computedToken = ...
// Compare the computed token with the one supplied and ensure they match.
var result = (token == computedToken);
if (!result) return false;
// get roles for user (ASP.NET Identity 2.0)
var user = context.Users.Single(u => u.UserName == username);
var rolesPerUser = context.UserRoles.Where(x => x.UserId == user.Id).ToList();
var roles = rolesPerUser.Select(role => context.Roles.Single(r => r.Id == role.RoleId).Name);
// NOTE: define public DbSet<IdentityUserRole> UserRoles { get; set; } ...
// ... in your DbContext in IdentityModels.cs
HttpContext.Current.User = new GenericPrincipal(new TokenIdentity(username), roles.ToArray());
return true;
}
catch
{
return false;
}
}
}
TokenIdentity.cs (adapted from Steve's Coding Blog)
public class TokenIdentity : IIdentity
{
public string User { get; private set; }
public TokenIdentity(string user)
{
this.User = user;
}
public string Name => User;
public string AuthenticationType => "ApplicationToken";
public bool IsAuthenticated => true;
}
我有一个 MVC 5 应用程序,它的控制器带有 [Authorize]
属性。但是,要求指出客户端可能无法登录到 Web 应用程序(因为此 Web 应用程序将部署在标牌播放器上,而不是 Web 浏览器上,该播放器接受 URLs 作为输入以访问网络应用程序)。
作为回应,我正在考虑使用客户端可以使用的 URL 来授权他们的操作,而不是登录...格式为:
http://{website}/Action?token={/* randomly generated key for this user */}
如何在不更改带有 [Authorize]
属性的当前代码的情况下实现它?
我确实稍微更改了我的代码。我没有用 [Authorize]
装饰我的控制器,而是创建了一个名为 [TokenAuthorize]
的
我创建了一个 SecurityManager
class 来生成和验证令牌。这改编自 Primary Objects, although I chose not to use any of their client-side code or hash using ip
or userAgent
. Also, I created a TokenIdentity
class to create an Identity
for authorizing an action. This was adapted from Steve's Coding Blog. Jelle's link 也有助于使用 GenericPrincipal
通过令牌授权用户:HttpContext.Current.User = new GenericPrincipal(new TokenIdentity(username), roles.ToArray());
执行此操作后,我能够使用名为 token
的 url 参数授权操作:http://{website}/Action?token={/* randomly generated key for this user */}
public class TokenAuthorize : AuthorizeAttribute
{
private const string SecurityToken = "token";
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Authorize(filterContext))
{
return;
}
HandleUnauthorizedRequest(filterContext);
}
private bool Authorize(AuthorizationContext actionContext)
{
try
{
HttpContextBase context = actionContext.RequestContext.HttpContext;
string token = context.Request.Params[SecurityToken];
// check if the token is valid. if so, authorize action.
bool isTokenAuthorized = SecurityManager.IsTokenValid(token);
if (isTokenAuthorized) return true;
// if the token is not valid, check if the user is authorized by default.
bool isDefaultAuthorized = AuthorizeCore(context);
return isDefaultAuthorized;
}
catch (Exception)
{
return false;
}
}
}
SecurityManager.cs (adapted from Primary Objects)
public class SecurityManager
{
private const string Alg = "HmacSHA256";
private const string Salt = "rz8LuOtFBXphj9WQfvFh";
// Generates a token to be used in API calls.
// The token is generated by hashing a message with a key, using HMAC SHA256.
// The message is: username
// The key is: password:salt
public static string GenerateToken(string username, string password)
{
string hash = string.Join(":", new string[] { username });
string hashLeft;
string hashRight;
using (HMAC hmac = HMAC.Create(Alg))
{
hmac.Key = Encoding.UTF8.GetBytes(GetHashedPassword(password));
hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
hashLeft = Convert.ToBase64String(hmac.Hash);
hashRight = string.Join(":", username);
}
return Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(":", hashLeft, hashRight)));
}
// used in generating a token
private static string GetHashedPassword(string password)
{
string key = string.Join(":", new string[] { password, Salt });
using (HMAC hmac = HMAC.Create(Alg))
{
// Hash the key.
hmac.Key = Encoding.UTF8.GetBytes(Salt);
hmac.ComputeHash(Encoding.UTF8.GetBytes(key));
return Convert.ToBase64String(hmac.Hash);
}
}
// Checks if a token is valid.
public static bool IsTokenValid(string token)
{
var context = new ApplicationDbContext();
try
{
// Base64 decode the string, obtaining the token:username.
string key = Encoding.UTF8.GetString(Convert.FromBase64String(token));
// Split the parts.
string[] parts = key.Split(':');
if (parts.Length != 2) return false;
// Get the username.
string username = parts[1];
// Get the token for said user
// var computedToken = ...
// Compare the computed token with the one supplied and ensure they match.
var result = (token == computedToken);
if (!result) return false;
// get roles for user (ASP.NET Identity 2.0)
var user = context.Users.Single(u => u.UserName == username);
var rolesPerUser = context.UserRoles.Where(x => x.UserId == user.Id).ToList();
var roles = rolesPerUser.Select(role => context.Roles.Single(r => r.Id == role.RoleId).Name);
// NOTE: define public DbSet<IdentityUserRole> UserRoles { get; set; } ...
// ... in your DbContext in IdentityModels.cs
HttpContext.Current.User = new GenericPrincipal(new TokenIdentity(username), roles.ToArray());
return true;
}
catch
{
return false;
}
}
}
TokenIdentity.cs (adapted from Steve's Coding Blog)
public class TokenIdentity : IIdentity
{
public string User { get; private set; }
public TokenIdentity(string user)
{
this.User = user;
}
public string Name => User;
public string AuthenticationType => "ApplicationToken";
public bool IsAuthenticated => true;
}