ASP.NET Web API 如何对用户进行身份验证
Posted
技术标签:
【中文标题】ASP.NET Web API 如何对用户进行身份验证【英文标题】:ASP.NET Web API how to authenticate user 【发布时间】:2013-11-14 11:46:05 【问题描述】:我正在尝试创建一个简单的用户身份验证功能,但我无法让它工作。 这是我正在处理的代码:
public class LoginController : ApiController
private void SetPrincipal(IPrincipal principal)
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
public bool Login(string token)
//Check token
if (.....)
//Authenticate user
var identity = new GenericIdentity("Test user");
SetPrincipal(new GenericPrincipal(identity, new string[]"Test role"));
[Authorize]
public string TestFun()
return "Hello " + User.Identity.Name;
所以,如果我首先尝试调用方法 TestFun()
,它会返回错误代码 401。
但是,当我调用方法 Login()
时,它应该以某种方式保存用户凭据,但这是我迷路的地方,我无法让它工作。
TestFun()
总是返回错误代码 401,即使我先调用 Login()
。
如果我尝试将 return "Hello " + User.Identity.Name;
放在 Login()
函数中,它会返回正确的用户名,但在 TestFun()
中,用户不可用。
我什至尝试过使用 Sessions 和 FormsAuthentication,但即使在这个非常简单的示例中,我也无法让它工作。
谁能告诉我我错过了什么?
谢谢!
【问题讨论】:
【参考方案1】:Login 方法仅为当前请求设置主体。请求完成后,主体上下文被清除,以便服务器可以处理其他用户的其他请求。当一个新请求到来时,从服务器的角度来看,主体上下文不再存在,如果没有任何恢复,则该请求未经身份验证。
要解决此问题,您必须将登录方法中的某些内容返回给客户端。不仅是 bool,而是 - 身份验证令牌。客户端可以用来验证进一步请求的东西。
它可以是任何东西。只要客户端记得将其附加到进一步的请求中,表单 cookie 就可以了。另一种常见的做法是将自定义身份验证令牌返回给客户端,然后由客户端附加到自定义身份验证标头中。由于表单 cookie 由表单身份验证模块处理,自定义标头需要自定义 mvc 身份验证过滤器或自定义 asp.net 身份验证模块,以便读取令牌、提取和恢复身份之前请求即将执行。
如果您不喜欢烘焙自己的令牌基础架构,我还推荐 OAuth2 令牌。有一本很棒的书,其中包含有关此方法和其他可能的身份验证方法的易于遵循的示例:
http://www.amazon.com/Pro-ASP-NET-Web-API-Security/dp/1430257822/ref=sr_1_1?ie=UTF8&sr=8-1&keywords=web+api+security
【讨论】:
没有更简单的方法吗?我发送给 Login 方法的令牌实际上是 Google OAuth2 令牌。然后,该方法通过向 google 发送请求来检索 google id,然后从我的数据库中检索我的私有 userId。我试图通过不对所有未来的方法执行此操作来节省宝贵的 CPU 时间。理想情况下,我只想将检索到的 userId 保存在某个 Session 变量中,但这也不起作用,由于某种原因,Session 对象始终为空。 您发送给 google 以在那里进行身份验证的令牌和发送给您的方法以在那里进行身份验证的令牌不应混淆。此外,不要害怕“节省 cpu 时间”,令牌身份验证总是在每次请求时解密令牌,表单身份验证会这样做,任何其他方法都会这样做。如果将其存储在会话中,则会浪费 CPU 内存。无论如何,可扩展性问题都会影响到你,而且在每次请求时验证令牌更容易。 如果我理解正确的话,我可以有两种方法:1)我可以将谷歌令牌发送到所有方法。一开始他们会用 google 对用户进行身份验证,然后从我的数据库中检索我自己的 userId。这会更容易并且可能更安全,因为谷歌会完成所有工作。由于额外的谷歌服务器之旅,它也会变慢。 2)我将谷歌令牌发送到Login()
方法,该方法对用户进行身份验证并创建我自己的令牌并将其与userId一起存储在我的数据库中。所有其他方法都使用我自己的令牌,它更快,但我必须自己编写所有安全逻辑。
1) 和 2) 都是正确的。我不建议 1) 由于受到惩罚,在内部使用 google 令牌来验证访问您服务的用户意味着您每次都必须验证此类令牌。关于 2),一种常见的做法是在您的自定义令牌中加密用户名,以便当您的令牌附带请求时,您只需对其进行解密。如果它正确解密并且其中有用户名,则意味着您(您的服务器)必须对其进行加密(假设您的私钥没有丢失)。【参考方案2】:
我刚遇到同样的问题,是的,我同意我们需要将该主体保存到某个位置(cookie、会话)以供其他操作使用,因此,我在 SetPrincipal 函数中添加了
HttpContext.Current.Session["user"] = HttpContext.Current.User;
现在,问题是如何将其取回以进行其他操作,我脑海中的想法是扩展 AuthorizeAttribute 并覆盖 IsAuthrized 函数,它将首先读取会话,如果找到会话,它将返回 true,否则返回 false。
namespace BinZ
public class MyAuthorizeAttribute:AuthorizeAttribute
protected override bool IsAuthorized(HttpActionContext actionContext)
HttpContext.Current.User = HttpContext.Current.Session["user"] as IPrincipal;
return HttpContext.Current.User != null;
请记住在 WebApi 控制器中将 [Authorize] 替换为 [MyAuthorizeAttribute]。
它非常适合我。
干杯
【讨论】:
以上是关于ASP.NET Web API 如何对用户进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章
使用 ADFS 对 ASP.NET Web Api 进行身份验证
无法使用 HttpClient 对 ASP.NET Web Api 服务进行身份验证