![](/img/trans.png)
[英]How to authenticate from Web API controller endpoint when implementing Identity 2.1 + OWIN OAuth JWT bearer token
[英]Return JWT Token generated by OAuthAuthorizatioServer from controller in Web API
在@Taiseer Joudeh之后,我能够创建简单的Web API POC。 我能够创建新帐户,然后登录并在向标头添加JWT令牌时调用安全Web API。
我想修改负责创建帐户的方法。
现在我正在返回带有新用户对象的Create(201)代码,但我想返回访问令牌。
我发现了类似的问题,但它需要创建HttpClient
并向OAuthAuthorizatioServer TokenEndpointPath发出请求。
我发现的第二个问题需要生成返回到前端的临时令牌,但是前端必须向服务器发出额外请求以获得“真实”令牌。
我想做的是在创建用户帐户时返回登录响应(access_token,token_type和expires_in)。 我希望用户在创建帐户时进行身份验证。
我只使用Web API和JWT而没有任何cookie。
编辑:我的临时解决方案:
创建用户后我正在这样做:
var validTime = new TimeSpan(0, 0, 0, 10);
var identity = await UserManager.CreateIdentityAsync(user, "JWT");
var jwtFormat = new CustomJwtFormat(ApplicationConfiguration.Issuer);
var authenticationProperties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow, ExpiresUtc = DateTimeOffset.UtcNow.Add(validTime) };
var authenticationTicket = new AuthenticationTicket(identity, authenticationProperties);
var token = jwtFormat.Protect(authenticationTicket);
var response = new
{
access_token = token,
token_type = "bearer",
expires_in = validTime.TotalSeconds.ToInt()
};
return Ok(response);
CustomJwtFormat
来自这篇很棒的文章 。
下面是一些代码,类似于我在我的应用程序中所做的,它使用的是Asp.Net Core 1.0。 如果您不使用Core 1.0,您的登录和用户注册会有所不同。
public async Task<string> CreateUser(string username, string password)
{
string jwt = String.Empty;
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
var user = await _userManager.FindByNameAsync(username);
if (user == null) // user doesn't exist, create user
{
var newUser = await _userManager.CreateAsync(new ApplicationUser() { UserName = username }, password);
if (newUser.Succeeded) //user was successfully created, sign in user
{
user = await _userManager.FindByNameAsync(username);
var signInResult = await _signInManager.PasswordSignInAsync(user, password, false, true);
if (signInResult.Succeeded) //user signed in, create a JWT
{
var tokenHandler = new JwtSecurityTokenHandler();
List<Claim> userClaims = new List<Claim>();
//add any claims to the userClaims collection that you want to be part of the JWT
//...
ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.UserName, "TokenAuth"), userClaims);
DateTime expires = DateTime.Now.AddMinutes(30); //or whatever
var securityToken = tokenHandler.CreateToken(
issuer: _tokenOptions.Issuer, //_tokenAuthOptions is a class that holds the issuer, audience, and RSA security key
audience: _tokenOptions.Audience,
subject: identity,
notBefore: DateTime.Now,
expires: expires,
signingCredentials: _tokenOptions.SigningCredentials
);
jwt = tokenHandler.WriteToken(securityToken);
Response.StatusCode = (int)HttpStatusCode.Created;
await _signInManager.SignOutAsync(); //sign the user out, which deletes the cookie that gets added if you are using Identity. It's not needed as security is based on the JWT
}
}
//handle other cases...
}
}
基本上,创建用户然后自动登录。 然后我构建一个JWT(添加你想要的任何声明)并将其返回到响应正文中。 在客户端(MVC和Angular JS),我从响应主体中获取JWT并存储它。 然后在每个后续请求的Authorization标头中将其传递回服务器。 所有服务器操作的授权策略均基于JWT提供的声明集。 没有cookie,服务器上没有状态。
编辑:我想你甚至不需要在这个过程中调用signIn方法,因为你可以创建用户,创建一个JWT,并返回JWT。 当用户登录将来的请求时,您将使用登录,创建令牌,注销方法。
在创建用户时发送带有access_token,token_type和expires_in的响应的想法是个好主意。 但是,我会坚持用HttpClient调用“/ token”终点来完成这个任务。 在生成令牌之前,需要执行相当多的安全检查。 由于这是安全,我不会冒任何风险。 我很乐意使用行业专家提供的库/代码。
也就是说,我尝试创建一个类,您可以在创建用户后调用该类来创建令牌。 此代码取自Microsoft在Katana项目中的OAuth授权服务器实现。 您可以在此处访问源代码。 正如您所看到的,在创建令牌时会发生很多事情。
以下是用于生成令牌的中间件类的修改版本。 您必须提供正确的OAuthAuthorizationServerOptions,Context,username,password,Scopes和clientid才能获取访问令牌。 请注意,这是一个示例实现,可指导您朝着正确的方向前进。 如果你想使用它,请彻底测试。
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AspNetIdentity.WebApi.Providers;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Microsoft.Owin.Security.OAuth;
namespace WebApi.AccessToken
{
public class TokenGenerator
{
public string ClientId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public IList<string> Scope { get; set; }
private OAuthAuthorizationServerOptions Options { get; } =
new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat("http://localhost:59822")
};
public async Task<IList<KeyValuePair<string, string>>> InvokeTokenEndpointAsync(IOwinContext owinContext)
{
var result = new List<KeyValuePair<string, string>>();
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
// remove milliseconds in case they don't round-trip
currentUtc = currentUtc.Subtract(TimeSpan.FromMilliseconds(currentUtc.Millisecond));
AuthenticationTicket ticket = await InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(owinContext, Options, currentUtc);
if (ticket == null)
{
result.Add(new KeyValuePair<string, string>("ERROR", "Failed to create acess_token"));
return result;
}
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(Options.AccessTokenExpireTimeSpan);
ticket = new AuthenticationTicket(ticket.Identity, ticket.Properties);
var accessTokenContext = new AuthenticationTokenCreateContext(
owinContext,
Options.AccessTokenFormat,
ticket);
await Options.AccessTokenProvider.CreateAsync(accessTokenContext);
string accessToken = accessTokenContext.Token;
if (string.IsNullOrEmpty(accessToken))
{
accessToken = accessTokenContext.SerializeTicket();
}
DateTimeOffset? accessTokenExpiresUtc = ticket.Properties.ExpiresUtc;
result.Add(new KeyValuePair<string, string>("access_token", accessToken));
result.Add(new KeyValuePair<string, string>("token_type", "bearer"));
TimeSpan? expiresTimeSpan = accessTokenExpiresUtc - currentUtc;
var expiresIn = (long)expiresTimeSpan.Value.TotalSeconds;
if (expiresIn > 0)
{
result.Add(new KeyValuePair<string, string>("expires_in", "bearer"));
}
return result;
}
private async Task<AuthenticationTicket> InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(IOwinContext owinContext, OAuthAuthorizationServerOptions options, DateTimeOffset currentUtc)
{
var grantContext = new OAuthGrantResourceOwnerCredentialsContext(
owinContext,
options,
ClientId,
UserName,
Password,
Scope);
await options.Provider.GrantResourceOwnerCredentials(grantContext);
return grantContext.Ticket;
}
}
}
请让我知道,如果你有任何问题。
谢谢,Soma。
我假设你指的是以下文章: http : //bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity- 2 /
在这种情况下验证用户的一般方法是
现在尝试了解一般情况下,需要整个身份验证过程,因为您的服务器不信任登录的用户是该用户声称的用户。 但是,在您的情况下,您(或您的服务器代码)只是创建了一个用户,并且您确定访问您网站的人是您刚刚创建的用户。 在这种情况下,您无需验证令牌即可为此用户创建会话。 只需创建一个适合您的用例的会话。
下次用户必须使用令牌登录并向服务器证明他/她自己,但这次用户不需要证明他/她自己。
注意:如果您绝对坚持要求令牌登录您自己刚刚使用其凭据创建的用户,则存在一些问题。
另外,看一下支持隐式授权的Windows Server 2016(技术预览版本5),如果您可以等待RTM,可能需要将所有这些自定义代码写下来。
在OAuth解决方案中, 您作为开发人员不需要自己处理cookie设置。 cookie处理由框架自动完成。
设置会话的唯一方法是a。 使用会话cookie或b。 使用无cookie(在url中)方法。 请查看http://www.cloudidentity.com/blog/2015/02/19/introducing-adal-js-v1/ ,了解有关令牌验证和会话建立的更多详细信息(同时搜索术语cookie,您将知道它的全部内容用于)。
如果您开始考虑根本不使用cookie,您不仅需要弄清楚如何维护会话并在没有cookie的情况下安全地执行,还必须重新编写令牌刷新代码,以便根据您的身份检测并刷新令牌。存在会话cookie。 (即不是一个聪明的主意)
我正在使用确切的技术堆栈和最近成功实现的基于令牌的授权。 我参考的链接已经非常巧妙地定义了Web API中基于令牌的身份验证。 我必须说的必须书签页面。 这是链接: 基于TOKEN的WEB API认证 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.