上一篇文章说到JWT API 跨域验证实战,这一篇从完整login 打,跨域访问带有权限的页面。
资源
SiteA
SiteB
Summery
jS:
SiteA js postMessageToken to siteB,siteB get the Token, and send request by adding header and the Token, then redirect the page need to verify
Server:
MVC框架
Model
UserInfo // 保存用户信息,和验证内容
public class UserInfo
{
/// <summary>
/// name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Email
/// </summary>
public string Email { get; set; }
/// <summary>
/// PassWord
/// </summary>
public string PassWord { get; set; }
/// <summary>
/// role
/// </summary>
public List<string> Roles { get; set; }
/// <summary>
/// is admin
/// </summary>
public bool IsAdmin { get; set; }
/// <summary>
/// expiry date
/// </summary>
public DateTime? ExpiryDateTime { get; set; }
}
JwtResult
JWT加密,解密过程
Token的创建
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Newtonsoft.Json;
namespace MVCWeb.Models
{
public class JwtResult {
public string JwtCode { get; set; }
public string StatusCode { get; set; }
public string Message { get; set; }
}
public class JwtHelper
{
private string m_Secret = "THC";
public string EncodeJwt(UserInfo userInfo)
{
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
return encoder.Encode(userInfo, m_Secret);
}
public UserInfo DecodeJwt(string token)
{
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
var algorithm = new HMACSHA256Algorithm();
IJwtDecoder decoder = new JwtDecoder(serializer, validator,urlEncoder, algorithm);
var userInfo = decoder.DecodeToObject<UserInfo>(token, m_Secret, verify: true);//token为之前生成的字符串
return userInfo;
}
public JwtResult CreateToken(string UserName, string PassWord)
{
if (string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(PassWord))
throw new Exception("参数为空");
JwtResult jwtResult;
try
{
//var param = HttpContext.Request["UserName"];
//param = HttpContext.Request["PassWord"];
if (!UserName.Equals("test") || !PassWord.Equals("test"))
{
throw new Exception("用户密码不正确。");
}
UserInfo pUserLoginInfo = new UserInfo() { Name = UserName, PassWord = PassWord };
JwtHelper pHelper = new JwtHelper();
string sJwt = pHelper.EncodeJwt(pUserLoginInfo);
jwtResult = new JwtResult()
{
JwtCode = sJwt,
StatusCode = "200",
Message = "success"
};
return jwtResult;
}
catch (Exception ex)
{
jwtResult = new JwtResult()
{
JwtCode = "",
StatusCode = "403",
Message = ex.Message
};
}
return jwtResult;
}
}
}
Filter:
AppAuthorizeAttribute,自动验证过程,本代码支持两证验证,一是登录验证,二是头部auto验证,任何一种满足都可以拥有访问权限,需要注意点,无论哪种验证,等待验证额通过后,我都会把Token对象保存在Session中,//System.Web.HttpContext.Current.Session["tokenInfo"] = jr,目的是访问有权限的页面时候,便于通过验证
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCWeb.Models;
namespace MVCWeb.Filters
{
public class AppAuthorizeAttribute : AuthorizeAttribute
{
/// <summary>
/// 验证入口
/// </summary>
/// <param name="filterContext"></param>
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
JwtHelper pHelper = new JwtHelper();
try
{
JwtResult jr = (JwtResult)GHelper.GetCurrentTokenInfo();
//前端请求api时会将token存放在名为"auth"的请求头中
var authHeader = httpContext.Request.Headers["auth"];
/*
if (authHeader == null)
{
httpContext.Response.StatusCode = 403;
return false;
}
*/
if (authHeader != null)
{
jr = new JwtResult();
jr.JwtCode = authHeader;
}
else if (jr != null)
{
}
else {
httpContext.Response.StatusCode = 403;
return false;
}
var userinfo = pHelper.DecodeJwt(jr.JwtCode);
if (userinfo != null)
{
jr.Message = "success";
jr.StatusCode = "200";
System.Web.HttpContext.Current.Session["tokenInfo"] = jr;
return true;
}
httpContext.Response.StatusCode = 403;
return false;
}
catch
{
httpContext.Response.StatusCode = 403;
return false;
}
}
/// <summary>
/// 验证失败处理
/// </summary>
/// <param name="filterContext"></param>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
System.Web.HttpContext.Current.Session.Clear();
base.HandleUnauthorizedRequest(filterContext);
if (filterContext.HttpContext.Response.StatusCode == 403)
{
//filterContext.Result = new RedirectResult("/Error");
filterContext.HttpContext.Response.Redirect("/account/login");
}
}
}
}
Controls:
AccountController
登录后保存token到sesssion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCWeb.Models;
namespace MVCWeb.Controllers
{
public class AccountController : Controller
{
// GET: Account
public ActionResult Index()
{
return View();
}
[HttpGet] //返回页面,展示登录页面
public ActionResult Login()
{
return View();
}
[HttpPost] //提交登录,开始登录时执行业务逻辑
public ActionResult Login(UserInfo user)
{
/*
if (ModelState.IsValid) //触发实体验证,如果返回true,则通过验证登录成功
{
if (user.Name == "test" && user.PassWord == "test")
{
return base.Redirect("/Home/Index");
}
else {
return Content("用户名或密码有误!");
}
}
return base.Redirect("Home/Index");
*/
if (ModelState.IsValid)
{
JwtHelper jh = new JwtHelper();
JwtResult jr = jh.CreateToken(user.Name, user.PassWord);
System.Web.HttpContext.Current.Session["tokenInfo"] = jr;
if (jr.Message == "success")
{
//return RedirectToAction("index", "Home", jr); //url后有参数
return base.Redirect("/Home/Index");
}
else
{
return Content("用户名或密码有误!");
}
}
return Content("fail to login!");
}
}
}
HomeController
将被访问带有权限的页面 [AppAuthorize],首先需要通过验证,才能访问
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCWeb.Filters;
using MVCWeb.Models;
namespace MVCWeb.Controllers
{
[AppAuthorize]
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
//to do....
JwtResult jr = (JwtResult)GHelper.GetCurrentTokenInfo();
//解析token => jr
//...
UserInfo pUser = new UserInfo()
{
Name = "Test",
PassWord = "Test"
};
//ViewBag.Token = jr.JwtCode;
//ViewBag.Message = jr.Message;
//ViewBag.Status = jr.StatusCode;
return View(pUser);
}
public string test() {
return "test";
}
}
}
THCController
未经过授权的页面,目的是仅供跨域者使用,是一个空页面,或者中转页面
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCWeb.Filters;
using MVCWeb.Models;
namespace MVCWeb.Controllers
{
//[AppAuthorize]
public class THCController : Controller
{
// GET: THC
public ActionResult Index()
{
return View();
}
}
}