asp.net-core - IdentityServer4 使用 asp.net 核心中的密码授权请求 JWT / 访问承载令牌
问题描述
我已经在 asp.net 核心中使用 IdentityServer4 使用密码授予请求 JWT / Access Bearer Token 进行了全面搜索,但我似乎找不到正确的方法。
下面是我注册用户的 POST 请求。
下面是不记名令牌 GET 请求,我可以使用 IdentityServer4 从中获取 JWT 令牌
以下是我登录用户的 POST 请求
现在,我想要做的是当我登录我的用户时,我想要一个 JWT / Bearer Token,就像我从这里得到的一样http://localhost:52718/connect/token。当我点击这个网址时。
这是我的 AccountController 代码:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Server.Models;
using Server.Models.AccountViewModels;
using Server.Models.UserViewModels;
namespace Server.Controllers
{
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
public AccountController(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager
)
{
_userManager = userManager;
_roleManager = roleManager;
}
[HttpPost]
public async Task<IActionResult> Register([FromBody]RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser { UserName = model.UserName, FirstName = model.FirstName, LastName = model.LastName, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
string role = "Basic User";
if (result.Succeeded)
{
if (await _roleManager.FindByNameAsync(role) == null)
{
await _roleManager.CreateAsync(new IdentityRole(role));
}
await _userManager.AddToRoleAsync(user, role);
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("userName", user.UserName));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("firstName", user.FirstName));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("lastName", user.LastName));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("email", user.Email));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("role", role));
return Ok(new ProfileViewModel(user));
}
return BadRequest(result.Errors);
}
public async Task<IActionResult> Signin([FromBody]LoginViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await _userManager.FindByNameAsync(model.UserName);
if (result != null && await _userManager.CheckPasswordAsync(result, model.Password))
{
return Ok(new ProfileViewModel(result));
}
return BadRequest("Invalid username or password.");
}
}
}
当我点击登录方法时,我成功获取了用户的数据。
但是当用户登录我的应用程序时,我还需要一个 jwt / 访问令牌。
现在我的实际问题是:
我可以在我的登录方法中做什么,所以当用户登录时它会返回我的令牌以及其他用户数据。我希望我简要解释一下我的问题。
谢谢
解决方案
我找到了自己的问题答案。在开始之前,我将向您展示我定义客户端的代码。
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
// resource owner password grant client
new Client
{
ClientId = "ro.angular",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Address,
"api1"
}
}
};
}
现在我在我的登录方法中所做的是使用 TokenClient 类来请求令牌。要创建实例,您需要传入令牌端点地址、客户端 ID 和密码。
接下来,我使用使用密码授予请求令牌,以允许客户端将用户名和密码发送到令牌服务并获取代表该用户的访问令牌。
这是我需要修改的登录代码:
public async Task<IActionResult> Signin([FromBody]LoginViewModel model)
{
var disco = await DiscoveryClient.GetAsync("http://localhost:52718");
if (disco.IsError)
{
return BadRequest(disco.Error);
}
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.angular", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(model.UserName, model.Password, "api1 openid");
if (tokenResponse.IsError)
{
return BadRequest(tokenResponse.Error);
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = _userManager.FindByNameAsync(model.UserName);
var result = await _userManager.FindByNameAsync(model.UserName);
if (result != null && await _userManager.CheckPasswordAsync(result, model.Password))
{
return Ok(new ProfileViewModel(result, tokenResponse));
}
return BadRequest("Invalid username or password.");
}
我还修改了 ProfileViewModel 类并添加了两个新的 Token & Expiry:
public class ProfileViewModel
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Token { get; set; }
public int Expiry { get; set; }
public ProfileViewModel()
{
}
public ProfileViewModel(ApplicationUser user, TokenResponse UToken = null)
{
Id = user.Id;
FirstName = user.FirstName;
LastName = user.LastName;
Email = user.Email;
Token = UToken.AccessToken;
Expiry = UToken.ExpiresIn;
}
public static IEnumerable<ProfileViewModel> GetUserProfiles(IEnumerable<ApplicationUser> users)
{
var profiles = new List<ProfileViewModel> { };
foreach (ApplicationUser user in users)
{
profiles.Add(new ProfileViewModel(user));
}
return profiles;
}
}
现在这是我的愿望输出。希望这个答案对其他人有所帮助。
推荐阅读
- java - JDK9 模块 - maven - 如何导入例如 org.apache.http 包?
- layout - Xamarin 表格网格布局尺寸不断减小
- .net - SqlDataAdapter.FillSchema("SELECT * FROM MyTable", SchemaType.Source) 是一种昂贵的方法吗?
- angular - 使用量角器在角度应用程序中选择下拉
- protocol-buffers - 带有 grpc_python_plugin 的协议:google/protobuf/any.proto:找不到文件
- spring-boot - AMQP 无法从侦听器接收回消息
- delphi - 双循环索引问题
- php - 如何在 symfony 4 中使用 collectionType 创建一个包含多个表单作为字段的表单?
- ms-access - 我希望子表单根据两个表的主键相同的组合框来查找值
- html - 防止网格区域扩大导致整个页面滚动