一:为什么需要身份认证
没有身份认证,匿名用户只要知道了我们服务的url,就能随意访问接口
增加了身份认证后,只有拿到票据的请求才能访问接口
下面是代码示例
WEB前端
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> </head> <body style=""> <canvas class="pg-canvas" width="1536" height="310"></canvas> <div class="login"> <div class="login_title"> <span>管理员登录</span> </div> <div class="login_fields"> <div class="login_fields__user"> <div class="icon"> <img alt="" src="/Admin/img/user_icon_copy.png"> </div> <input name="login" placeholder="用户名" maxlength="16" type="text" autocomplete="off" value="admin"> <div class="validation"> <img alt="" src="/Admin/img/tick.png"> </div> </div> <div class="login_fields__password"> <div class="icon"> <img alt="" src="/Admin/img/lock_icon_copy.png"> </div> <input name="pwd" placeholder="密码" maxlength="16" type="text" autocomplete="off"> <div class="validation"> <img alt="" src="/Admin/img/tick.png"> </div> </div> <div class="login_fields__password"> <div class="icon"> <img alt="" src="/Admin/img/key.png"> </div> <input name="code" placeholder="验证码" maxlength="4" type="text" autocomplete="off"> <div class="validation" style="opacity: 1; right: -5px; top: -3px;"> <canvas class="J_codeimg" id="myCanvas" onclick="Code();">对不起,您的浏览器不支持canvas,请下载最新版浏览器!</canvas> </div> </div> <div class="login_fields__submit"> <input type="button" value="登录"> </div> </div> <div class="success"> </div> <div class="disclaimer"> <p>欢迎登陆后台管理系统 </p> <%--<p style="text-align:right;"><a href="Register.aspx" style="color:white;">注册</a></p>--%> </div> </div> <div class="authent"> <div class="loader" style="height: 44px; 44px; margin-left: 28px;"> <div class="loader-inner ball-clip-rotate-multiple"> <div></div> <div></div> <div></div> </div> </div> <p>认证中...</p> </div> <div class="OverWindows"></div> <script type="text/javascript"> var canGetCookie = 0; //是否支持存储Cookie 0 不支持 1 支持 var ajaxmockjax = 1; //是否启用虚拟Ajax的请求响 0 不启用 1 启用 //默认账号密码 var truelogin = "admin"; var truepwd = "123456"; var CodeVal = 0; Code(); function Code() { if (canGetCookie == 1) { createCode("AdminCode"); var AdminCode = getCookieValue("AdminCode"); showCheck(AdminCode); } else { showCheck(createCode("")); } } function showCheck(a) { CodeVal = a; var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.clearRect(0, 0, 1000, 1000); ctx.font = "80px 'Hiragino Sans GB'"; ctx.fillStyle = "#E8DFE8"; ctx.fillText(a, 0, 100); } $(document).keypress(function (e) { // 回车键事件 if (e.which == 13) { $('input[type="button"]').click(); } }); //粒子背景特效 $('body').particleground({ dotColor: '#E8DFE8', lineColor: '#133b88' }); $('input[name="pwd"]').focus(function () { $(this).attr('type', 'password'); }); $('input[type="text"]').focus(function () { $(this).prev().animate({ 'opacity': '1' }, 200); }); $('input[type="text"],input[type="password"]').blur(function () { $(this).prev().animate({ 'opacity': '.5' }, 200); }); $('input[name="login"],input[name="pwd"]').keyup(function () { var Len = $(this).val().length; if (!$(this).val() == '' && Len >= 5) { $(this).next().animate({ 'opacity': '1', 'right': '30' }, 200); } else { $(this).next().animate({ 'opacity': '0', 'right': '20' }, 200); } }); var open = 0; layui.use('layer', function () { //var msgalert = '默认账号:' + truelogin + '<br/> 默认密码:' + truepwd; //var index = layer.alert(msgalert, { icon: 6, time: 4000, offset: 't', closeBtn: 0, title: '友情提示', btn: [], anim: 2, shade: 0 }); //layer.style(index, { // color: '#777' //}); //非空验证 $('input[type="button"]').click(function () { var login = $('input[name="login"]').val(); var pwd = $('input[name="pwd"]').val(); var code = $('input[name="code"]').val(); if (login == '') { ErroAlert('请输入您的账号'); } else if (pwd == '') { ErroAlert('请输入密码'); } else if (code == '' || code.length != 4) { ErroAlert('输入验证码'); } else { //认证中.. fullscreen(); $('.login').addClass('test'); //倾斜特效 setTimeout(function () { $('.login').addClass('testtwo'); //平移特效 }, 300); setTimeout(function () { $('.authent').show().animate({ right: -320 }, { easing: 'easeOutQuint', duration: 600, queue: false }); $('.authent').animate({ opacity: 1 }, { duration: 200, queue: false }).addClass('visible'); }, 500); //登陆 var JsonData = { login: login, pwd: pwd, code: code }; //此处做为ajax内部判断 var url = ""; if (JsonData.code.toUpperCase() == CodeVal.toUpperCase()) { url = "/Tools/Login.ashx"; } else { url = "Ajax/LoginFalse"; } AjaxPost(url, JsonData, function () { //ajax加载中 }, function (data) { console.log(data); //ajax返回 //认证完成 setTimeout(function () { $('.authent').show().animate({ right: 90 }, { easing: 'easeOutQuint', duration: 600, queue: false }); $('.authent').animate({ opacity: 0 }, { duration: 200, queue: false }).addClass('visible'); $('.login').removeClass('testtwo'); //平移特效 }, 2000); setTimeout(function () { $('.authent').hide(); $('.login').removeClass('test'); if (data.Status == 'ok') { //登录成功 $('.login div').fadeOut(100); $('.success').fadeIn(1000); $('.success').html(data.Text); //跳转操作 location.href = "Admin_index.aspx" } else { AjaxErro(data); } }, 2400); }) } }) }) var fullscreen = function () { elem = document.body; if (elem.webkitRequestFullScreen) { elem.webkitRequestFullScreen(); } else if (elem.mozRequestFullScreen) { elem.mozRequestFullScreen(); } else if (elem.requestFullScreen) { elem.requestFullscreen(); } else { //浏览器不支持全屏API或已被禁用 } } if (ajaxmockjax == 1) { $.mockjax({ url: 'Ajax/Login', status: 200, responseTime: 50, responseText: { "Status": "ok", "Text": "登陆成功<br /><br />欢迎回来" } }); $.mockjax({ url: 'Ajax/LoginFalse', status: 200, responseTime: 50, responseText: { "Status": "Erro", "Erro": "账号名或密码或验证码有误" } }); } </script> <div class="layui-layer-move"></div> </body> </html>
登录API接口
namespace SW163WebClient.Controllers { /// <summary> /// ==========2022-6-1(小金)=============== /// </summary> public class UserController : ApiController { /// <summary> /// 用户登录 /// </summary> /// <param name="strUser"></param> /// <param name="strPwd"></param> /// <returns></returns> [HttpGet] public object Login(string strUser, string strPwd) { if (!ValidateUser(strUser, strPwd)) { return new { bRes = false }; } FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(0, strUser, DateTime.Now, DateTime.Now.AddHours(1), true, string.Format("{0}&{1}", strUser, strPwd), FormsAuthentication.FormsCookiePath); //返回登录结果、用户信息、用户验证票据信息 var oUser = new UserInfo { bRes = true, UserName = strUser, Password = strPwd, Ticket = FormsAuthentication.Encrypt(ticket) }; //将身份信息保存在session中,验证当前请求是否是有效请求 //HttpContext.Current.Session[strUser] = oUser; return oUser; } public void ProcessRequest(HttpContext context) { throw new NotImplementedException(); } //校验用户名密码 private bool ValidateUser(string strUser, string strPwd) { SW163BLL.Administrators blladmin = new SW163BLL.Administrators(); DataTable dt = blladmin.GetList("UserName='" + strUser + "' and PassWord='" + SW163Common.DESEncrypt.Encrypt(strPwd) + "'").Tables[0]; if (dt.Rows.Count > 0) { return true; } else { return false; } } } public class UserInfo { public bool bRes { get; set; } public string UserName { get; set; } public string Password { get; set; } public string Ticket { get; set; } } }
Home/Index.aspx界面
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace SW163WebClient.Admin { /// <summary> /// 2022-6-1 小金 /// </summary> public partial class Index: System.Web.UI.Page { public SW163UI.BasePage bg = new SW163UI.BasePage(); public string UserName = string.Empty; public string Ticket = string.Empty; public int first = 1; protected void Page_Load(object sender, EventArgs e) { HttpCookie myCookie = HttpContext.Current.Request.Cookies["Web"]; if (bg.CookieYESNo()) { Response.Redirect("Login.aspx"); } else { if (myCookie["UserName"] != null && myCookie["Ticket"] != null && myCookie["date"] != null) { UserName = myCookie["UserName"]; Ticket = myCookie["Ticket"]; //第一次登录时间和当前时间对比,大于30分钟清除cookie; DateTime time = Convert.ToDateTime(myCookie["date"]); DateTime time1 = DateTime.Now; TimeSpan timeSpan = time1 - time; if (timeSpan.TotalMinutes >= 30) { UserName = ""; Ticket = ""; bg.ExitCookie(); } else { //当前时间大于第一次登录时间1分钟,非第一次登录 if (Convert.ToDateTime(time1.ToString("yyyy-MM-dd HH:mm")) > Convert.ToDateTime(time.ToString("yyyy-MM-dd HH:mm"))) { first = 2; myCookie["Ticket"] = "2"; } } } else { bg.ExitCookie(); Response.Redirect("Login.aspx"); } } } //退出登录 protected void lbtnExit_Click(object sender, EventArgs e) { bg.ExitCookie(); } } }
<head> <meta charset="utf-8"> <script type="text/javascript" src="js/jquery.min.js"></script> <script type="text/javascript"> //打开页面的时候保存票据信息 var UserName ="<%=UserName %>"; var Ticket = "<%=Ticket%>"; var first =<%=first%>; </script> </head>
<script> $(function () { $.ajax({ type: "get", url: "/api/Charging/GetAllChargingData", data: {}, beforeSend: function (XHR) { //发送ajax请求之前向http的head里面加入验证信息 XHR.setRequestHeader('Authorization', 'BasicAuth ' + Ticket); }, success: function (data, status) { if (status == "success") { if (first==1) { layer.alert(UserName + ":欢迎回家!"); } } }, error: function (e) { layer.alert("认证信息已过期!请重新登录", function () { //清除kooie $.ajax({ type: "post", url: "/Tools/Login.ashx", data: { "act":"clean_up"}, success: function (result) { window.location.href = "Login.aspx"; } }) }); }, complete: function () { } }); }); </script>
在WebAPI项目里面自定义一个类RequestAuthorizeAttribute,去继承AuthorizeAttribute这个类。然后重写OnAuthorization方法,在这个方法里面取到请求头的Ticket信息,再效验用户名和密码是否正确
/// <summary> /// ==========2022-6-1(小金)=============== /// 自定义此特性用于接口的身份验证 /// </summary> public class RequestAuthorizeAttribute : AuthorizeAttribute { //重写基类的验证方式,加入我们自定义的Ticket验证 public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext) { //从http请求的头里面获取身份验证信息,验证是否是请求发起方的ticket var authorization = actionContext.Request.Headers.Authorization; if ((authorization != null) && (authorization.Parameter != null)) { //解密用户ticket,并校验用户名密码是否匹配 var encryptTicket = authorization.Parameter; if (ValidateTicket(encryptTicket)) { base.IsAuthorized(actionContext); } else { HandleUnauthorizedRequest(actionContext); } } //如果取不到身份验证信息,并且不允许匿名访问,则返回未验证401 else { var attributes = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().OfType<AllowAnonymousAttribute>(); bool isAnonymous = attributes.Any(a => a is AllowAnonymousAttribute); if (isAnonymous) base.OnAuthorization(actionContext); else HandleUnauthorizedRequest(actionContext); } } //校验用户名密码 private bool ValidateTicket(string encryptTicket) { //解密Ticket var strTicket = FormsAuthentication.Decrypt(encryptTicket).UserData; //从Ticket里面获取用户名和密码 var index = strTicket.IndexOf("&"); string strUser = strTicket.Substring(0, index); string strPwd = strTicket.Substring(index + 1); SW163BLL.Administrators blladmin = new SW163BLL.Administrators(); DataTable dt = blladmin.GetList("UserName='" + strUser + "' and PassWord='" + SW163Common.DESEncrypt.Encrypt(strPwd) + "'").Tables[0]; if (dt.Rows.Count > 0) { return true; } else { return false; } } }
在具体的Api接口增加我们上面定义的自定义特性
/// <summary> /// ==========2022-6-1(小金)=============== /// </summary> public class ChargingController : BaseApiController { /// <summary> /// 得到所有数据 /// </summary> /// <returns>返回数据</returns> [HttpGet,Route("api/Charging/GetAllChargingData")] public string GetAllChargingData() { return "Success"; } /// <summary> /// 得到当前Id的所有数据 /// </summary> /// <param name="id">参数Id</param> /// <returns>返回数据</returns> [HttpGet, Route("api/Charging/GetAllChargingData")] public string GetAllChargingData(string id) { return "ChargingData" + id; } }
在API添加身份认证公共父类,子类只需继承父类认证即可
/// <summary> /// API 身份认证公共父类 /// /// ==========2022-6-1(小金)=============== /// /// origins配置允许访问的域名,多个域名以逗号分隔即可,域名一定要完整, /// 如果是ip地址前面要加上“http”,只使用IP的话一定会失效的。 /// 参数headers配置所支持的资源。 /// 参数methods配置支持的方法,get、post、put等。 /// 如果允许任意域名、任意资源、任意方法访问自己的webapi,则三个参数全部使用星号”*”即可 /// </summary> [RequestAuthorize] [EnableCors(origins: "*", headers: "*", methods: "*")] public class BaseApiController : ApiController { }
本文转自:https://www.cnblogs.com/landeanfen/p/5287064.html