• Asp.Net Core 6 之 基于Jwt 的身份认证


    程序集

    Microsoft.AspNetCore.Authentication.JwtBearer;
    

    身份认证服务器

    jwt配置类: JWTTokenOptions.cs

    public class JWTTokenOptions
        {
            public string Audience
            {
                get;
                set;
            }
            public string SecurityKey
            {
                get;
                set;
            }
            //public SigningCredentials Credentials
            //{
            //    get;
            //    set;
            //}
            public string Issuer
            {
                get;
                set;
            }
        }
    
    

    apppsetting.json

    
      "JWTTokenOptions": {
        "Audience": "http://localhost:5200",
        "Issuer": "http://localhost:5200",
        "SecurityKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADxxxx"
      }
    
    

    生成 token

         [Route("api/[controller]")]
        [ApiController]
        public class AuthenticationController : ControllerBase
        {
            private ICustomJWTService _iJWTService = null;
            public AuthenticationController(ICustomJWTService customJWTService)
            {
                _iJWTService = customJWTService;
            }
    
            [Route("Login")]
            [HttpPost]
            public string Login(string name, string password)
            {
                //在这里需要去数据库中做数据验证
                if ("admin".Equals(name) && "123456".Equals(password))
                {
                    //就应该生成Token 
                    string token = this._iJWTService.GetToken(name, password);
                    return JsonConvert.SerializeObject(new
                    {
                        result = true,
                        token
                    });
    
                }
                else
                {
                    return JsonConvert.SerializeObject(new
                    {
                        result = false,
                        token = ""
                    });
                }
    

    对称加密

    using Microsoft.Extensions.Options;
    using Microsoft.IdentityModel.Tokens;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using System.Text;
    
        public class CustomHSJWTService : ICustomJWTService
        {
            #region Option注入
            private readonly JWTTokenOptions _JWTTokenOptions;
            public CustomHSJWTService(IOptionsMonitor<JWTTokenOptions> jwtTokenOptions)
            {
                this._JWTTokenOptions = jwtTokenOptions.CurrentValue;
            }
            #endregion
            /// <summary>
            /// 用户登录成功以后,用来生成Token的方法
            /// </summary>
            /// <param name="UserName"></param>
            /// <returns></returns>
            public string GetToken(string UserName, string password)
            {
                #region 有效载荷,大家可以自己写,爱写多少写多少;尽量避免敏感信息
                var claims = new[]
                {
                   new Claim(ClaimTypes.Name, UserName),
                    new Claim(ClaimTypes.Role, "teache0"),
                   new Claim("NickName",UserName),
                   new Claim("Role","Administrator"),//传递其他信息   
                   new Claim("ABCC","ABCC"),
                   new Claim("Student","甜酱油")
                };
    
                //需要加密:需要加密key:
                //Nuget引入:Microsoft.IdentityModel.Tokens
                SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_JWTTokenOptions.SecurityKey));
    
                SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
                //Nuget引入:System.IdentityModel.Tokens.Jwt
                JwtSecurityToken token = new JwtSecurityToken(
                 issuer: _JWTTokenOptions.Issuer,
                 audience: _JWTTokenOptions.Audience,
                 claims: claims,
                 expires: DateTime.Now.AddMinutes(5),//5分钟有效期
                 signingCredentials: creds);
    
                string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
                return returnToken;
                #endregion
    
            }
        }
    

    非对称加密

     public class CustomRSSJWTervice : ICustomJWTService
     {
            #region Option注入
            private readonly JWTTokenOptions _JWTTokenOptions;
            public CustomRSSJWTervice(IOptionsMonitor<JWTTokenOptions> jwtTokenOptions)
            {
                this._JWTTokenOptions = jwtTokenOptions.CurrentValue;
            }
            #endregion
    
            public string GetToken(string userName, string password)
            {
                #region 使用加密解密Key  非对称 
                string keyDir = Directory.GetCurrentDirectory();
                if (RSAHelper.TryGetKeyParameters(keyDir, true, out RSAParameters keyParams) == false)
                {
                    keyParams = RSAHelper.GenerateAndSaveKey(keyDir);
                }
                #endregion
    
                //string jtiCustom = Guid.NewGuid().ToString();//用来标识 Token
                Claim[] claims = new[]
                {
                       new Claim(ClaimTypes.Name, userName),
                       new Claim(ClaimTypes.Role,"admin"),
                       new Claim("password",password)
                };
    
                SigningCredentials credentials = new SigningCredentials(new RsaSecurityKey(keyParams), SecurityAlgorithms.RsaSha256Signature);
    
                var token = new JwtSecurityToken(
                   issuer: this._JWTTokenOptions.Issuer,
                   audience: this._JWTTokenOptions.Audience,
                   claims: claims,
                   expires: DateTime.Now.AddMinutes(60),//5分钟有效期
                   signingCredentials: credentials);
    
                var handler = new JwtSecurityTokenHandler();
                string tokenString = handler.WriteToken(token);
                return tokenString;
            }
        }
    

    受保护WebApi

    执行身份验证

    Program.cs

    //跨域
    builder.Services.AddCors(policy =>
    {
    policy.AddPolicy("CorsPolicy", opt => opt
    .AllowAnyOrigin()
    .AllowAnyHeader()
    .AllowAnyMethod()
    .WithExposedHeaders("X-Pagination"));
    });
    
    {
        #region jwt校验  HS(对称加密)
        {
            //第二步,增加鉴权逻辑
            JWTTokenOptions tokenOptions = new JWTTokenOptions();
            builder.Configuration.Bind("JWTTokenOptions", tokenOptions);
            builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)//Scheme
             .AddJwtBearer(options =>  //这里是配置的鉴权的逻辑
             {
                 options.TokenValidationParameters = new TokenValidationParameters
                 {
                     //JWT有一些默认的属性,就是给鉴权时就可以筛选了
                     ValidateIssuer = true,//是否验证Issuer
                     ValidateAudience = true,//是否验证Audience
                     ValidateLifetime = true,//是否验证失效时间
                     ValidateIssuerSigningKey = true,//是否验证SecurityKey
                     ValidAudience = tokenOptions.Audience,//
                     ValidIssuer = tokenOptions.Issuer,//Issuer,这两项和前面签发jwt的设置一致
                     IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOptions.SecurityKey))//拿到SecurityKey 
                 };
    
                 options.Events = new JwtBearerEvents
                 {
                     //通过身份验证后,回调
                     //可以在这里进行登录,设置 HttpContext.Principal
                     OnTokenValidated = async ctx =>
                     {
                         string userName = ctx.Principal.FindFirst(ClaimTypes.Name)?.Value;
                         /*
                         string userName = ctx.Principal.FindUserByUserName(userName);
                         User user = FindFromDb()
                         ctx.Principal = 
                         */
    
                         var claims = new List<Claim>()//鉴别你是谁,相关信息
                        {
                            new Claim(ClaimTypes.Name,userName),
     
                            new Claim("Userid","1"),
                            new Claim(ClaimTypes.Role,"Admin"),
                            new Claim(ClaimTypes.Role,"User"),
                            new Claim(ClaimTypes.Email,$"xxxx@163.com"),
                            new Claim("password",password),//可以写入任意数据
                            new Claim("Account","Administrator"),
                            new Claim("role","admin"),
                            new Claim("QQ","xxxx")
                        };
                         ClaimsPrincipal userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Customer"));
                         ctx.Principal = userPrincipal;
                     }
                 };
             });
    
            
        }
        #endregion
    }
    
    
    var app = builder.Build();
    
    app.UseAuthentication();
    app.UseAuthorization();
    
    

    重点: 使用

    options.Events = new JwtBearerEvents
                 {
                     //通过身份验证后,回调
                     //可以在这里进行登录,设置 HttpContext.Principal
                     OnTokenValidated = async ctx =>
                     {
                         string userName = ctx.Principal.FindFirst(ClaimTypes.Name)?.Value;
                         /*
                         string userName = ctx.Principal.FindUserByUserName(userName);
                         User user = FindFromDb()
                         ctx.Principal = 
                         */
    
                         var claims = new List<Claim>()//鉴别你是谁,相关信息
                        {
                            new Claim(ClaimTypes.Name,userName),
     
                            new Claim("Userid","1"),
                            new Claim(ClaimTypes.Role,"Admin"),
                            new Claim(ClaimTypes.Role,"User"),
                            new Claim(ClaimTypes.Email,$"xxxx@163.com"),
                            new Claim("password",password),//可以写入任意数据
                            new Claim("Account","Administrator"),
                            new Claim("role","admin"),
                            new Claim("QQ","xxxx")
                        };
                         ClaimsPrincipal userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Customer"));
                         ctx.Principal = userPrincipal;
                     }
    

    在通过身份验证后,回调设置当前用户信息,

         ClaimsPrincipal userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Customer"));
         ctx.Principal = userPrincipal;
    

    以便无缝对接当前基于用户 Claime 设置的其它授权策略,方案等。
    相当于 使用 token 身份验证成功后,再执行 UserName + Password 登录的逻辑.
    当然,这一步根据实际情况取舍。

    保护端点,Contronller或 Action

       public class SecondController : ControllerBase
       { 
            [HttpGet]
            [Authorize(AuthenticationSchemes= JwtBearerDefaults.AuthenticationScheme)]
            public object GetData()
            {
                Console.WriteLine("请求到了~~");
                return new
                {
                    Id = 123,
                    Name = "Richard"
                };
            }
    

    Artisan.Zo 中的示例

    配置类

    JwtOptions

        public class JwtOptions
        {
            public string Audience { get; set; }
            public string SecurityKey { get; set; }
            public string Issuer { get; set; }
            public int ExpirationTime { get; set; }
            public bool RequireHttps { get; set; } 
            
    

    Artisan.Zo.Applicaiton

    AccountAppService.cs

    using Artisan.Zo.Auths.Jwt;
    using Artisan.Zo.Dtos;
    using IdentityModel;
    using Microsoft.Extensions.Options;
    using Microsoft.IdentityModel.Tokens;
    using System;
    using System.Collections.Generic;
    using System.IdentityModel.Tokens.Jwt;
    using System.Linq;
    using System.Security.Claims;
    using System.Text;
    using System.Threading.Tasks;
    using Volo.Abp;
    using Volo.Abp.Identity;
    using Volo.Abp.Security.Claims;
    using AspNetCore = Microsoft.AspNetCore.Identity;
    
    namespace Artisan.Zo.Account
    {
        public class AccountAppService : ZoAppServiceBase, IAccountAppService
        {
            private readonly IdentityUserManager userManager;
            private readonly AspNetCore.SignInManager<IdentityUser> signInManager;
    
            //jwt
            private readonly JwtOptions jwtOptions;
            private readonly IJwtGenerator jwtGenerator;
    
            public AccountAppService(
                IdentityUserManager userManager,
                AspNetCore.SignInManager<IdentityUser> signInManager, 
                IOptionsSnapshot<JwtOptions> jwtOptions)
            {
                this.userManager = userManager;
                this.signInManager = signInManager;
                this.jwtOptions = jwtOptions.Value;
            }
    
            public virtual async Task<LoginDto> LoginAsync(LoginInput input)
            {
                var result = await signInManager.PasswordSignInAsync(input.Name, input.Password, false, true);
                if (result.Succeeded)
                {
                    var user = await userManager.FindByNameAsync(input.Name);
                    return await BuildLoginResult(user);
                }
                else
                {
                    if (result.IsLockedOut)
                    {
                        throw new UserFriendlyException(L["DisplayName:LockoutEnabled"]);
                    }else 
                    {
                        throw new UserFriendlyException(L["InvalidUserNameOrPassword"]);
                    }
                }
            }
    
    
            private async Task<LoginDto> BuildLoginResult(IdentityUser user)
            {
                if (user.LockoutEnabled) {
                    throw new UserFriendlyException(L["DisplayName:LockoutEnabled"]);
                } 
                var roles = await userManager.GetRolesAsync(user);
                var token = jwtGenerator.GenerateToken(new CreateJwtTokenInput { IdentityUser = user, JwtOptions = jwtOptions, Roles = roles});
               
                var LoginDto = ObjectMapper.Map<IdentityUser, LoginDto>(user);
                LoginDto.Token = token;
                LoginDto.Roles = roles; 
    
                return LoginDto;
            }
        }
    }
    
    

    JwtHsGenerator.cs 对称加密算法token

    using IdentityModel;
    using Microsoft.Extensions.Options;
    using Microsoft.IdentityModel.Tokens;
    using System;
    using System.Collections.Generic;
    using System.IdentityModel.Tokens.Jwt;
    using System.Linq;
    using System.Security.Claims;
    using System.Text;
    using Volo.Abp.Identity;
    using Volo.Abp.Security.Claims;
    
    namespace Artisan.Zo.Auths.Jwt
    {
        /// <summary>
        /// 对称加密
        /// </summary>
        public class JwtHsGenerator : IJwtGenerator
        {
            public string GenerateToken(CreateJwtTokenInput input)
            {
                IdentityUser user = input.IdentityUser;
                IList<string> roles = input.Roles;
                JwtOptions jwtOptions = input.JwtOptions;
    
                var dateNow = DateTime.Now;
                var expirationTime = dateNow + TimeSpan.FromHours(jwtOptions.ExpirationTime);
                var key = Encoding.ASCII.GetBytes(jwtOptions.SecurityKey);
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, user.UserName),
                };
    
                var tokenDescriptor = new SecurityTokenDescriptor()
                {
                    Subject = new ClaimsIdentity(claims),
                    Expires = expirationTime,
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                };
    
                var handler = new JwtSecurityTokenHandler();
                var token = handler.CreateToken(tokenDescriptor);
    
                return handler.WriteToken(token);
            }
    
            //public string GenerateToken(CreateJwtTokenInput input)
            //{
            //    IdentityUser user = input.IdentityUser;
            //    IList<string> roles = input.Roles;
            //    JwtOptions jwtOptions = input.JwtOptions;
    
            //    var dateNow = DateTime.Now;
            //    var expirationTime = dateNow + TimeSpan.FromHours(jwtOptions.ExpirationTime);
            //    var key = Encoding.ASCII.GetBytes(jwtOptions.SecurityKey);
    
            //    var claims = new List<Claim>
            //    {
            //        new Claim(JwtClaimTypes.Audience, jwtOptions.Audience),
            //        new Claim(JwtClaimTypes.Issuer, jwtOptions.Issuer),
            //        new Claim(AbpClaimTypes.UserId, user.Id.ToString()),
            //        new Claim(AbpClaimTypes.Name, user.Name),
            //        new Claim(AbpClaimTypes.UserName, user.UserName),
            //        new Claim(AbpClaimTypes.Email, user.Email),
            //        new Claim(AbpClaimTypes.TenantId, user.TenantId.ToString())
            //    };
    
            //    if (roles != null && roles.Any())
            //    {
            //        foreach (var item in roles)
            //        {
            //            claims.Add(new Claim(JwtClaimTypes.Role, item));
            //        }
            //    }
    
            //    var tokenDescriptor = new SecurityTokenDescriptor()
            //    {
            //        Subject = new ClaimsIdentity(claims),
            //        Expires = expirationTime,
            //        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            //    };
            //    var handler = new JwtSecurityTokenHandler();
            //    var token = handler.CreateToken(tokenDescriptor);
    
            //    return handler.WriteToken(token);
            //}
    
        }
    }
    
    

    WebApi.Host

    appsetting.json

      "Auth": {
        "Jwt": {
          //客户端标识
          "Audience": "ArtisanZoApiHost",
          "SecurityKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNAD=",
          //签发者
          "Issuer": "ArtisanZo",
          //过期时间,单位:小时
          "ExpirationTime": 24,
          "RequireHttps": false
        }
    
    
            private void ConfigureOptions(ServiceConfigurationContext context, IConfiguration configuration)
            {
                context.Services.Configure<JwtOptions>(context.Services.GetConfiguration()
                    .GetSection("Auth:Jwt"));
            }
    
           private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
            {
                //
                //context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                //    .AddJwtBearer(options =>
                //    {
                //        options.Authority = configuration["AuthServer:Authority"];
                //        options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
                //        options.Audience = "Zo";
                //    });
    
                var jwtOption = configuration.GetSection("Auth:Jwt").Get<JwtOptions>();
    
                context.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options =>{
                    options.RequireHttpsMetadata = jwtOption.RequireHttps;
                    options.SaveToken = true;
                    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        //ClockSkew = TimeSpan.Zero,
                        ValidIssuer = jwtOption.Issuer,
                        ValidAudience = jwtOption.Audience,
                        IssuerSigningKey =
                                    new SymmetricSecurityKey(
                                        Encoding.ASCII.GetBytes(jwtOption.SecurityKey))
                    };
                    options.Events = new JwtBearerEvents
                    {
                        OnTokenValidated = async ctx =>
                        {
                            var services = ctx.HttpContext.RequestServices;
                            var userManager = services.GetRequiredService<IdentityUserManager>();
                            var signInManager = services.GetRequiredService<AbpSignInManager>();
    
                            string name = ctx.Principal.FindFirst(ClaimTypes.Name)?.Value;
                            IdentityUser identityUser = await userManager.FindByNameAsync(name);
                            //构建jwt令牌和AspNet Core Identity数据的桥梁,
                            //确保像基于角色的授权这样的特性无缝地工作
                            ctx.Principal = await signInManager.CreateUserPrincipalAsync(identityUser);
                        }
                    };
                });
    
            }
    
    
  • 相关阅读:
    Warning:mailcious javascript detected on this domain来由
    CSS盒模型重新理解篇
    sublime生产力提升利器
    Aptana studio 3前端开发编辑器推荐
    Provides PHP completions for Sublime Text
    关于google电子地图跟卫星地图位置不重合
    无名前端库
    npm 编写cli
    webpack.merge
    ExcelDNA UDF 攻略
  • 原文地址:https://www.cnblogs.com/easy5weikai/p/15706555.html
Copyright © 2020-2023  润新知