使用 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;
}