首页 > 解决方案 > IdentityServer4 使用 asp.net 核心中的密码授权请求 JWT / 访问承载令牌

问题描述

我已经在 asp.net 核心中使用 IdentityServer4 使用密码授予请求 JWT / Access Bearer Token 进行了全面搜索,但我似乎找不到正确的方法。

下面是我注册用户的 POST 请求。

http://localhost:52718/account/register

下面是不记名令牌 GET 请求,我可以使用 IdentityServer4 从中获取 JWT 令牌

http://localhost:52718/connect/token

以下是我登录用户的 POST 请求

http://localhost:52718/account/signin

现在,我想要做的是当我登录我的用户时,我想要一个 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 / 访问令牌。

现在我的实际问题是:

我可以在我的登录方法中做什么,所以当用户登录时它会返回我的令牌以及其他用户数据。我希望我简要解释一下我的问题。

谢谢

标签: asp.net-corejwtidentityserver4

解决方案


我找到了自己的问题答案。在开始之前,我将向您展示我定义客户端的代码。

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;
    }
}

现在这是我的愿望输出。希望这个答案对其他人有所帮助。

在此处输入图像描述


推荐阅读