鉴权流程揭秘
传统的授权方式
Token校验
信任问题:
AuthorizetionServer:鉴权;
Api:授权
Api不能访问AuthorizetionServer;
Api要授权:
1.必须要确定这个token(令牌)来自于鉴权中心;
2.如果是来自于鉴权中心;还要判断token是否有效;
要实现这两点就可以解决信任问题?
如何知道Token是来自于鉴权中心
如何解决信任问题;
3.1. 对称可逆加密: 一组Key, 加密Key就是解秘Key; 鉴权中心和Api、第三方Api都是同一个key; 保密性会差一些;
如: 请求鉴权中心===鉴权中心通过Key解秘==token==客户端,客户端带着Token去请求Api/第三方的Api, Api方法或者第三方Api都有解密Key; Api就解密,如果能够解开:说明: 这个Token是来自于鉴权中心的 ==有权限;如果解不开,就没有权限;
3.2. 非对称可逆加密: 也是一组Key, 两个不同的Key; 二者不能相互推导;
一个作为加密Key,一个作为解密Key;
3.3. 公钥私钥:
1.如果加密Key公开———内容的安全性(只有特定的人才有解密key)
2.如果解密Key公开———防止抵赖(只要可以解开就可以,追朔来源)
3.4 Jwt:对称可逆加密—-性能高——安全性差;
非对称可逆加密—-安全性好—-性能差;
3.5. 如何选用?
如果是在内网,鉴权授权可以使用对称可逆加密;
如果是公网,鉴权授权建议使用非对称可逆加密
加密技术
4.1. Des 对称可逆加密 :
对称可逆加密: 就是一个key,key可以加密,也必须使用这个key才能够解密
鉴权中心:key1
Api方法:key1 鉴权中心和api是同一个key;
用户在鉴权中心登录成功以后,可以使用key1加密;得到一段密文;
用户访问Api的时候,Api方就使用ke1去解密:
1.如果解密成功—-防止抵赖(密文一定是来自于鉴权中心),就进一步验证token是否过期,如果过期就拦截,如果没有过期,就放行;
2.如果不成功,则无效
4.2. Rsa 非对称可逆加密
实际开发中,不会使用对称可逆加密,而是非堆成可逆加密;
1.
JWT: 一对key, 二者无法相互推导;
1. 私钥、公钥;
私钥——-鉴权中心;作为加密Key(加密)
1. 公钥-----Api方法;所谓揭秘key; (解密)
1. Api方法只要是通过公钥能够解开密文;说明token是来自于鉴权中心;
JWT-Json Web Token
官网:https://jwt.io/
1 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,允许用户访问该令牌允许的路由,服务和资源。Single Sign On是一种现在广泛使用JWT的功能,因为它的开销很小,并且能够在不同的域中轻松使用。
2 信息交换:JSON Web令牌是在各方之间安全传输信息的好方法。因为JWT可以签名 - 例如,使用公钥/私钥对 - 您可以确定发件人是他们所说的人。此外,由于使用标头和有效负载计算签名,您还可以验证内容是否未被篡改。
JWT-Token组成
单点登录
一个地方登录了,其他地方也就可登录,和多登录互踢有区别 !!!
JWT鉴权授权的多种实现
对称性加密
- 新建一个项目称为授权中心: AuthenticationCenter
- 修改JSON配置文件 ```json { “Logging”: { “LogLevel”: { “Default”: “Information”, “Microsoft”: “Warning”, “Microsoft.Hosting.Lifetime”: “Information” } }, “AllowedHosts”: “*”, “JWTTokenOptions”: { “Audience”: “http://localhost:5200“, “Issuer”: “http://localhost:5200“, “SecurityKey”: “MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI2a2EJ7m872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI543nNDAPfnJsas96mSA7L/mD7RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB” } }
3. 注入验证配置
```json
public class JWTTokenOptions
{
public string Audience
{
get;
set;
}
public string SecurityKey
{
get;
set;
}
public string Issuer
{
get;
set;
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ICustomJWTService, CustomHSJWTService>();
services.Configure<JWTTokenOptions>(this.Configuration.GetSection("JWTTokenOptions"));
}
新增配置类
public interface ICustomJWTService
{
string GetToken(string UserName, string password);
}
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("NickName",UserName),
new Claim("Role","Administrator"),//传递其他信息
new Claim("password",password) //传递其他信息
};
//需要加密:需要加密key:
SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_JWTTokenOptions.SecurityKey));
SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
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
}
}
新增项目: Wrebapi ,修改配置 ```csharp public void ConfigureServices(IServiceCollection services) {
JWTTokenOptions tokenOptions = new JWTTokenOptions();
Configuration.Bind("JWTTokenOptions", tokenOptions);
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
//AudienceValidator = (m, n, z) =>
//{
// //等同于去扩展了下Audience的校验规则---鉴权
// return m != null && m.FirstOrDefault().Equals(this.Configuration["audience"]);
//},
//LifetimeValidator = (notBefore, expires, securityToken, validationParameters) =>
//{
// return notBefore <= DateTime.Now
// && expires >= DateTime.Now;
// //&& validationParameters
//}//自定义校验规则
};
});
}
<a name="vb0Yt"></a>
#### 非对称性加密
1. 修改认证中心配置
```csharp
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ICustomJWTService, CustomRSSJWTervice>();
services.Configure<JWTTokenOptions>(this.Configuration.GetSection("JWTTokenOptions"));
}
2.新增配置类
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)
{
//string jtiCustom = Guid.NewGuid().ToString();//用来标识 Token
Claim[] claims = new[]
{
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role,"admin"),
new Claim("password",password)
};
string keyDir = Directory.GetCurrentDirectory();
if (RSAHelper.TryGetKeyParameters(keyDir, true, out RSAParameters keyParams) == false)
{
keyParams = RSAHelper.GenerateAndSaveKey(keyDir);
}
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;
}
}
3.新增公钥私钥生成类
public class RSAHelper
{
/// <summary>
/// 从本地文件中读取用来签发 Token 的 RSA Key
/// </summary>
/// <param name="filePath">存放密钥的文件夹路径</param>
/// <param name="withPrivate"></param>
/// <param name="keyParameters"></param>
/// <returns></returns>
public static bool TryGetKeyParameters(string filePath, bool withPrivate, out RSAParameters keyParameters)
{
string filename = withPrivate ? "key.json" : "key.public.json";
string fileTotalPath = Path.Combine(filePath, filename);
keyParameters = default(RSAParameters);
if (!File.Exists(fileTotalPath))
{
return false;
}
else
{
keyParameters = JsonConvert.DeserializeObject<RSAParameters>(File.ReadAllText(fileTotalPath));
return true;
}
}
/// <summary>
/// 生成并保存 RSA 公钥与私钥
/// </summary>
/// <param name="filePath">存放密钥的文件夹路径</param>
/// <returns></returns>
public static RSAParameters GenerateAndSaveKey(string filePath, bool withPrivate = true)
{
RSAParameters publicKeys, privateKeys;
using (var rsa = new RSACryptoServiceProvider(2048))//即时生成
{
try
{
privateKeys = rsa.ExportParameters(true);
publicKeys = rsa.ExportParameters(false);
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
File.WriteAllText(Path.Combine(filePath, "key.json"), JsonConvert.SerializeObject(privateKeys));
File.WriteAllText(Path.Combine(filePath, "key.public.json"), JsonConvert.SerializeObject(publicKeys));
return withPrivate ? privateKeys : publicKeys;
}
}
4.请求token后会主动生成公钥和私钥, 然后把公钥复制到webapi项目下,即可访问该api
5.修改webapi配置
#region 读取公钥
string path = Path.Combine(Directory.GetCurrentDirectory(), "key.public.json");
string key = File.ReadAllText(path);//this.Configuration["SecurityKey"];
Console.WriteLine($"KeyPath:{path}");
var keyParams = JsonConvert.DeserializeObject<RSAParameters>(key);
SigningCredentials credentials = new SigningCredentials(new RsaSecurityKey(keyParams), SecurityAlgorithms.RsaSha256Signature);
#endregion
JWTTokenOptions tokenOptions = new JWTTokenOptions();
Configuration.Bind("JWTTokenOptions", tokenOptions);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,//是否验证Issuer
ValidateAudience = true,//是否验证Audience
ValidateLifetime = true,//是否验证失效时间
ValidateIssuerSigningKey = true,//是否验证SecurityKey
ValidAudience = tokenOptions.Audience,//Audience
ValidIssuer = tokenOptions.Issuer,//Issuer,这两项和前面签发jwt的设置一致
IssuerSigningKey = new RsaSecurityKey(keyParams),
//IssuerSigningKeyValidator = (m, n, z) =>
// {
// Console.WriteLine("This is IssuerValidator");
// return true;
// },
//IssuerValidator = (m, n, z) =>
// {
// Console.WriteLine("This is IssuerValidator");
// return "http://localhost:5726";
// },
//AudienceValidator = (m, n, z) =>
//{
// Console.WriteLine("This is AudienceValidator");
// return true;
// //return m != null && m.FirstOrDefault().Equals(this.Configuration["Audience"]);
//},//自定义校验规则,可以新登录后将之前的无效
};
});