鉴权流程揭秘

传统的授权方式

image.png

Token校验

image.png
信任问题: 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. 1.

JWT: 一对key, 二者无法相互推导;

  1. 1. 私钥、公钥;

私钥——-鉴权中心;作为加密Key(加密)

  1. 1. 公钥-----Api方法;所谓揭秘key; (解密)
  2. 1. Api方法只要是通过公钥能够解开密文;说明token是来自于鉴权中心;

JWT-Json Web Token

官网:https://jwt.io/
1 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,允许用户访问该令牌允许的路由,服务和资源。Single Sign On是一种现在广泛使用JWT的功能,因为它的开销很小,并且能够在不同的域中轻松使用。

2 信息交换:JSON Web令牌是在各方之间安全传输信息的好方法。因为JWT可以签名 - 例如,使用公钥/私钥对 - 您可以确定发件人是他们所说的人。此外,由于使用标头和有效负载计算签名,您还可以验证内容是否未被篡改。

JWT-Token组成

image.png
签名:防止被串改

单点登录

一个地方登录了,其他地方也就可登录,和多登录互踢有区别 !!!

JWT鉴权授权的多种实现

对称性加密

  1. 新建一个项目称为授权中心: AuthenticationCenter
  2. 修改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” } }
  1. 3. 注入验证配置
  2. ```json
  3. public class JWTTokenOptions
  4. {
  5. public string Audience
  6. {
  7. get;
  8. set;
  9. }
  10. public string SecurityKey
  11. {
  12. get;
  13. set;
  14. }
  15. public string Issuer
  16. {
  17. get;
  18. set;
  19. }
  20. }
  21. public void ConfigureServices(IServiceCollection services)
  22. {
  23. services.AddTransient<ICustomJWTService, CustomHSJWTService>();
  24. services.Configure<JWTTokenOptions>(this.Configuration.GetSection("JWTTokenOptions"));
  25. }
  1. 新增配置类

    1. public interface ICustomJWTService
    2. {
    3. string GetToken(string UserName, string password);
    4. }
    5. public class CustomHSJWTService : ICustomJWTService
    6. {
    7. #region Option注入
    8. private readonly JWTTokenOptions _JWTTokenOptions;
    9. public CustomHSJWTService(IOptionsMonitor<JWTTokenOptions> jwtTokenOptions)
    10. {
    11. this._JWTTokenOptions = jwtTokenOptions.CurrentValue;
    12. }
    13. #endregion
    14. /// <summary>
    15. /// 用户登录成功以后,用来生成Token的方法
    16. /// </summary>
    17. /// <param name="UserName"></param>
    18. /// <returns></returns>
    19. public string GetToken(string UserName, string password)
    20. {
    21. #region 有效载荷,大家可以自己写,爱写多少写多少;尽量避免敏感信息
    22. var claims = new[]
    23. {
    24. new Claim(ClaimTypes.Name, UserName),
    25. new Claim("NickName",UserName),
    26. new Claim("Role","Administrator"),//传递其他信息
    27. new Claim("password",password) //传递其他信息
    28. };
    29. //需要加密:需要加密key:
    30. SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_JWTTokenOptions.SecurityKey));
    31. SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    32. JwtSecurityToken token = new JwtSecurityToken(
    33. issuer: _JWTTokenOptions.Issuer,
    34. audience: _JWTTokenOptions.Audience,
    35. claims: claims,
    36. expires: DateTime.Now.AddMinutes(5),//5分钟有效期
    37. signingCredentials: creds);
    38. string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
    39. return returnToken;
    40. #endregion
    41. }
    42. }
  2. 新增项目: Wrebapi ,修改配置 ```csharp public void ConfigureServices(IServiceCollection services) {

    1. JWTTokenOptions tokenOptions = new JWTTokenOptions();
    2. Configuration.Bind("JWTTokenOptions", tokenOptions);
    3. services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)//Scheme
    4. .AddJwtBearer(options =>
    5. {
    6. options.TokenValidationParameters = new TokenValidationParameters
    7. {
    8. //JWT有一些默认的属性,就是给鉴权时就可以筛选了
    9. ValidateIssuer = true,//是否验证Issuer
    10. ValidateAudience = true,//是否验证Audience
    11. ValidateLifetime = true,//是否验证失效时间
    12. ValidateIssuerSigningKey = true,//是否验证SecurityKey
    13. ValidAudience = tokenOptions.Audience,//
    14. ValidIssuer = tokenOptions.Issuer,//Issuer,这两项和前面签发jwt的设置一致
    15. IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOptions.SecurityKey))//拿到SecurityKey
    16. //AudienceValidator = (m, n, z) =>
    17. //{
    18. // //等同于去扩展了下Audience的校验规则---鉴权
    19. // return m != null && m.FirstOrDefault().Equals(this.Configuration["audience"]);
    20. //},
    21. //LifetimeValidator = (notBefore, expires, securityToken, validationParameters) =>
    22. //{
    23. // return notBefore <= DateTime.Now
    24. // && expires >= DateTime.Now;
    25. // //&& validationParameters
    26. //}//自定义校验规则
    27. };
    28. });

}

  1. <a name="vb0Yt"></a>
  2. #### 非对称性加密
  3. 1. 修改认证中心配置
  4. ```csharp
  5. public void ConfigureServices(IServiceCollection services)
  6. {
  7. services.AddTransient<ICustomJWTService, CustomRSSJWTervice>();
  8. services.Configure<JWTTokenOptions>(this.Configuration.GetSection("JWTTokenOptions"));
  9. }

2.新增配置类

  1. public class CustomRSSJWTervice : ICustomJWTService
  2. {
  3. #region Option注入
  4. private readonly JWTTokenOptions _JWTTokenOptions;
  5. public CustomRSSJWTervice(IOptionsMonitor<JWTTokenOptions> jwtTokenOptions)
  6. {
  7. this._JWTTokenOptions = jwtTokenOptions.CurrentValue;
  8. }
  9. #endregion
  10. public string GetToken(string userName, string password)
  11. {
  12. //string jtiCustom = Guid.NewGuid().ToString();//用来标识 Token
  13. Claim[] claims = new[]
  14. {
  15. new Claim(ClaimTypes.Name, userName),
  16. new Claim(ClaimTypes.Role,"admin"),
  17. new Claim("password",password)
  18. };
  19. string keyDir = Directory.GetCurrentDirectory();
  20. if (RSAHelper.TryGetKeyParameters(keyDir, true, out RSAParameters keyParams) == false)
  21. {
  22. keyParams = RSAHelper.GenerateAndSaveKey(keyDir);
  23. }
  24. SigningCredentials credentials = new SigningCredentials(new RsaSecurityKey(keyParams), SecurityAlgorithms.RsaSha256Signature);
  25. var token = new JwtSecurityToken(
  26. issuer: this._JWTTokenOptions.Issuer,
  27. audience: this._JWTTokenOptions.Audience,
  28. claims: claims,
  29. expires: DateTime.Now.AddMinutes(60),//5分钟有效期
  30. signingCredentials: credentials);
  31. var handler = new JwtSecurityTokenHandler();
  32. string tokenString = handler.WriteToken(token);
  33. return tokenString;
  34. }
  35. }

3.新增公钥私钥生成类

  1. public class RSAHelper
  2. {
  3. /// <summary>
  4. /// 从本地文件中读取用来签发 Token 的 RSA Key
  5. /// </summary>
  6. /// <param name="filePath">存放密钥的文件夹路径</param>
  7. /// <param name="withPrivate"></param>
  8. /// <param name="keyParameters"></param>
  9. /// <returns></returns>
  10. public static bool TryGetKeyParameters(string filePath, bool withPrivate, out RSAParameters keyParameters)
  11. {
  12. string filename = withPrivate ? "key.json" : "key.public.json";
  13. string fileTotalPath = Path.Combine(filePath, filename);
  14. keyParameters = default(RSAParameters);
  15. if (!File.Exists(fileTotalPath))
  16. {
  17. return false;
  18. }
  19. else
  20. {
  21. keyParameters = JsonConvert.DeserializeObject<RSAParameters>(File.ReadAllText(fileTotalPath));
  22. return true;
  23. }
  24. }
  25. /// <summary>
  26. /// 生成并保存 RSA 公钥与私钥
  27. /// </summary>
  28. /// <param name="filePath">存放密钥的文件夹路径</param>
  29. /// <returns></returns>
  30. public static RSAParameters GenerateAndSaveKey(string filePath, bool withPrivate = true)
  31. {
  32. RSAParameters publicKeys, privateKeys;
  33. using (var rsa = new RSACryptoServiceProvider(2048))//即时生成
  34. {
  35. try
  36. {
  37. privateKeys = rsa.ExportParameters(true);
  38. publicKeys = rsa.ExportParameters(false);
  39. }
  40. finally
  41. {
  42. rsa.PersistKeyInCsp = false;
  43. }
  44. }
  45. File.WriteAllText(Path.Combine(filePath, "key.json"), JsonConvert.SerializeObject(privateKeys));
  46. File.WriteAllText(Path.Combine(filePath, "key.public.json"), JsonConvert.SerializeObject(publicKeys));
  47. return withPrivate ? privateKeys : publicKeys;
  48. }
  49. }

4.请求token后会主动生成公钥和私钥, 然后把公钥复制到webapi项目下,即可访问该api

5.修改webapi配置

  1. #region 读取公钥
  2. string path = Path.Combine(Directory.GetCurrentDirectory(), "key.public.json");
  3. string key = File.ReadAllText(path);//this.Configuration["SecurityKey"];
  4. Console.WriteLine($"KeyPath:{path}");
  5. var keyParams = JsonConvert.DeserializeObject<RSAParameters>(key);
  6. SigningCredentials credentials = new SigningCredentials(new RsaSecurityKey(keyParams), SecurityAlgorithms.RsaSha256Signature);
  7. #endregion
  8. JWTTokenOptions tokenOptions = new JWTTokenOptions();
  9. Configuration.Bind("JWTTokenOptions", tokenOptions);
  10. services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  11. .AddJwtBearer(options =>
  12. {
  13. options.TokenValidationParameters = new TokenValidationParameters
  14. {
  15. ValidateIssuer = true,//是否验证Issuer
  16. ValidateAudience = true,//是否验证Audience
  17. ValidateLifetime = true,//是否验证失效时间
  18. ValidateIssuerSigningKey = true,//是否验证SecurityKey
  19. ValidAudience = tokenOptions.Audience,//Audience
  20. ValidIssuer = tokenOptions.Issuer,//Issuer,这两项和前面签发jwt的设置一致
  21. IssuerSigningKey = new RsaSecurityKey(keyParams),
  22. //IssuerSigningKeyValidator = (m, n, z) =>
  23. // {
  24. // Console.WriteLine("This is IssuerValidator");
  25. // return true;
  26. // },
  27. //IssuerValidator = (m, n, z) =>
  28. // {
  29. // Console.WriteLine("This is IssuerValidator");
  30. // return "http://localhost:5726";
  31. // },
  32. //AudienceValidator = (m, n, z) =>
  33. //{
  34. // Console.WriteLine("This is AudienceValidator");
  35. // return true;
  36. // //return m != null && m.FirstOrDefault().Equals(this.Configuration["Audience"]);
  37. //},//自定义校验规则,可以新登录后将之前的无效
  38. };
  39. });