这是第一部:先实现NetFramework
上的WebApi
使用JWT
认证
1、VS新建一个WebApi
项目
2、项目右键----管理Nuget
程序包----找到JWT
,然后安装
3、Model
文件夹下新建三个类LoginResult,LoginRequest,AuthInfo
namespace JwtWebApi.Models
{
public class LoginResult
{
public bool Success { get; set; }
public string Token { get; set; }
public string Message { get; set; }
}
}
namespace JwtWebApi.Models
{
public class LoginRequest
{
public string UserName { get; set; }
public string Password { get; set; }
}
}
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; }
}
}
4、在Controllers
文件夹中的HomeController
(没有就新建一个)中添加一个Post
方法,这是生成JWT Token
方法的地方,一般应放在登录的Action
下
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 HomeController : ApiController
{
public LoginResult Post([FromBody]LoginRequest request)
{
LoginResult rs = new LoginResult();
//这是是获取用户名和密码的,这里只是为了模拟
if (request.UserName == "wangshibang" && request.Password == "123456")
{
AuthInfo info = new AuthInfo { UserName = "wangshibang", Roles = new List<string> { "Admin", "Manage" }, IsAdmin = true };
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);
rs.Message = "XXXXX";
rs.Token = token;
rs.Success = true;
}
catch (Exception ex)
{
rs.Message = ex.Message;
rs.Success = false;
}
}
else
{
rs.Message = "fail";
rs.Success = false;
}
return rs;
}
}
}
5、项目下添加一个Attributes
文件夹,需要写个权限拦截器,新建一个ApiAuthorizeAttribute
类继承自AuthorizeAttribute
类
using JWT;
using JWT.Serializers;
using JwtWebApi.Models;
using System;
using System.Linq;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace JwtWebApi.Attributes
{
public class ApiAuthorizeAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var authHeader = from t in actionContext.Request.Headers where t.Key == "auth" select t.Value.FirstOrDefault();
if (authHeader != null)
{
string token = authHeader.FirstOrDefault();
if (!string.IsNullOrEmpty(token))
{
try
{
const string secret = "To Live is to change the world";
//secret需要加密
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);
var json = decoder.DecodeToObject<AuthInfo>(token, secret, verify: true);
if (json != null)
{
actionContext.RequestContext.RouteData.Values.Add("auth", json);
return true;
}
return false;
}
catch (Exception ex)
{
return false;
}
}
}
return false;
}
}
}
6、Controllers
文件夹中新建一个UserController
,新建一个Get
的Action
,需要加上ApiAuthorize
特性
using JwtWebApi.Attributes;
using JwtWebApi.Models;
using System.Web.Http;
namespace JwtWebApi.Controllers
{
public class UserController : ApiController
{
// GET: User
[ApiAuthorize]
public string Get()
{
AuthInfo info = RequestContext.RouteData.Values["auth"] as AuthInfo;
if (info == null)
{
return "获取不到,失败";
}
else
{
return $"获取到了,Auth的Name是 {info.UserName}";
}
}
}
}
7、然后用PostMan
测试
下面是解决接口调用的跨域问题,有两种,一种是用CORS
,另外一种就是修改WebConfig
添加自定义options
谓词处理模块
我只用了自定义Options
谓词处理
<system.webServer>
<handlers>
<!--开启options谓词处理模块-->
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<!--<remove name="OPTIONSVerbHandler" />-->
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<httpProtocol>
<customHeaders>
<!--添加自定义options谓词处理模块-->
<add name="Access-Control-Allow-Origin" value="http://localhost:8057"/>
<add name="Access-Control-Allow-Headers" value="accept, auth"/>
<add name="Access-Control-Allow-Methods" value="GET, OPTIONS"/>
</customHeaders>
</httpProtocol>
<validation validateIntegratedModeConfiguration="false" />
<modules>
<remove name="ApplicationInsightsWebTracking" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
</modules>
</system.webServer>
好了,现在把你的WebApi
部署到服务器上,然后用另一个跨域页面调取接口访问吧
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<meta charset="utf-8" />
<script src="jquery-3.3.1.min.js"></script>
</head>
<body>
<fieldset>
<legend>身份验证</legend>
<form>
<label for="UserName">用户名:</label><input type="text" name="userName" id="userName" value="admin" />
<br />
<br />
<label for="Password">密码:</label><input type="password" name="password" id="password" value="123" />
<br />
<br />
</form>
<button id="login">登录</button>
</fieldset>
<br />
<fieldset>
<legend>调用接口</legend>
<button id="invoke">调用接口</button>
</fieldset>
<script>
$(function () {
//调用api站点的登录接口,接口在登录成功后返回一个token。
$("#login").on("click", function () {
$.ajax({
url: "http://localhost:8056/api/home",
data: $("form").serialize(),
method: "post",
success: function (data) {
if (data.Success) {
//为简单起见,将token保存在全局变量中。
window.token = data.Token;
alert("登录成功");
} else {
alert("登录失败:" + data.Message);
}
}
});
});
//调用api站点的获取数据的接口,该接口要求身份验证。
$("#invoke").on("click", function () {
console.log(window.token);
$.ajax({
url: "http://localhost:8056/api/user",
method: "get",
headers: { "auth": window.token },//通过请求头来发送token,放弃了通过cookie的发送方式
complete: function (jqXHR,textStatus) {
alert(jqXHR.responseText);
},
});
});
});
</script>
</body>
</html>