保护返回JSON的ASP.NET MVC控制器操作

9

我有一个MVC3应用程序,我的控制器操作使用[Authorize]属性进行安全保护。到目前为止,表单身份验证效果很好。现在我想在我的应用程序中添加JSON API,以便一些操作可以被非浏览器客户端访问。

我在设计上遇到了一些困难。

1)每个用户都有一个秘密API密钥。

2)用户ID 5调用http://myapp.com/foocontroller/baraction/5?param1=value1&param2=value2&secure_hash=someValue。这里,secure_hash只是参数1和参数2的值附加上用户的秘密API密钥后计算的SHA1哈希值

3)/foocontroller/baraction将被标记为[CustomAuthorize]。这将是AuthorizeAttribute的实现,它将检查请求是否作为JSON进入。如果是,则检查哈希值并查看是否匹配。否则,如果请求是HTML,则调用现有的授权。

我完全不确定这是否有效。在查询字符串中传递安全哈希值是否正常,还是应该将其作为HTTP头传递?使用HTTP基本身份验证而不是使用秘密API密钥生成的哈希值是否更好?

欢迎任何已使用ASP.NET MVC创建Web API的人提供提示!

1个回答

11

我在请求正文中传递了秘密API密钥以及用户名和密码。授权后,会生成一个令牌,客户端必须在Authorization头中传递该令牌。在每个请求中,基础控制器将检查此令牌。

  1. 客户端调用myapp.com/authorize以获取授权令牌。
  2. 客户端在本地存储授权令牌。
  3. 客户端使用Authorization头调用带有授权令牌的myapp.com/anycontroller。

AuthorizeController继承自控制器。 Anycontroller继承自一个自定义的基础控制器,该控制器执行授权代码。

我的示例需要以下路由,将POST请求指向任何控制器中名为post的ActionResult。我手动输入此内容,以尽可能简化并给您概括基本思路。请不要期望剪切和粘贴即可使用 :)

routes.MapRoute(
    "post-object",
    "{controller}",
    new { controller = "Home", action = "post" {,
    new { httpMethod = new HttpMethodConstraint("POST")}
);

你的授权控制器可以使用这个

public class AuthorizationController : Controller
{
    public ActionResult Post()
    {
        string authBody;
        var request = ControllerContext.HttpContext.Request;
        var response = ControllerContext.HttpContext.Response;

        using(var reader = new StreamReader(request.InputStream))
            authBody = reader.ReadToEnd();

        // authorize based on credentials passed in request body
        var authToken = {result of your auth method}

        response.Write(authToken);

    }
}

你的其他控制器继承自一个基础控制器。

public class BaseController : Controller
{
    protected override void Execute(RequestContext requestContext)
    {
        var request = requestContext.HttpContext.Request;
        var response = requestContext.HttpContext.Response;

        var authToken = Request.Headers["Authorization"];

        // use token to authorize in your own method
        var authorized = AmIAuthorized();

        if(authorized = false) { 
            response.StatusCode = 401; 
            response.Write("Invalid token");
            return;            
        }

        response.StatusCode = 200; // OK

        base.Execute(requestContext);  // allow inheriting controller to continue

    }
}

调用API的示例代码

 public static void ExecutePostRequest(string contentType)
        {
            request = (HttpWebRequest)WebRequest.Create(Uri + Querystring);
            request.Method = "POST";
            request.ContentType = contentType;  // application/json usually
            request.Headers["Authorization"] = token;

            using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
                writer.Write(postRequestData);

            // GetResponse reaises an exception on http status code 400
            // We can pull response out of the exception and continue on our way            
            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException ex)
            {
                response = (HttpWebResponse)ex.Response;
            }
            finally
            {
                using (StreamReader reader =
                    new StreamReader(response.GetResponseStream()))
                    responseText = reader.ReadToEnd();
                httpcontext = HttpContext.Current;
            }
        }

Jason,我想了解更多细节。我对MVC很陌生(有RoR背景),所以不太清楚授权属性的工作原理。谢谢! - Bilal and Olga
谢谢Jason!您如何“将秘密API密钥与用户名和密码一起通过请求正文传递”?您只需将自定义HTTP标头添加到请求中还是使用Http-Authorize? - Bilal and Olga
谢谢Jason!我有一个关于通常用于生成加密令牌的问题。我已经有了一个自定义的MembershipProvider,可能你只需在认证请求中传递的用户名和密码上进行身份验证,将这些详细信息与时间戳一起编码/加密?我想我的问题实际上是,令牌包含什么?谢谢 - sambomartin
这是调用我们的REST API的代码。POST方法需要Authorization头 - 一个GUID - 我们使用自定义令牌管理器创建。 - Jason Watts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接