.Net Core Jwt验证

1.准备

  新建一个.Net Core项目,添加Jwt控制器

2.JwtController

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace StudyToken2.Controllers
{
    [Route("api/Token")]
    public class JwtController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
        [HttpPost]
        public IActionResult RequestToken([FromBody] TokenRequest request)
        {
            string SecurityKey = "MySecurityKey!!kxy";
            if (request.Username == "AngelaDaddy" && request.Password == "123456")
            {
                // push the user’s name into a claim, so we can identify the user later on.
                var claims = new[]
                {
                   new Claim(ClaimTypes.Name, request.Username),//账号
                   new Claim(ClaimTypes.Role, request.Role),//角色
                };
                //sign the token using a secret key.This secret will be shared between your API and anything that needs to check that the token is legit.
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                //.NET Core’s JwtSecurityToken class takes on the heavy lifting and actually creates the token.
                /**
                 * Claims (Payload)
                    Claims 部分包含了一些跟这个 token 有关的重要信息。 JWT 标准规定了一些字段,下面节选一些字段:

                    iss: The issuer of the token,token 是给谁的
                    sub: The subject of the token,token 主题
                    exp: Expiration Time。 token 过期时间,Unix 时间戳格式
                    iat: Issued At。 token 创建时间, Unix 时间戳格式
                    jti: JWT ID。针对当前 token 的唯一标识
                    除了规定的字段外,可以包含其他任何 JSON 兼容的字段。
                 * */
                var token = new JwtSecurityToken(
                    issuer: "yourdomain.com",
                    audience: "yourdomain.com",
                    claims: claims,
                    expires: DateTime.Now.AddMinutes(30),
                    signingCredentials: creds);

                return Ok(new
                {
                    token = new JwtSecurityTokenHandler().WriteToken(token)
                });
            }

            return BadRequest("Could not verify username and password");
        }
    }
    public class TokenRequest
    {
        public string Username { get; set; }
        public string Password { get; set; }
        public string Role { get; set; }
    }
}

3.配置文件

  ConfigureServices

            //授权
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(options =>
                        {
                            options.RequireHttpsMetadata = false;//是否需要https
                            options.SaveToken = true;//是否将信息存储到token
                            options.TokenValidationParameters = new TokenValidationParameters
                            {
                                ValidateIssuer = false,//是否验证Issuer
                                ValidateAudience = false,//是否验证Audience
                                ValidateLifetime = true,//是否验证失效时间
                                ValidateIssuerSigningKey = true,//是否验证SecurityKey
                                ValidAudience = "http://localhost:5000",//Audience
                                ValidIssuer = "http://localhost:5000",//Issuer,这两项和前面签发jwt的设置一致
                                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("VertivSecurityKey001")),//拿到SecurityKey
                            };
                        }
                    );

 

  Configure

            //Token  认证和授权,顺序不能乱
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseStatusCodePages(new StatusCodePagesOptions()
            {
                HandleAsync = (context) =>
                {
                    if (context.HttpContext.Response.StatusCode == 401)
                    {
                        using (System.IO.StreamWriter sw = new System.IO.StreamWriter(context.HttpContext.Response.Body))
                        {
                            sw.Write(Newtonsoft.Json.JsonConvert.SerializeObject(new
                            {
                                status = 401,
                                message = "未授权",
                            }));
                        }
                    }
                    if (context.HttpContext.Response.StatusCode == 403)
                    {
                        using (System.IO.StreamWriter sw = new System.IO.StreamWriter(context.HttpContext.Response.Body))
                        {
                            sw.Write(Newtonsoft.Json.JsonConvert.SerializeObject(new
                            {
                                status = 403,
                                message = "Role不匹配",
                            }));
                        }
                    }
                    return Task.Delay(0);
                }
            });

 

4.接口测试

  在Home中添加接口

        [HttpGet("ceshi2")]
        [Authorize(Roles = "mall,tenant")]//可以不指定角色
        public string ceshi2()
        {
            return "访问成功!";
        }

  为了方便测试,我再控制器上加上了路由规则

    [Route("api/[controller]")]

  [AllowAnonymous] 可以跳过验证

5.postman测试

  1)获取Token

https://localhost:44346/api/Token

  raw参数

{
    "Username":"AngelaDaddy",
    "Password":"123456",
    "Role":"mall"
}

  生成结果

{
    "token": "mytoken"
}

  2)使用token访问接口ceshi2

https://localhost:44346/api/home/ceshi2

  Header

Authorization:Bearer mytoken

 6.附上控制器扩展函数

    public static class ControllerExt
    {
        /// <summary>
        /// 是商管账号登录了
        /// </summary>
        /// <param name="controller"></param>
        /// <returns></returns>
        public static bool IsMall(this Controller controller) => controller.User?.Identity != null && controller.User.Claims.Any(c => c.Type == ClaimTypes.Role && c.Value == "mall");

        /// <summary>
        /// 是租户账号登录了
        /// </summary>
        /// <param name="controller"></param>
        /// <returns></returns>
        public static bool IsTenant(this Controller controller) => controller.User?.Identity != null && controller.User.Claims.Any(c => c.Type == ClaimTypes.Role && c.Value == "tenant");

        /// <summary>
        /// 租户账号体系下的账号ID
        /// </summary>
        /// <param name="controller"></param>
        /// <returns></returns>
        public static string TenantAccountCode(this Controller controller) => controller.IsTenant() ? controller.User.Identity.Name : null;

        /// <summary>
        /// 商场账号体系下的账号
        /// </summary>
        /// <param name="controller"></param>
        /// <returns></returns>
        public static string MallAccountCode(this Controller controller) => controller.IsMall() ? controller.User.Identity.Name : null;

    }

 7.将StartUp的配置抽离出来

  1)ConfigureServices 部分

  新建一个类:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace JwtHelperCore
{//扩展方法
    public static class JwtServiceExtensions
    {
        public static AuthenticationBuilder AddJwtService(this IServiceCollection services)
        {
            //lambda跑了异步,数据加载不到,直接写函数体,GetJwtOptions作废
            //return services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            //    .AddJwtBearer(options => JwtService.GetJwtOptions());
            return services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.RequireHttpsMetadata = false;//是否需要https
                    options.SaveToken = true;//是否将信息存储到token
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = false,//是否验证Issuer
                        ValidateAudience = false,//是否验证Audience
                        ValidateLifetime = true,//是否验证失效时间
                        ValidateIssuerSigningKey = true,//是否验证SecurityKey
                        ValidAudience = "http://localhost:5000",//Audience
                        ValidIssuer = "http://localhost:5000",//Issuer,这两项和前面签发jwt的设置一致
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("VertivSecurityKey001")),//拿到SecurityKey
                    };
                }
                );
        }
    }
}

  在ConfigureServices 添加

            //Token 授权
            services.AddJwtService();

  2)Configure 部分

  新建一个类:

using ComLibrary;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;

namespace JwtHelperCore
{
    public class ErrorHandlingMiddleware
    {
        private readonly RequestDelegate next;
        public ErrorHandlingMiddleware(RequestDelegate next)
        {
            this.next = next;
        }
        //先跑这里
        public async Task Invoke(HttpContext context)
        {
            try
            {
                await next(context);
            }
            catch (Exception ex)
            {
                var statusCode = context.Response.StatusCode;
                if (ex is ArgumentException)
                {
                    statusCode = 200;
                }
                await HandleExceptionAsync(context, statusCode, ex.Message);
            }
            finally
            {
                var statusCode = context.Response.StatusCode;
                var msg = "";
                if (statusCode == 401)
                {
                    msg = "未授权";
                }
                else if (statusCode == 403)
                {
                    msg = "Role错误";
                }
                else if (statusCode == 404)
                {
                    msg = "未找到服务";
                }
                else if (statusCode == 502)
                {
                    msg = "请求错误";
                }
                else if (statusCode != 200)
                {
                    msg = "未知错误";
                }
                if (!string.IsNullOrWhiteSpace(msg))
                {
                    await HandleExceptionAsync(context, statusCode, msg);
                }
            }
        }
        //异常错误信息捕获,将错误信息用Json方式返回
        private static Task HandleExceptionAsync(HttpContext context, int statusCode, string msg)
        {
            var result = JsonConvert.SerializeObject(new ResultDataModel<string>() { Success = false, Message = msg, ErrCode = statusCode });
            context.Response.ContentType = "application/json;charset=utf-8";
            return context.Response.WriteAsync(result);
        }
    }
    //扩展方法
    public static class ErrorHandlingExtensions
    {
        public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<ErrorHandlingMiddleware>();
        }
    }
}

  在 Configure 添加

            //Token  异常捕捉、授权、认证
            app.UseErrorHandling();
            app.UseAuthentication();
            app.UseAuthorization();

 8.token输入框不显示问题

  不知怎么了,token输入框忽然消失了,通过以下配置可以使用同一的token

   ConfigureServices

            //Swagger
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
                c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "VPart.xml"));
                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
                {
                    Description = "Bearer +{token}",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                });
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    { new OpenApiSecurityScheme
                     {
                        Reference = new OpenApiReference()
                        {
                            Id = "Bearer",
                            Type = ReferenceType.SecurityScheme
                        }
                     }, Array.Empty<string>() }
                });
            });

  效果为:

  

 

posted @ 2019-01-09 18:14  wskxy  阅读(80)  评论(0)    收藏  举报