简单口述一下这个JWT Token验证和Attribute拦截器在项目中起到的作用
一.Token存在的意义
Token的存是为了实现接口安全,比如当接口地址泄露了,别人随便就能访问了,当我们有了Token,当别人知道了我们的接口地址,他们没有Token,我们后台检测到token为空,就会返回请求错误
二.Attribute拦截器的意义
就是相当于别人调用你的接口方法,我们加了拦截器,别人调用我们接口的时候就会先进入拦截器,我们就可以先在拦截器里面做一个校验什么的操作了,当然拦截器有进入方法前,返回值前,返回值后等的拦截器,前面的文章我有写到过
三.JWT Token验证结合Attribute拦截器在项目中的使用流程简单描述(表达能力略差)
第一步:客户端登录
第二步:验证用户名和密码以及添加Token
第三步:当用户名密码验证通过且Token生成成功,则返回验证成功状态和token值给前端页面
第四步:前端页面接收到返回验证成功状态和token值则把token值存到页面缓存之中(怎么存都行看你们自己)
第五步:当我们进入主页做其它功能请求接口时,先获取页面缓存中的token,携带token请求接口
第六步:当我们请求方法时,方法加了拦截器,就会先进入拦截器,就会判断这个token是否存在且token值正确
第七步:当token传输过来无误就会解析token存储到请求上下文Request上下文中(这个解析之后你们也可以不用一定存储到Request上下文中,根据情况而定)
第八步:然后再进入请求的接口方法体中进行数据操作
下面开始我们Demo实战演习............................................................
1.添加项目
2.添加model
using System.Collections.Generic;
namespace JwtWebApi.Models
{
public class AuthInfo
{
//模拟JWT的payload
public string UserName { get; set; }
public List<string> Roles { get; set; }
public bool IsAdmin { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public string ExpirationTime { get; set; }
}
}
namespace JwtWebApi.Models
{
public class LoginRequest
{
public string UserName { get; set; }
public string Password { get; set; }
}
}
namespace JwtWebApi.Models
{
public class LoginResult
{
public bool Success { get; set; }
public string Token { get; set; }
public string Message { get; set; }
}
}
3.添加拦截器(创建一个文件夹Attributes)添加一个ApiAuthorizeAttribute类
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using JwtWebApi.Models;
using System;
using System.Linq;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace JwtWebApi.Attributes
{
/// <summary>
/// 拦截器
/// </summary>
public class ApiAuthorizeAttribute: AuthorizeAttribute
{
/// <summary>
/// 验证请求是否携带token
/// </summary>
/// <param name="actionContext"></param>
/// <returns></returns>
protected override bool IsAuthorized(HttpActionContext actionContext)
{
//获取token
var authHeader = from t in actionContext.Request.Headers
where t.Key == "auth"
select t.Value.FirstOrDefault();
//验证token是否为空
if (authHeader != null)
{
string token = authHeader.FirstOrDefault();
if (!string.IsNullOrEmpty(token))
{
try
{
const string secret = "To Live is to change the world";
//secret需要加密
JWT.IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
var authInfo = decoder.DecodeToObject<AuthInfo>(token, secret, verify: true);
if (authInfo != null)
{
//验证token是否超时,这里设置的过期时间为两小时
if (Convert.ToDateTime(authInfo.ExpirationTime) < DateTime.Now.AddHours(2) && !string.IsNullOrEmpty(authInfo.ExpirationTime))
{
actionContext.RequestContext.RouteData.Values.Add("autninfoDate", authInfo);
return true;
}
else return true;
}
return false;
}
catch (Exception ex)
{
return false;
}
}
}
return false;
}
}
}
4.创建Controller
SignInController 控制器
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using JwtWebApi.Models;
using System;
using System.Collections.Generic;
using System.Web.Http;
namespace JwtWebApi.Controllers
{
public class SignInController : ApiController
{
/// <summary>
/// 登录/生成token
/// </summary>
/// <param name="UserName">用户名</param>
/// <param name="PassWord">密码</param>
/// <returns></returns>
[HttpGet]
public LoginResult Post(string UserName,string PassWord)
{
LoginResult rs = new LoginResult();
//这是是获取用户名和密码的,这里只是为了模拟
AuthInfo info = new AuthInfo {
UserName = UserName, //用户名
Roles = new List<string> { "Admin", "Manage","YQ" }, //权限
IsAdmin = true,
ExpirationTime = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") };//过期时间
try
{
const string secret = "To Live is to change the world";
//secret需要加密
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(info, secret);
#region 模拟数据库存
//登录用户名密码验证
List<LoginRequest> list=new List<LoginRequest>();
list.Add(new LoginRequest { UserName = "admin", Password = "admin" });
#endregion
//且数据库有这用户才能登录成功
if (list.Exists(t => t.UserName == UserName && t.Password == PassWord)==false)
return new LoginResult { Message = "登录失败,账号或密码错误!", Success = false };
//验证token是否为空
if (token==null || token=="")
return new LoginResult { Message = "登录失败,token生成失败!", Success = false };
return new LoginResult { Message = "登录成功!", Token = token, Success = true };
}
catch (Exception ex)
{
//异常返回
return new LoginResult { Message = ex.Message, Success = false };
}
}
}
}
UserController 控制器
using JwtWebApi.Attributes;
using JwtWebApi.Models;
using System.Web.Http;
namespace JwtWebApi.Controllers
{
public class UserController : ApiController
{
/// <summary>
/// 获取测试token
/// </summary>
/// <returns></returns>
[ApiAuthorize]
[HttpGet]
public string Get11()
{
AuthInfo info = RequestContext.RouteData.Values["autninfoDate"] as AuthInfo;
if (info == null)
{
return "获取不到,失败";
}
else
{
return $"获取到了,Auth的Name是 {info.UserName}";
}
}
}
}
5.更改Home/Index页面内容
index页面内容
<div class="jumbotron">
<fieldset>
<legend>身份验证</legend>
<form id="form">
<label for="UserName">用户名:</label><input type="text" name="userName" id="UserName" />
<br />
<br />
<label for="Password">密码:</label><input type="password" name="password" id="PassWord" />
<br />
<br />
</form>
<button id="login">登录</button>
</fieldset>
<br />
<fieldset>
<button id="invoke">调用接口</button>
</fieldset>
<script src="~/Scripts/jquery-3.4.1.js"></script>
<script src="~/Scripts/jquery-3.4.1.min.js"></script>
<script>
//调用api站点的登录接口,接口在登录成功后返回一个token。
$("#login").on("click", function () {
debugger
var UserName = $("#UserName").val();
if (!UserName) {
alert("用户名不能为空!");
return;
}
var PassWord = $("#PassWord").val();
if (!PassWord) {
alert("密码不能为空!");
return;
}
$.ajax({
//url: "https://localhost:44330/api/SignIn/Post?UserName=" + UserName + "&PassWord=" + PassWord + "",
url: "../../api/SignIn/Post?UserName=" + UserName + "&PassWord=" + PassWord + "",
method: "get",
async: false,
success: function (data) {
if (data.Success) {
//为简单起见,将token保存在全局变量中。
window.token = data.Token;
alert("登录成功");
} else {
alert("登录失败:" + data.Message);
}
}, error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log("请求进入error--" + XMLHttpRequest.status);
// 状态
console.log(XMLHttpRequest.readyState);
// 错误信息
console.log(textStatus);
console.log(XMLHttpRequest.responseText);
}
});
});
//调用api站点的获取数据的接口,该接口要求身份验证。
$("#invoke").on("click", function () {
console.log(window.token);
$.ajax({
//url: "https://localhost:44330/api/User/Get11",
url: "../../api/User/Get11",
method: "get",
headers: {
"auth": window.token
}, //通过请求头来发送token,放弃了通过cookie的发送方式
complete: function (jqXHR, textStatus) {
alert(jqXHR.responseText);
}, error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log("请求进入error--" + XMLHttpRequest.status);
// 状态
console.log(XMLHttpRequest.readyState);
// 错误信息
console.log(textStatus);
console.log(XMLHttpRequest.responseText);
}
});
});
</script>
</div>
完结撒花........