注意:我在下面的代码注释中有许多问题。我也需要那些答案。
我已阅读(以及其他许多文章)以下文章:
我希望我的web api使用Authorization标头在标头中发送身份验证。我希望将此标头填充到名为AuthenticationToken
的c#类中。然后,当我进行参数绑定时,我想要检索此先前创建的AuthenticationToken
对象并将其传递给我的控制器操作。例如,如果我有以下控制器
public class MyServiceController : ApiController {
readonly ISecurityService _security;
readonly IMyService _myService;
// constructor values are injected
public MyServiceController(ISecurityService security, IMyService myService) {
_security = security;
_myService = myService;
}
public SomeData GetASpecificItem(AuthenticationToken token, int id) {
if (_security.IsAuthorized(token, Permissions.Read)) {
return myService.DoStuffToGetSomeData(token);
} else {
var msg = new HttpResponseMessage(HttpStatusCode.Forbidden);
throw new HttpResponseException(msg);
}
}
}
和以下参数绑定类
public class AuthenticationTokenParameterBinding
: HttpParameterBinding { // do I need to inherit from a different class?
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext,
CancellationToken cancellationToken) {
try {
AuthenticationToken token; // UPDATED: how can i get this from the data
// available from inside this method?
SetValue(actionContext, token);
// is this the correct task to return on successfull parameter binding?
return base.ExecuteBindingAsyn(metadataProvider, actionContext, cancellationToken);
} catch {
return Task<HttpResponseMessage>.Factory.StartNew(() => {
var hpm = new HttpResponseMessage(HttpStatusCode.Unauthorized);
hpm.Headers.Add("WWW-Authenticate","MyCustomScheme");
return hpm;
});
}
}
}
如果正确实现了这两个,那么控制器将自动获取授权期间创建的AuthenticationToken
实例。
我不知道在此过程之前在何处进行身份验证。我也不知道如何在身份验证和授权之间传递对象。
更新
我无法使用自定义AuthorizeAttribute
,因为授权可能针对某个对象:
public SaveResponse Save(AuthenticationToken user, SomeObjectThatNeedsToBeSaved obj) {
// NOTE: permissions are checked between the object and the user, not a role
if (_security.IsAuthorized(user, obj, Permission.Modify, Permission.Create)) {
// NOTE: other permissions we don't know about may need to be checked in the service call
return new SaveResponse {
Success = ISomeService.Save(user, obj); // bool return value
}
} else {
// return 403 Forbidden }
}
我需要将令牌传递给控制器操作,但我还需要在将令牌传递给控制器之前对其进行身份验证。由于所有这些都不一定是基于角色的,我不知道如何从自定义AuthorizeAttribute
内部进行身份验证
答案 0 :(得分:1)
我使用自定义 AuthorizeAttribute 来处理Web API的身份验证和授权。此属性用作过滤器,并在请求到达Web API方法之前处理该请求。在重写的 OnAuthorize 方法中,如果身份验证失败并且 HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)<,则返回 HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) / strong>如果授权失败,则客户端可以区分两种类型的错误。除了自定义 AuthorizeAttribute 之外,我还实施了自定义 MembershipProvider 和 RoleProvider 来处理我的特定安全要求和自定义数据库架构。
我使用基本身份验证来传递凭据以进行授权。这会将凭据放在标头中。要通过使用JQuery ajax 函数的 beforeSend 事件处理程序,可以做到这一点非常简单。以下是如何执行此操作的示例。
getAuthorizationHeader = function (username, password) {
var authType;
var up = $.base64.encode(username + ":" + password);
authType = "Basic " + up;
};
return authType;
};
$.ajax({
url: _url,
data: _data,
type: _type,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", getAuthorizationHeader(username, password));
},
success: ajaxSuccessHandler,
error: ajaxErrHandler
});
这会对标头中发送的用户名/密码进行编码。请注意,这仅仅依赖于编码是不够安全的,因为它很容易解码。您仍然希望使用HTTPS / SSL来确保通过线路发送的信息是安全的。
在Web API方面,您可以创建自定义 AuthorizeAttribute ,从标头中获取凭据,对其进行解码并执行授权过程。 Web API使用单独的 AuthorizeAttribute 而不是控制器。在创建自定义 AuthorizeAttribute 时,请务必使用 System.Web.Http.AuthorizeAttribute 作为基类。他们有不同的行为。控制器的一个将重定向到登录页面,而Web API的一个返回指示成功或失败的HTTP代码。如果授权无法区分由于授权而导致的失败而非身份验证,我会返回Forbidden的HTTP代码,以便客户端做出相应的反应。
以下是从标头中获取可在自定义 AuthorizeAttribute 中使用的凭据的示例方法。
private bool GetUserNameAndPassword(HttpActionContext actionContext, out string username, out string password)
{
bool gotIt = false;
username = string.Empty;
password = string.Empty;
IEnumerable<string> headerVals;
if (actionContext.Request.Headers.TryGetValues("Authorization", out headerVals))
{
try
{
string authHeader = headerVals.FirstOrDefault();
char[] delims = { ' ' };
string[] authHeaderTokens = authHeader.Split(new char[] { ' ' });
if (authHeaderTokens[0].Contains("Basic"))
{
string decodedStr = SecurityHelper.DecodeFrom64(authHeaderTokens[1]);
string[] unpw = decodedStr.Split(new char[] { ':' });
username = unpw[0];
password = unpw[1];
}
gotIt = true;
}
catch { gotIt = false; }
}
return gotIt;
}
以下是解码此方法中使用的标头数据的代码。
public static string DecodeFrom64(string encodedData)
{
byte[] encodedDataAsBytes
= System.Convert.FromBase64String(encodedData);
string returnValue =
System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);
return returnValue;
}
获得用户名和密码后,您可以执行授权过程并将相应的HTTP代码返回给客户端进行处理。
您可以使用自定义令牌执行类似的过程,或者如果您不想将密码/用户名保存在客户端中,则可以利用来回传递的cookie。