首页 > C# > ASP.NET Web API 的 JWT 身份验证

ASP.NET Web API 的 JWT 身份验证

上一篇 下一篇

我正在尝试在我的 Web API 应用程序中支持 JWT 持有者令牌(JSON Web 令牌),但我迷路了。

我看到了对.NET Core和OWIN应用程序的支持。
我当前在 IIS 中托管我的应用程序。

如何在我的应用程序中实现此身份验证模块?有什么方法可以使用类似于使用表单/Windows 身份验证的配置?<authentication>

分割线

网友回答:

我回答了这个问题:4年前如何使用HMAC保护 ASP.NET Web API。

现在,安全性发生了很多变化,尤其是JWT越来越受欢迎。在这个答案中,我将尝试以最简单和基本的方式解释如何使用 JWT,这样我们就不会迷失在 OWIN、Oauth2、ASP.NET 身份等丛林中。

如果您不了解 JWT 代币,您需要查看:

https://www.rfc-editor.org/rfc/rfc7519

基本上,JWT 令牌如下所示:

<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>

例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ

JWT 令牌包含三个部分:

  1. 标头:以 Base64 编码的 JSON 格式
  2. 声明:以 Base64 编码的 JSON 格式。
  3. 签名:基于以 Base64 编码的标头和声明创建和签名。

如果您使用带有上述令牌的网站 jwt.io,则可以解码令牌并如下所示:

从技术上讲,JWT 使用从标头和声明签名的签名,并在标头中指定安全算法(例如:HMACSHA256)。因此,如果在 JWT 声明中存储任何敏感信息,则必须通过 HTTP 传输 JWT。

现在,为了使用 JWT 身份验证,如果您有旧版 Web API 系统,则实际上并不需要 OWIN 中间件。简单的概念是如何提供 JWT 令牌以及如何在请求到来时验证令牌。就是这样。

在我创建的演示(github)中,为了保持JWT令牌的轻量级,我只存储和.但是这样,如果要执行角色授权等,则必须重新构建新的本地标识(主体)以添加更多信息,例如角色。但是,如果您想向 JWT 添加更多信息,这取决于您:它非常灵活。usernameexpiration time

只需使用控制器操作提供 JWT 令牌终结点,而不是使用 OWIN 中间件:

public class TokenController : ApiController
{
    // This is naive endpoint for demo, it should use Basic authentication
    // to provide token or POST request
    [AllowAnonymous]
    public string Get(string username, string password)
    {
        if (CheckUser(username, password))
        {
            return JwtManager.GenerateToken(username);
        }

        throw new HttpResponseException(HttpStatusCode.Unauthorized);
    }

    public bool CheckUser(string username, string password)
    {
        // should check in the database
        return true;
    }
}

这是一个幼稚的行为;在生产中,应使用 POST 请求或基本身份验证终结点来提供 JWT 令牌。

如何根据 生成令牌?username

可以使用从 Microsoft 调用的 NuGet 包来生成令牌,甚至可以使用另一个包(如果需要)。在演示中,我使用:System.IdentityModel.Tokens.JwtHMACSHA256SymmetricKey

/// <summary>
/// Use the below code to generate symmetric Secret Key
///     var hmac = new HMACSHA256();
///     var key = Convert.ToBase64String(hmac.Key);
/// </summary>
private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";

public static string GenerateToken(string username, int expireMinutes = 20)
{
    var symmetricKey = Convert.FromBase64String(Secret);
    var tokenHandler = new JwtSecurityTokenHandler();

    var now = DateTime.UtcNow;
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new[]
        {
            new Claim(ClaimTypes.Name, username)
        }),

        Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
        
        SigningCredentials = new SigningCredentials(
            new SymmetricSecurityKey(symmetricKey), 
            SecurityAlgorithms.HmacSha256Signature)
    };

    var stoken = tokenHandler.CreateToken(tokenDescriptor);
    var token = tokenHandler.WriteToken(stoken);

    return token;
}

提供 JWT 令牌的终结点已完成。

当请求到来时如何验证 JWT?

在演示中,我构建
了继承自(有关身份验证过滤器的更多详细信息,请参见此处)。
JwtAuthenticationAttributeIAuthenticationFilter

使用此属性,您可以对任何操作进行身份验证:您只需将此属性放在该操作上即可。

public class ValueController : ApiController
{
    [JwtAuthentication]
    public string Get()
    {
        return "value";
    }
}

如果要验证 WebAPI 的所有传入请求(不特定于控制器或操作),也可以使用 OWIN 中间件或委托手

以下是身份验证过滤器的核心方法:

private static bool ValidateToken(string token, out string username)
{
    username = null;

    var simplePrinciple = JwtManager.GetPrincipal(token);
    var identity = simplePrinciple.Identity as ClaimsIdentity;

    if (identity == null || !identity.IsAuthenticated)
        return false;

    var usernameClaim = identity.FindFirst(ClaimTypes.Name);
    username = usernameClaim?.Value;

    if (string.IsNullOrEmpty(username))
       return false;

    // More validate to check whether username exists in system

    return true;
}

protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
    string username;

    if (ValidateToken(token, out username))
    {
        // based on username to get more information from database 
        // in order to build local identity
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, username)
            // Add more claims if needed: Roles, ...
        };

        var identity = new ClaimsIdentity(claims, "Jwt");
        IPrincipal user = new ClaimsPrincipal(identity);

        return Task.FromResult(user);
    }

    return Task.FromResult<IPrincipal>(null);
}

工作流是使用 JWT 库(上面的 NuGet 包)验证 JWT 令牌,然后返回 。您可以执行更多验证,例如检查系统上是否存在用户,并根据需要添加其他自定义验证。ClaimsPrincipal

用于验证 JWT 令牌并取回主体的代码:

public static ClaimsPrincipal GetPrincipal(string token)
{
    try
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;

        if (jwtToken == null)
            return null;

        var symmetricKey = Convert.FromBase64String(Secret);

        var validationParameters = new TokenValidationParameters()
        {
            RequireExpirationTime = true,
            ValidateIssuer = false,
            ValidateAudience = false,
            IssuerSigningKey = new SymmetricSecurityKey(symmetricKey)
        };

        SecurityToken securityToken;
        var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);

        return principal;
    }
    catch (Exception)
    {
        //should write log
        return null;
    }
}

如果验证了 JWT 令牌并返回了主体,则应生成新的本地标识,并将更多信息放入其中以检查角色授权。

请记住在全局范围添加(默认授权),以防止对资源发出任何匿名请求。config.Filters.Add(new AuthorizeAttribute());

您可以使用 Postman 来测试演示:

请求令牌(正如我上面提到的幼稚,仅用于演示):

GET http://localhost:{port}/api/token?username=cuong&password=1

将 JWT 令牌放在授权请求的标头中,例如:

GET http://localhost:{port}/api/value

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s

演示可以在这里找到:https://github.com/cuongle/WebApi.Jwt

分割线

网友回答:

我已经设法以最小的努力实现了它(就像使用 ASP.NET Core 一样简单)。

为此,我使用OWIN文件和库。Startup.csMicrosoft.Owin.Security.Jwt

为了使应用程序命中,我们需要修改:Startup.csWeb.config

<configuration>
  <appSettings>
    <add key="owin:AutomaticAppStartup" value="true" />
    ...

下面是应该如何看起来:Startup.cs

using MyApp.Helpers;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Jwt;
using Owin;

[assembly: OwinStartup(typeof(MyApp.App_Start.Startup))]

namespace MyApp.App_Start
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseJwtBearerAuthentication(
                new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = AuthenticationMode.Active,
                    TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidAudience = ConfigHelper.GetAudience(),
                        ValidIssuer = ConfigHelper.GetIssuer(),
                        IssuerSigningKey = ConfigHelper.GetSymmetricSecurityKey(),
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true
                    }
                });
        }
    }
}

你们中的许多人现在都在使用 ASP.NET Core,所以正如你所看到的,它与我们那里的没有太大区别。

首先这真的让我感到困惑,我正在尝试实现自定义提供程序等。但我没想到事情会这么简单。 只是岩石!OWIN

只有一件事要提 – 在我启用OWIN启动库后停止为我工作(例如,你们中的一些人可能想要为Angular应用程序自动生成打字稿HTTP代理)。NSWag

解决方案也非常简单——我更换了,没有任何进一步的问题。NSWagSwashbuckle


好的,现在共享代码:ConfigHelper

public class ConfigHelper
{
    public static string GetIssuer()
    {
        string result = System.Configuration.ConfigurationManager.AppSettings["Issuer"];
        return result;
    }

    public static string GetAudience()
    {
        string result = System.Configuration.ConfigurationManager.AppSettings["Audience"];
        return result;
    }

    public static SigningCredentials GetSigningCredentials()
    {
        var result = new SigningCredentials(GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256);
        return result;
    }

    public static string GetSecurityKey()
    {
        string result = System.Configuration.ConfigurationManager.AppSettings["SecurityKey"];
        return result;
    }

    public static byte[] GetSymmetricSecurityKeyAsBytes()
    {
        var issuerSigningKey = GetSecurityKey();
        byte[] data = Encoding.UTF8.GetBytes(issuerSigningKey);
        return data;
    }

    public static SymmetricSecurityKey GetSymmetricSecurityKey()
    {
        byte[] data = GetSymmetricSecurityKeyAsBytes();
        var result = new SymmetricSecurityKey(data);
        return result;
    }

    public static string GetCorsOrigins()
    {
        string result = System.Configuration.ConfigurationManager.AppSettings["CorsOrigins"];
        return result;
    }
}

另一个重要方面 – 我通过授权标头发送了JWT令牌,因此打字稿代码如下所示:

(以下代码由NSWag生成)

@Injectable()
export class TeamsServiceProxy {
    private http: HttpClient;
    private baseUrl: string;
    protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;

    constructor(@Inject(HttpClient) http: HttpClient, @Optional() @Inject(API_BASE_URL) baseUrl?: string) {
        this.http = http;
        this.baseUrl = baseUrl ? baseUrl : "https://localhost:44384";
    }

    add(input: TeamDto | null): Observable<boolean> {
        let url_ = this.baseUrl + "/api/Teams/Add";
        url_ = url_.replace(/[?&]$/, "");

        const content_ = JSON.stringify(input);

        let options_ : any = {
            body: content_,
            observe: "response",
            responseType: "blob",
            headers: new HttpHeaders({
                "Content-Type": "application/json", 
                "Accept": "application/json",
                "Authorization": "Bearer " + localStorage.getItem('token')
            })
        };

请参阅标题部分 –"Authorization": "Bearer " + localStorage.getItem('token')

分割线

网友回答:

下面是在 ASP.NET 核心 Web API 中使用 JWT 令牌的基于声明的身份验证的非常最小且安全的实现。

首先,需要公开一个终结点,该终结点返回 JWT 令牌,其中包含分配给用户的声明:

 /// <summary>
        /// Login provides API to verify user and returns authentication token.
        /// API Path:  api/account/login
        /// </summary>
        /// <param name="paramUser">Username and Password</param>
        /// <returns>{Token: [Token] }</returns>
        [HttpPost("login")]
        [AllowAnonymous]
        public async Task<IActionResult> Login([FromBody] UserRequestVM paramUser, CancellationToken ct)
        {

            var result = await UserApplication.PasswordSignInAsync(paramUser.Email, paramUser.Password, false, lockoutOnFailure: false);

            if (result.Succeeded)
            {
                UserRequestVM request = new UserRequestVM();
                request.Email = paramUser.Email;


                ApplicationUser UserDetails = await this.GetUserByEmail(request);
                List<ApplicationClaim> UserClaims = await this.ClaimApplication.GetListByUser(UserDetails);

                var Claims = new ClaimsIdentity(new Claim[]
                                {
                                    new Claim(JwtRegisteredClaimNames.Sub, paramUser.Email.ToString()),
                                    new Claim(UserId, UserDetails.UserId.ToString())
                                });


                //Adding UserClaims to JWT claims
                foreach (var item in UserClaims)
                {
                    Claims.AddClaim(new Claim(item.ClaimCode, string.Empty));
                }

                var tokenHandler = new JwtSecurityTokenHandler();
                  // this information will be retrived from you Configuration
                //I have injected Configuration provider service into my controller
                var encryptionkey = Configuration["Jwt:Encryptionkey"];
                var key = Encoding.ASCII.GetBytes(encryptionkey);
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Issuer = Configuration["Jwt:Issuer"],
                    Subject = Claims,

                // this information will be retrived from you Configuration
                //I have injected Configuration provider service into my controller
                    Expires = DateTime.UtcNow.AddMinutes(Convert.ToDouble(Configuration["Jwt:ExpiryTimeInMinutes"])),

                    //algorithm to sign the token
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)

                };

                var token = tokenHandler.CreateToken(tokenDescriptor);
                var tokenString = tokenHandler.WriteToken(token);

                return Ok(new
                {
                    token = tokenString
                });
            }

            return BadRequest("Wrong Username or password");
        }

现在,您需要在启动内部将身份验证添加到服务中.cs将 JWT 身份验证添加为默认身份验证服务,如下所示:ConfigureServices

services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
             .AddJwtBearer(cfg =>
             {
                 cfg.RequireHttpsMetadata = false;
                 cfg.SaveToken = true;
                 cfg.TokenValidationParameters = new TokenValidationParameters()
                 {
                     //ValidateIssuerSigningKey = true,
                     IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JWT:Encryptionkey"])),
                     ValidateAudience = false,
                     ValidateLifetime = true,
                     ValidIssuer = configuration["Jwt:Issuer"],
                     //ValidAudience = Configuration["Jwt:Audience"],
                     //IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Key"])),
                 };
             });

现在,您可以像这样向授权服务添加策略:

services.AddAuthorization(options =>
            {
                options.AddPolicy("YourPolicyNameHere",
                                policy => policy.RequireClaim("YourClaimNameHere"));
            });

或者,您也可以(不是必需的)从数据库中填充所有声明,因为这只会在应用程序启动时运行一次,并将它们添加到如下所示的策略中:

  services.AddAuthorization(async options =>
            {
                var ClaimList = await claimApplication.GetList(applicationClaim);
                foreach (var item in ClaimList)
                {                        
                    options.AddPolicy(item.ClaimCode, policy => policy.RequireClaim(item.ClaimCode));                       
                }
            });

现在,您可以将策略筛选器放在要授权的任何方法上,如下所示:

 [HttpPost("update")]
        [Authorize(Policy = "ACC_UP")]
        public async Task<IActionResult> Update([FromBody] UserRequestVM requestVm, CancellationToken ct)
        {
//your logic goes here
}

希望这有帮助

模板简介:该模板名称为【ASP.NET Web API 的 JWT 身份验证】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【C#】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 239次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 04-18
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
微信图片 单页式简历模板 自适应 响应式 html5 企业网站 微信模板 微信文章 微信公众平台 微信素材
您可能会喜欢的其他模板