代码改变世界

Asp.net Core认证和授权:JWT认证和授权

2019-01-24 21:24  糯米粥  阅读(5374)  评论(0编辑  收藏  举报

JWT验证一般用户移动端,因为它不像cookie验证那样,没有授权跳转到登陆页面

JWT是json web token的简称,在  jwt.io 网址可以看到

 

 

 

 

新建一个API项目,通过postman 可以访问:

 

 JWT在命名空间:using Microsoft.AspNetCore.Authentication.JwtBearer;

添加JWT实体类

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Api.Models
{
    public class JwtSettings
    {
        /// <summary>
        /// Token是谁颁发的
        /// </summary>
        public string Issuer { get; set; }

        /// <summary>
        /// Token给那些客户端去使用
        /// </summary>
        public string Audience { get; set; }

        /// <summary>
        /// 用于加密的key 必须是16个字符以上,要大于128个字节
        /// </summary>
        public string SecetKey { get; set; }
    }
}

 

 

添加配置文件

 

 

 

 

添加JWT认证

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.Configure<JwtSettings>(Configuration);

            var jwtSettings = new JwtSettings();
            Configuration.Bind("JwtSettings", jwtSettings);

            services.AddAuthentication(option => {
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(option=> {
                option.TokenValidationParameters = new TokenValidationParameters {
                    ValidIssuer = jwtSettings.Issuer,
                    ValidAudience = jwtSettings.Audience,
                    IssuerSigningKey=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecetKey))


 /***********************************TokenValidationParameters的参数默认值***********************************/
                    // RequireSignedTokens = true,
                    // SaveSigninToken = false,
                    // ValidateActor = false,
                    // 将下面两个参数设置为false,可以不验证Issuer和Audience,但是不建议这样做。
                    // ValidateAudience = true,
                    // ValidateIssuer = true,
                    // ValidateIssuerSigningKey = false,
                    // 是否要求Token的Claims中必须包含Expires
                    // RequireExpirationTime = true,
                    // 允许的服务器时间偏移量
                    // ClockSkew = TimeSpan.FromSeconds(300),
                    // 是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
                    // ValidateLifetime = true }; }); }

 

添加中间件(Middleware)

 app.UseAuthentication();

 

API接口打上标签:

 

 

 然后在postman访问 就是401 未授权

 

 接下来需要给用户颁发Token

当用户登陆成功后,颁发token

创建登陆API和实体类

namespace Api.Models
{
    public class LoginViewModel
    {
        [Required]
        public string user { get; set; }
        [Required]
        public string pwd { get; set; }
    }
}
using Api.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace Api.Controllers
{
    //[Route("api/[controller]")]
    //[ApiController]
    public class AuthorizeController : ControllerBase
    {
        private JwtSettings _jwtSettings;
        public AuthorizeController(IOptions<JwtSettings> jwtSetting)
        {
            _jwtSettings = jwtSetting.Value;
        }

        [HttpPost]
        public IActionResult Token([FromBody]LoginViewModel login)
        {
            if (ModelState.IsValid)
            {
                if (!(login.user == "cnglgos" && login.pwd == "123"))
                {
                    return BadRequest();
                }
                var claim = new Claim[] {
                    new Claim("name","cnbogs"),
                    new Claim("role","admin")
                };

                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                //neget包:Microsoft.AspNetCore.Authentication.JwtBearer
                //命名空间: System.IdentityModel.Tokens.Jwt;

                //第一种方式
                var token = new JwtSecurityToken(
                    _jwtSettings.Issuer,// Issuer 颁发者,通常为STS服务器地址
                    _jwtSettings.Audience,// Audience Token的作用对象,也就是被访问的资源服务器授权标识
                    claim,
                    DateTime.Now,  //Token生效时间,在此之前不可用
                    DateTime.Now.AddMinutes(30), //Token过期时间,在此之后不可用
                    creds);

                //第二种方式
                var descriptor = new SecurityTokenDescriptor
                {
                    Issuer = _jwtSettings.Issuer,
                    Audience = _jwtSettings.Audience,// Audience Token的作用对象,也就是被访问的资源服务器授权标识
                    Subject = new ClaimsIdentity(claim),
                    NotBefore = DateTime.Now, //Token生效时间,在此之前不可用
                    Expires = DateTime.Now.AddMinutes(30), //Token过期时间,在此之后不可用
                    SigningCredentials = creds,
                    IssuedAt=DateTime.Now //Token颁发时间
                };
                var handler = new JwtSecurityTokenHandler();
                JwtSecurityToken token1 = handler.CreateJwtSecurityToken(descriptor);


                return Ok(new
                {
                    token = new JwtSecurityTokenHandler().WriteToken(token),
                    token1 = handler.WriteToken(token1)
                });
            }
            return BadRequest();
        }

        public IActionResult Index()
        {
            return Ok();
        }
    }
}

 

 

Postman请求

 

 

然后上面的Token 请求 https://localhost:5001/api/values

 

 

从headers可以看到,前缀必须是Bearer

 

 

我们可以自定义Token,必须继承接口:ISecurityTokenValidator

 

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Collections.Generic;
using System.Security.Claims;
namespace Api.Models
{
    public class TokenValidtor : ISecurityTokenValidator
    {
        public bool CanValidateToken => true;

        public int MaximumTokenSizeInBytes { get; set; }

        public bool CanReadToken(string securityToken)
        {
            return true;
        }

        public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
        {
           validatedToken = null;

            var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);

            if (securityToken == "cnblgos")
            {
                var claim = new List<Claim> {
                    new Claim("name","cnblogs"),
                    new Claim("role","admin")
                };
                identity.AddClaims(claim);
            }

            var principal = new ClaimsPrincipal(identity);
            return principal; } } }

然后在StartUp中修改:

  public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            /*
             appsettings.json文件中JwtSettings是单独的一节,
             所以要GetSection方法获取
             */
            services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings"));

            //services.Configure<JwtSettings>(Configuration);

            var jwtSettings = new JwtSettings();
            Configuration.Bind("JwtSettings", jwtSettings);

            services.AddAuthentication(option =>
            {
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(option =>
            {
                //option.TokenValidationParameters = new TokenValidationParameters {
                //    ValidIssuer = jwtSettings.Issuer,
                //    ValidAudience = jwtSettings.Audience,
                //    IssuerSigningKey=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey))



                option.SecurityTokenValidators.Clear();
                option.SecurityTokenValidators.Add(new TokenValidtor());
                option.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var token = context.Request.Headers["token"];
                        context.Token = token.FirstOrDefault();
                        return Task.CompletedTask;
                    }
    
                };
            });

        }

 

 

   //添加Policy和Claim授权
            services.AddAuthorization(options => {
                options.AddPolicy("nsky", policy => policy.RequireClaim("nsky")); });

 

 

 

 

Token可以去jwt.io 网站验证

 

 

 参考大神的文章:https://www.cnblogs.com/RainingNight/p/jwtbearer-authentication-in-asp-net-core.html