如何使WebAPI操作只能从我的应用中访问?

时间:2021-07-25 20:05:08

A common use case for WebAPI would be to have shell views rendered by MVC controllers, which contain javascript that then hit your API to access data.

WebAPI的一个常见用例是让MVC控制器呈现shell视图,其中包含javascript,然后用API访问数据。

But let's say you have some expensive API operations and you don't want people remotely accessing those endpoints -- you only want your MVC views, delivered by your application, to access them. How could you go about protecting them?

但是,假设您有一些昂贵的API操作,并且您不希望人们远程访问这些端点——您只希望您的应用程序交付的MVC视图访问它们。你怎么能保护他们?

In this case Request.IsLocal doesn't work, because javascript is invoking it from the client's browser on their machine. Even if it did work, you need to dig to get the real HttpContext in order to find this property -- and that solution wouldn't work in self-hosted WebAPI.

在这种情况下请求。IsLocal无法工作,因为javascript正在从客户机的浏览器上调用它。即使它确实有效,您也需要挖掘以获取真正的HttpContext,以便找到这个属性——并且该解决方案在自托管的WebAPI中不能工作。

For API endpoints that require a valid IPrincipal, you could protect them with the [Authorize] attribute. But what about API endpoints that you want your app to be able to access for anonymous users?

对于需要有效IPrincipal的API端点,可以使用[Authorize]属性保护它们。但是,您希望应用程序能够访问匿名用户的API端点呢?

I have tried a solution and will post it separately as an answer, because I'm not sure if it's the best (or even a good) approach.

我已经尝试过一个解决方案,并将它作为一个答案单独发布,因为我不确定它是否是最好的(甚至是最好的)方法。

2 个解决方案

#1


2  

If your MVC site uses authentication, you could enable forms authentication for your Web API methods. You could write a custom [Authorize] attribute that will check for the presence of a forms authentication cookie which will be sent from the AJAX call and if present construct the principal.

如果您的MVC站点使用身份验证,您可以为您的Web API方法启用表单身份验证。您可以编写一个自定义[授权]属性,该属性将检查表单身份验证cookie的存在,该cookie将从AJAX调用中发送,如果当前构建了主体。

Another possible solution is to protect your API with tokens which is a more RESTful style. The idea here is that when a user authenticates on your MVC website you could generate and pass a token to the view which will be used when sending the AJAX request to the Web API which in turn will verify the validity of the token and its signature.

另一种可能的解决方案是使用更RESTful的标记来保护您的API。这里的想法是,当用户在您的MVC网站上进行身份验证时,您可以生成一个令牌并将其传递给视图,该视图将在将AJAX请求发送给Web API时使用,该API将验证令牌及其签名的有效性。

If on the other hand your site doesn't use authentication, then things will get very complicated because you have no way of knowing whether the request comes from a trusted client since you are using javascript to call your API methods.

另一方面,如果站点不使用身份验证,那么事情就会变得非常复杂,因为您无法知道请求是否来自受信任的客户端,因为您正在使用javascript调用API方法。

#2


2  

Before you go harping about "what have you tried", here is what I have tried. It works. Just not sure if there is a better way.

在你喋喋不休地说“你试过了什么”之前,我想说的是。它的工作原理。只是不确定是否有更好的方法。

  1. Create an MVC action filter and add it as a global filter during Application_Start.

    创建一个MVC操作过滤器,并在Application_Start期间将其作为全局过滤器添加。

  2. Create an Http (WebAPI) action filter and use it on actions that should reject remote requests.

    创建一个Http (WebAPI)操作过滤器,并将其用于应该拒绝远程请求的操作。

The global MVC filter does this:

全局MVC过滤器做以下事情:

  1. Looks for a specific cookie in the request. If the cookie is there, its value is decrypted. The decrypted value should be a string representation of a DateTime, so use DateTime.TryParse to get it out. If the value is correctly parsed to a DateTime, and that DateTime is less than a day old, STOP HERE and do nothing else.

    在请求中查找特定的cookie。如果cookie在那里,它的值将被解密。解密后的值应该是DateTime的字符串表示形式,所以使用DateTime。试着解析它。如果值被正确解析为一个DateTime,且该DateTime小于一天,那么就在这里停下来,什么都不做。

  2. If the cookie is not there, or cannot be decrypted / parsed, or is older than a day, write a new cookie to the browser. Use the current DateTime.UtcNow.ToString() as the value, encrypt it, and write it with HttpOnly = false.

    如果cookie不存在,或者无法解密/解析,或者超过一天,则向浏览器编写一个新的cookie。使用当前的datetime . utcn . tostring()作为值,对其进行加密,并使用HttpOnly = false将其写入。

The WebAPI filter does this:

WebAPI过滤器是这样做的:

  1. Looks for a specific cookie in the request. If the cookie is there, decrypt its value and try to parse it out as a DateTime.

    在请求中查找特定的cookie。如果cookie在那里,请解密它的值并尝试将其解析为DateTime。

  2. If the value is a valid DateTime and is less than 2 days old, STOP HERE and do nothing else.

    如果该值是一个有效的DateTime,且小于2天,那么就在这里停下来,什么都不做。

  3. Otherwise, throw a 403 Forbidden exception.

    否则,抛出一个403禁止的异常。

A couple of notes about my current implementation of this. First of all, I use AES encryption with a shared secret and a salt. The shared secret is stored as an appSetting in web.config. For the salt, I enabled anonymous identification and used Request.AnonymousID as the salt. I'm not entirely fond of the salt because it's tricker to get at in a WebAPI controller, but not impossible as long as it is not self-hosted.

关于我当前实现的一些注意事项。首先,我使用AES加密和一个共享的秘密和一个盐。共享密钥存储为web.config中的一个appset。对于salt,我启用了匿名标识并使用了请求。AnonymousID盐。我并不完全喜欢salt,因为在WebAPI控制器中使用它会很麻烦,但只要它不是自托管的,就不是不可能的。

#1


2  

If your MVC site uses authentication, you could enable forms authentication for your Web API methods. You could write a custom [Authorize] attribute that will check for the presence of a forms authentication cookie which will be sent from the AJAX call and if present construct the principal.

如果您的MVC站点使用身份验证,您可以为您的Web API方法启用表单身份验证。您可以编写一个自定义[授权]属性,该属性将检查表单身份验证cookie的存在,该cookie将从AJAX调用中发送,如果当前构建了主体。

Another possible solution is to protect your API with tokens which is a more RESTful style. The idea here is that when a user authenticates on your MVC website you could generate and pass a token to the view which will be used when sending the AJAX request to the Web API which in turn will verify the validity of the token and its signature.

另一种可能的解决方案是使用更RESTful的标记来保护您的API。这里的想法是,当用户在您的MVC网站上进行身份验证时,您可以生成一个令牌并将其传递给视图,该视图将在将AJAX请求发送给Web API时使用,该API将验证令牌及其签名的有效性。

If on the other hand your site doesn't use authentication, then things will get very complicated because you have no way of knowing whether the request comes from a trusted client since you are using javascript to call your API methods.

另一方面,如果站点不使用身份验证,那么事情就会变得非常复杂,因为您无法知道请求是否来自受信任的客户端,因为您正在使用javascript调用API方法。

#2


2  

Before you go harping about "what have you tried", here is what I have tried. It works. Just not sure if there is a better way.

在你喋喋不休地说“你试过了什么”之前,我想说的是。它的工作原理。只是不确定是否有更好的方法。

  1. Create an MVC action filter and add it as a global filter during Application_Start.

    创建一个MVC操作过滤器,并在Application_Start期间将其作为全局过滤器添加。

  2. Create an Http (WebAPI) action filter and use it on actions that should reject remote requests.

    创建一个Http (WebAPI)操作过滤器,并将其用于应该拒绝远程请求的操作。

The global MVC filter does this:

全局MVC过滤器做以下事情:

  1. Looks for a specific cookie in the request. If the cookie is there, its value is decrypted. The decrypted value should be a string representation of a DateTime, so use DateTime.TryParse to get it out. If the value is correctly parsed to a DateTime, and that DateTime is less than a day old, STOP HERE and do nothing else.

    在请求中查找特定的cookie。如果cookie在那里,它的值将被解密。解密后的值应该是DateTime的字符串表示形式,所以使用DateTime。试着解析它。如果值被正确解析为一个DateTime,且该DateTime小于一天,那么就在这里停下来,什么都不做。

  2. If the cookie is not there, or cannot be decrypted / parsed, or is older than a day, write a new cookie to the browser. Use the current DateTime.UtcNow.ToString() as the value, encrypt it, and write it with HttpOnly = false.

    如果cookie不存在,或者无法解密/解析,或者超过一天,则向浏览器编写一个新的cookie。使用当前的datetime . utcn . tostring()作为值,对其进行加密,并使用HttpOnly = false将其写入。

The WebAPI filter does this:

WebAPI过滤器是这样做的:

  1. Looks for a specific cookie in the request. If the cookie is there, decrypt its value and try to parse it out as a DateTime.

    在请求中查找特定的cookie。如果cookie在那里,请解密它的值并尝试将其解析为DateTime。

  2. If the value is a valid DateTime and is less than 2 days old, STOP HERE and do nothing else.

    如果该值是一个有效的DateTime,且小于2天,那么就在这里停下来,什么都不做。

  3. Otherwise, throw a 403 Forbidden exception.

    否则,抛出一个403禁止的异常。

A couple of notes about my current implementation of this. First of all, I use AES encryption with a shared secret and a salt. The shared secret is stored as an appSetting in web.config. For the salt, I enabled anonymous identification and used Request.AnonymousID as the salt. I'm not entirely fond of the salt because it's tricker to get at in a WebAPI controller, but not impossible as long as it is not self-hosted.

关于我当前实现的一些注意事项。首先,我使用AES加密和一个共享的秘密和一个盐。共享密钥存储为web.config中的一个appset。对于salt,我启用了匿名标识并使用了请求。AnonymousID盐。我并不完全喜欢salt,因为在WebAPI控制器中使用它会很麻烦,但只要它不是自托管的,就不是不可能的。