[toc] # 簡介 - Jwt分為三段 通過遠點分割 1. header => 描述這個token加密方式 2. PlayLoad => 有效載荷,用戶信息+自定義Claims信息Verify 3. Signature => 簽名, (頭部信息base64處理,有效載荷base64處理) + 密鑰 ...
目錄
簡介
- Jwt分為三段 通過遠點分割
- header => 描述這個token加密方式
- PlayLoad => 有效載荷,用戶信息+自定義Claims信息Verify
- Signature => 簽名, (頭部信息base64處理,有效載荷base64處理) + 密鑰
- 示例 :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJFeHRlbmRlZDEiOiLml6Dkv6Hmga8iLCJFeHRlbmRlZDIiOiIiLCJFeHRlbmRlZDMiOiIiLCJFeHRlbmRlZDQiOiIiLCJFeHRlbmRlZDUiOiIiLCIxIjoi57O757uf566h55CG5ZGYIiwiMiI6IueUqOaIt-euoeeQhuWRmCIsImV4cCI6MTY4ODkwMTA2NiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDg4IiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1MDg4In0.7J1J7yWj4ELHJZIwLKnT4RgcMu3rGAX5ACBFfCS0LWM
基於.Net Core 驗證方式
- 生成jwtToken
- 標記[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
- 驗證 Issuer
- 驗證 Audience
- 驗證 SecurityKey
- 驗證自定義驗證
- 驗證完成可以正常訪問介面
驗證的那幾步順序可以直接在自定義驗證中驗證
Jwt獲取Token
引入三方包
<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.31.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.31.0" />
</ItemGroup>
生成Token
using Microsoft.IdentityModel.Tokens;
using Programming.DotNetCore.Function.Entity.Jwt;
using Programming.DotNetCore.Function.Interface.PasswordService;
using Programming.DotNetCore.Function.Password.Entity;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Programming.DotNetCore.Function.Password
{
public class JwtServices : IPassWordService
{
public string GetToken(UserInfo user,JwtConfig jwtConfig)
{
List<Claim> claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName ?? ""),
new Claim("Extended1", user.Extended1 ?? ""),
new Claim("Extended2", user.Extended2 ?? ""),
new Claim("Extended3", user.Extended3 ?? ""),
new Claim("Extended4", user.Extended4 ?? ""),
new Claim("Extended5", user.Extended5 ?? ""),
};
if (user.Role is not null)
{
foreach (var item in user.Role)
{
claims.Add(new Claim(item.Id.ToString(), item.Role));
}
}
if(jwtConfig.SecurityKey == null)
{
throw new Exception("JwtConfig.SecurityKey 不能為空");
}
SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey));
SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
JwtSecurityToken token = new JwtSecurityToken(
issuer:jwtConfig.Issuer,
audience:jwtConfig.Audience,
claims:claims,
expires: DateTime.UtcNow.AddMinutes(jwtConfig.ExpiresMinutes),
signingCredentials:creds
);
string resultToken = new JwtSecurityTokenHandler().WriteToken(token);
return resultToken;
}
}
}
UserInfo
namespace Programming.DotNetCore.Function.Password.Entity
{
public class UserInfo
{
public string? UserName { get; set; }
public List<RoleInfo>? Role { get; set; }
public string? Extended1 { get; set; }
public string? Extended2 { get; set; }
public string? Extended3 { get; set; }
public string? Extended4 { get; set; }
public string? Extended5 { get; set; }
}
}
JwtConfig
namespace Programming.DotNetCore.Function.Entity.Jwt
{
public class JwtConfig
{
public string? Audience { get; set; }
public string? Issuer { get; set; }
public string? SecurityKey { get; set; }
public int ExpiresMinutes { get; set; }
}
}
WebApi測試(獲取Token)
Program.cs
//讀取Jwt配置
builder.Services.Configure<JwtConfig>(builder.Configuration.GetSection("JwtTokenOptions"));
appsetting.json
{
"JwtTokenOptions": {
"Issuer": "http://localhost:5088",
"Audience": "http://localhost:5088",
"SecurityKey": "kq4DY5N1eFJhscOkI7Zp4Nd0WNy9d9AEsN6Yjgdv9OxLyol66tzGBKT_7vwolN7GZ8EDwqJBwccjDJfb81ws5s3sbbP5wUzQ3-PcTSsD-Rueiu2rsOUZwg_NR3RBCwmtouV-832YV2trCjNTawLB1z0LMukWGFNaAJVZ8WdQcrYn6a0ko5oVhZqaHBgsCLEGiqPtoFsiCcrJTz1IvXHk9_cDSr2hwEmSl18GlkOtgCHFH8aidYth3aQHRHuClTi6Y9mYRJtqqK-FNQYq4ZP23DSGZGFejJFTnM9YMpppuTMLklhSGySwX8rfjZ_0L5ac18nHaykTaiC2fvH00W42qQ"
}
}
Controller
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Programming.DotNetCore.Function.Entity.Jwt;
using Programming.DotNetCore.Function.Interface.PasswordService;
using Programming.DotNetCore.Function.Password.Entity;
namespace Cnpc.Com.Ioc.WebApp.Controllers
{
[Route("api/[controller]/[Action]")]
[ApiController]
public class TestJwtController : ControllerBase
{
IPassWordService _passWordService;
JwtConfig _jwtconfig;
public TestJwtController(IPassWordService passWordService,IOptions<JwtConfig> jwtconfig)
{
_passWordService = passWordService;
_jwtconfig = jwtconfig.Value;
}
[HttpGet]
public IActionResult Login(string userName,string passWord)
{
string token = _passWordService.GetToken(new()
{
UserName = userName,
Extended1 = "無信息",
Role = new List<RoleInfo>()
{
new RoleInfo() { Id = "1",Role="系統管理員"} ,
new RoleInfo() { Id = "2",Role="用戶管理員"} ,
}
}, new()
{
Audience = _jwtconfig.Audience,
Issuer= _jwtconfig.Issuer,
SecurityKey= _jwtconfig.SecurityKey,
ExpiresMinutes = 5,
});
return new JsonResult(new { token = token });
}
}
}
.Net Core 驗證(webApi)
Progarm
添加JWT驗證
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = jwtConfig.Issuer, //發行人
ValidateAudience = true,
ValidAudience = jwtConfig.Audience,//訂閱人
ValidateIssuerSigningKey = true,
//對稱加密密鑰
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey!)),
ValidateLifetime = true, //驗證失效時間
ClockSkew = TimeSpan.FromSeconds(30), //過期時間容錯值
RequireExpirationTime = true,
AudienceValidator = (audiences, securityToken, validationParameters) =>
{
return true;
},
LifetimeValidator = (notBefore,expires, securityToken, validationParameters) =>
{
return true;
}
};
});
讀取配置文件
JwtConfig jwtConfig = new JwtConfig();
builder.Configuration.Bind("JwtTokenOptions", jwtConfig);
添加Swagger支持,api右上角可以寫Token
builder.Services.AddSwaggerGen(c =>
{
//添加Jwt驗證設置,添加請求頭信息
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
}
},
new List<string>()
}
});
//放置介面Auth授權按鈕
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "Value Bearer {token}",
Name = "Authorization",//jwt預設的參數名稱
In = ParameterLocation.Header,//jwt預設存放Authorization信息的位置(請求頭中)
Type = SecuritySchemeType.ApiKey
});
}); ;
Contorller
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Programming.DotNetCore.Function.Entity.Jwt;
using Programming.DotNetCore.Function.Interface.PasswordService;
using Programming.DotNetCore.Function.Password.Entity;
namespace Cnpc.Com.Ioc.WebApp.Controllers
{
[Route("api/[controller]/[Action]")]
[ApiController]
public class TestJwtController : ControllerBase
{
[HttpGet]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public IActionResult TestApi()
{
//獲取用戶Claim信息
var user = HttpContext.User.Claims.Select(it => new { it.Type,it.Value});
return new JsonResult(user);
}
}
}
.Net Core 授權
簡介
可以在資料庫中進一步驗證訪問介面的許可權
Program.cs
//jwt 授權
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("JwtPolicy", policy =>
{
//jwt 授權
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
//這裡為自定義授權指定一下類
.AddRequirements(new UserRoleRequirement(JwtBearerDefaults.AuthenticationScheme));
});
});
JwtAuthorization.cs
註意
驗證中涉及到 IUserServices 和 JwtAuthorization, 需要在ioc容器中註冊一下,我這裡使用的是Autofac註冊,如果使用系統自帶的註冊可以這麼寫:
- builder.Services.AddTransient<IUserServices, IUserServices>();
- builder.Services.AddTransient<IAuthorizationHandler, JwtAuthorization>();
Autofac 註冊授權服務
using Autofac;
using Autofac.Extras.DynamicProxy;
using Castle.DynamicProxy;
using Cnpc.Com.Ioc.Bll;
using Cnpc.Com.Ioc.Dal;
using Cnpc.Com.Ioc.IBll;
using Cnpc.Com.Ioc.IDal;
using Cnpc.Com.Ioc.Tools;
using Cnpc.Com.Ioc.WebApp.Authorization;
using Cnpc.Com.Ioc.WebApp.Filter.ActionFilter;
using Microsoft.AspNetCore.Authorization;
using Programming.DotNetCore.Function.Interface.PasswordService;
using Programming.DotNetCore.Function.Password;
namespace WepApiTest.Autofac
{
public class AutofacConfig : Module
{
protected override void Load(ContainerBuilder builder)
{
//ioc
builder.RegisterType<JwtAuthorization>().As<IAuthorizationHandler>();
builder.RegisterType<UserServices>().As<IUserServices>();
}
}
}
using Cnpc.Com.Ioc.IBll;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;
namespace Cnpc.Com.Ioc.WebApp.Authorization
{
public class UserRoleRequirement : IAuthorizationRequirement
{
public string AuthenticateScheme;
public UserRoleRequirement(string authenticateScheme)
{
AuthenticateScheme = authenticateScheme;
}
}
public class JwtAuthorization : AuthorizationHandler<UserRoleRequirement>
{
IUserServices userSercices;
public JwtAuthorization(IUserServices userSercices)
{
this.userSercices = userSercices;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserRoleRequirement requirement)
{
string? userName = context.User.FindFirst(it => it.Type == ClaimTypes.Name)?.Value;
if (userSercices.IsAdmin(userName!))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
}
Controller
註意
唯一需要修改的地方就是這裡, 指定Policy 為 Program.cs 中設置授權方案名稱
- [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme,Policy = "JwtPolicy")]
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Programming.DotNetCore.Function.Entity.Jwt;
using Programming.DotNetCore.Function.Interface.PasswordService;
using Programming.DotNetCore.Function.Password.Entity;
namespace Cnpc.Com.Ioc.WebApp.Controllers
{
[Route("api/[controller]/[Action]")]
[ApiController]
public class TestJwtController : ControllerBase
{
[HttpGet]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme,Policy = "JwtPolicy")]
public IActionResult TestApi()
{
var user = HttpContext.User.Claims.Select(it => new { it.Type,it.Value});
return new JsonResult(user);
}
}
}