JWT介绍

参考文章 https://www.cnblogs.com/cjsblog/p/9277677.html

一、.NET 中使用

1. NuGet包

搜索JWT,下载安装(本人用的是8.2.3版本)

2. 自定义帮助类

2.1 新建 IHttpResponseResult interface接口

    public interface IHttpResponseResult
{
} /// <summary>
/// 响应数据输出泛型接口
/// </summary>
/// <typeparam name="T"></typeparam>
// ReSharper disable once UnusedTypeParameter
public interface IHttpResponseResult<T> : IHttpResponseResult
{
}

2.2 新建 HttpResponseResult 数据响应类

    public class HttpResponseResult<T> : IHttpResponseResult<T>
{
/// <summary>
/// 状态码
/// </summary>
public int Code { get; set; } /// <summary>
/// 消息
/// </summary>
public string Message { get; set; } /// <summary>
/// 数据
/// </summary>
public T Data { get; set; } /// <summary>
/// 成功
/// </summary>
/// <param name="data">数据</param>
/// <param name="msg">消息</param>
public HttpResponseResult<T> Success(T data = default, string msg = null)
{
Code = 0;
Data = data;
Message = msg;
return this;
} /// <summary>
/// 失败
/// </summary>
/// <param name="code">状态码</param>
/// <param name="msg">消息</param>
/// <param name="data">数据</param>
/// <returns></returns>
public HttpResponseResult<T> Fail(T data = default, int code = -1, string msg = null)
{
Code = code;
Message = msg;
Data = data;
return this;
}
} /// <summary>
/// 响应数据静态输出
/// </summary>
public static class HttpResponseResult
{
/// <summary>
/// 成功
/// </summary>
/// <param name="data">数据</param>
/// <param name="msg">消息</param>
/// <returns></returns>
public static IHttpResponseResult Success<T>(T data, string msg = "message")
{
return new HttpResponseResult<T>().Success(data, msg);
} /// <summary>
/// 失败
/// </summary>
/// <param name="data">数据</param>
/// <param name="msg">消息</param>
/// <param name="code">状态码</param>
/// <returns></returns>
public static IHttpResponseResult Fail<T>(T data, string msg = null, int code = -1)
{
return new HttpResponseResult<T>().Fail(data, code, msg);
}
}

3.生成JWT Token

3.1 新建Conteoller

新建 LoginController 并且新建一个生成 Token 的方法 Login

    /// <summary>
/// JWT授权
/// </summary>
[RoutePrefix("api/login")]
public class LoginController : ApiController
{
private readonly string secretKey = "JwtSecretKey";//Jwt密钥,自定义
private readonly int tokenExpire = 2;//Token过期时间 /// <summary>
/// 授权登录获取Token
/// </summary>
/// <param name="loginModel"></param>
/// <returns></returns>
[HttpPost, Route("GetToken")]
public IHttpResponseResult Login([FromBody] Login loginModel)
{
dynamic obj = new ExpandoObject();
try
{
var userName = loginModel.UserName;
var userPwd = loginModel.UserPwd;
//var loginRes = ChatUserDao.GetJwtUser(userName, userPwd).IsNull(); //自己的数据库验证
var loginRes = "Dennis".Equals(userName) && "123".Equals(userPwd);
if (loginRes)
{
return HttpResponseResult.Fail(obj, "The user is not found or password is error.");
} var expireTime = DateTime.Now.AddHours(tokenExpire);
//身份验证信息,这里存放的就是payload信息
var jwtToken = new JwtToken { AuthUserName = userName, ExpireTime = expireTime };
var key = Encoding.UTF8.GetBytes(secretKey);
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); //加密方式
IJsonSerializer serializer = new JsonNetSerializer(); //序列化Json
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); //base64加解密
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); //JWT编码
var token = encoder.Encode(jwtToken, key); //生成令牌 obj.Token = token;
obj.ExpireTime = expireTime; return HttpResponseResult.Success(obj, "Get Token Success.");
}
catch (Exception e)
{
LogFileHelper.WriteLog("GetToken", e.ToExceptionString());
return HttpResponseResult.Fail(obj, e.ToExceptionString());
}
}
}

3.2 新建JwtToken 模型类

    /// <summary>
/// Jwt Token
/// </summary>
public class JwtToken
{
/// <summary>
/// 授权者
/// </summary>
public string AuthUserName { get; set; } /// <summary>
/// Token过期时间
/// </summary>
public DateTime ExpireTime { get; set; }
}

4. 新建Attribute类验证Token

App_Start项目文件加中新建一个ApiAuthAttribute类,继承自AuthorizeAttribute

    /// <summary>
/// Jwt 授权验证
/// </summary>
public class ApiAuthAttribute : AuthorizeAttribute
{
private readonly string secretKey = "JwtSecretKey";//加密秘钥,与生成Token时的密钥相同
private const string authHeader = "Authorization";//请求头Header中存放JwtToken的Key名称 /// <summary>
/// 判断授权
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
protected override bool IsAuthorized(HttpActionContext httpContext)
{
try
{
var httpHeader = httpContext.Request.Headers;
var token = string.Empty;//获取token foreach (var keyHeader in httpHeader)
{
if (authHeader.Equals(keyHeader.Key))
{
token = keyHeader.Value.FirstOrDefault();
}
} if (token.IsEmpty())
{
return false;
} var key = Encoding.UTF8.GetBytes(secretKey);
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); //加密方式
IJsonSerializer serializer = new JsonNetSerializer();//序列化Json
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//base64加解密
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm); //解密
var json = decoder.DecodeToObject<JwtToken>(token, key, true);
if (json.IsNull()) return false;
return json.ExpireTime >= DateTime.Now;
}
catch (Exception e)
{
LogFileHelper.WriteLog(authHeader, e.ToExceptionString());
return false;
}
} /// <summary>
/// 授权失败时调用
/// </summary>
/// <param name="httpContext"></param>
protected override void HandleUnauthorizedRequest(HttpActionContext httpContext)
{
//Token过期时,响应头添加过期标识
httpContext.Response = httpContext.ControllerContext.Request.CreateResponse(
HttpStatusCode.Unauthorized, HttpResponseResult.Fail(false, "Token is not found or expired, authorization failed."));
httpContext.Response.Headers.Add("Token-Expired", "true");
}
}

5. 测试Token验证

    /// <summary>
/// 测试接口
/// </summary>
[RoutePrefix("api/test")]
public class TestController : ApiController
{
/// <summary>
/// Token认证测试
/// </summary>
/// <returns></returns>
[HttpGet, Route("TokenAuth")]
[ApiAuth]
public IHttpResponseResult TokenAuth()
{
return HttpResponseResult.Success(true, "Auth Passed");
}
}

二、.NET Core 中使用

1. NuGet包

搜索JwtBearer,下载安装(本人安装的是5.0.7)

2. 配置Startup

注册JWT中间件,我是单独写了一个类然后引用,你也可以直接写在Startup的ConfigureServices方法中

    /// <summary>
/// JWT授权中间件
/// </summary>
public static class AuthorizationMiddleware
{
/// <summary>
/// 注册授权服务
/// </summary>
/// <param name="services"></param>
public static void AddAuthorizationService(this IServiceCollection services)
{
// 开启Bearer认证
services.AddAuthentication(options =>
{
// 设置默认使用jwt验证方式
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
// 添加JwtBearer服务
.AddJwtBearer(o =>
{
// 令牌验证参数
o.TokenValidationParameters = new TokenValidationParameters
{
// 设置生成token的秘钥
//IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(AppConfig.SecretKey)),
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AppConfig.SecretKey)),
// 验证秘钥
ValidateIssuerSigningKey = true,
// 验证发布者
ValidateIssuer = true,
// 验证Issure
ValidIssuer = AppConfig.Issuer,//发行人
// 验证接收者
ValidateAudience = true,
// 读配置Audience
ValidAudience = AppConfig.Audience,//订阅人
// 验证过期时间
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(30),
RequireExpirationTime = true
};
o.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// 如果过期,则把<是否过期>添加到,返回头信息中
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
} return Task.CompletedTask;
}
};
}); //如果没有角色控制到Action则注释下列代码
services.AddAuthorization(options =>
{
options.AddPolicy("User", policy => policy.RequireRole("User").Build());
options.AddPolicy("SystemOrAdmin", policy => policy.RequireRole("Admin", "System")); });
}
}

然后在Startup的ConfigureServices方法中添加引用

services.AddAuthorizationService();

在Startup的Configure方法中使用授权

app.UseRouting()之后和app.UseEndpoints之前添加代码

app.UseAuthentication();//身份验证
app.UseAuthorization();//身份授权

3. JWT自定义帮助类

3.1 JWTHelper

    /// <summary>
/// JWT 帮助类
/// </summary>
public static class JWTHelper
{
/// <summary>
/// 颁发JWT字符串
/// </summary>
/// <param name="tokenModel"></param>
/// <returns></returns>
public static string IssueJwt(UserModel tokenModel)
{
//获取Appsetting配置
var iss = AppConfig.Issuer;
var aud = AppConfig.Audience;
var secret = AppConfig.SecretKey;
var expire = AppConfig.Expire; // 这里存放的是payload信息
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Jti, tokenModel.UserId),
new Claim(JwtRegisteredClaimNames.Iss, iss),
new Claim(JwtRegisteredClaimNames.Aud, aud)
}; // 可以将一个用户的多个角色全部赋予,比如参数System,Admin,那么该token即拥有两个角色;
claims.AddRange(tokenModel.UserName.Split(',').Select(s => new Claim(ClaimTypes.Role, s))); //秘钥 (SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常)
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var jwt = new JwtSecurityToken(
issuer: iss,
claims: claims,
expires: DateTime.Now.AddMinutes(expire),
signingCredentials: credentials);
var jwtHandler = new JwtSecurityTokenHandler();
var encodedJwt = jwtHandler.WriteToken(jwt); return encodedJwt;
} /// <summary>
/// 解析
/// </summary>
/// <param name="jwtStr"></param>
/// <returns></returns>
public static UserModel SerializeJwt(string jwtStr)
{
var jwtHandler = new JwtSecurityTokenHandler();
var jwtToken = jwtHandler.ReadJwtToken(jwtStr);
object role;
try
{
jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
var tm = new UserModel
{
UserId = jwtToken.Id,
UserRole = role != null ? role.ToString() : "",
};
return tm;
}
}

密钥等相关配置动态配置在appsettings.json文件中

  "JwtSetting": {
"Issuer": "DennisDong",
"Audience": "https://www.dennisdong.top",
"SecretKey": "D@1#n$n%i&s.D*0n!g",
"Expire": "2"
}

3.2 UserModel

    /// <summary>
/// 用户Model
/// </summary>
public class UserModel
{
/// <summary>
/// UserId
/// </summary>
public string UserId { get; set; } /// <summary>
/// 角色
/// </summary>
public string UserRole { get; set; } /// <summary>
/// 姓名
/// </summary>
public string UserName { get; set; } /// <summary>
/// 生日
/// </summary>
public DateTime UserBirthDay { get; set; }
}

4. 生成和测试Token

    /// <summary>
/// 登录
/// </summary>
[Route("api/login/[action]")]
[ApiController]
public class LoginController : ControllerBase
{
/// <summary>
/// 登录获取Token
/// </summary>
/// <param name="role"></param>
/// <returns></returns>
[HttpGet]
public IActionResult Login(string role)
{
string token;
var result = false;
if (role.NotNull())
{
//这里只是测试,具体数据验证根据自己需要来即可
token = JWTHelper.IssueJwt(new UserModel
{
UserId = Guid.NewGuid().ToString(),
UserRole = role,
UserName = role,
UserBirthDay = DateTime.Now
});
result = true;
}
else
{
token = " Login Fail.";
} return Ok(new { Status = result, Token = token });
} /// <summary>
/// 解析Token
/// </summary>
/// <returns></returns>
[HttpGet]
//该接口限制只有System 或 Admin 角色(中间件中AddAuthorization方法的配置决定)的Token可以访问
[Authorize("SystemOrAdmin")]
//如果中间件中没有配置AddPolicy,直接使用Authorize即可
//[Authorize]
public IActionResult ParseToken()
{
//需要截取Bearer
var tokenHeader = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
var user = JWTHelper.SerializeJwt(tokenHeader);
return Ok(user);
}
}

三、Swagger配合JWT使用

请参考文章 https://www.cnblogs.com/dennisdong/p/15719616.html

四、WebAPI 跨域问题

请参考文章 https://www.cnblogs.com/dennisdong/p/15719873.html

最新文章

  1. tomcat跨域请求
  2. ionic + cordova 使用 cordova-plugin-crosswalk-webview 中的一些个坑
  3. jquery实现隐藏,简化和更多
  4. Navi.Soft30.产品.Net对象查看器.操作手册
  5. 【cs229-Lecture18】线性二次型调节控制
  6. ios开发--苹果企业开发者账号
  7. impersonate a user
  8. SPOJ LGLOVE 7488 LCM GCD Love (区间更新,预处理出LCM(1,2,...,n))
  9. BZOJ 3944 Sum 解题报告
  10. linux基础之Shell Script入门介绍
  11. Hadoop上结合opencv\javacv
  12. CSS3 设置 Table 隔行变色
  13. PHP自学2——将用户提交表单存储到外部普通文件中
  14. WEB前端性能优化之二——css优化
  15. TestNG--入门介绍教程
  16. 数据文件实时同步(rsync + sersync2)
  17. [Swift]LeetCode302. 包含黑色像素的最小矩形 $ Smallest Rectangle Enclosing Black Pixels
  18. 转:[kipmi0]进程导致系统负载高
  19. url的三个js编码函数escape(),encodeURI(),encodeURIComponent()简介【转】
  20. MATLAB细节笔记

热门文章

  1. RabbitMQ 常见问题
  2. oracle常用查看命令
  3. Windows自带管理工具
  4. Windows之应用安装程序 —— winget
  5. 关于pip3 ImportError: cannot import name 'main'的报错的原因及解决办法
  6. Zabbix技术分享——使用Zabbix6.0监控业务日志
  7. centos7安装MySql8.0.29教程
  8. 【JVM故障问题排查心得】「内存诊断系列」Xmx和Xms的大小是小于Docker容器以及Pod的大小的,为啥还是会出现OOMKilled?
  9. [seaborn] seaborn学习笔记4-核密度图DENSITYPLOT
  10. 面对集中式缓存实现上的挑战,Redis交出的是何种答卷?聊聊Redis在分布式方面的能力设计