From 06ae6ffe5a60e8effdc7b39e65ab6a4ba8e5bb3c Mon Sep 17 00:00:00 2001 From: dk96 Date: Wed, 27 Nov 2024 17:29:50 +0800 Subject: [PATCH] updates session function --- App_Code/authToken.cs | 78 ++++ App_Code/dbClass.cs | 29 ++ App_Code/globalClass.cs | 63 +++ BackEnd/Index.aspx | 21 + BackEnd/Index.aspx.cs | 2 +- BackEnd/Index.aspx.designer.cs | 6 +- BackEnd/Login.html | 97 +++++ BackEnd/Main.Master | 18 +- BackEnd/Main.Master.cs | 23 ++ BackEnd/api/signin.ashx | 1 + BackEnd/api/signin.ashx.cs | 86 +++++ BackEnd/assets/javascript/custom/Login.js | 55 +++ BackEnd/assets/javascript/custom/globalJS.js | 359 ++++++++++++++++++ BackEnd/assets/javascript/sha256.js | 250 ++++++++++++ .../vendor/jquery.cookie/jquery.cookie.js | 117 ++++++ Web.config | 59 +-- abbott_2024_event.csproj | 16 + bin/abbott_2024_event.dll | Bin 4608 -> 14848 bytes bin/abbott_2024_event.dll.config | 59 +-- packages.config | 4 + 20 files changed, 1269 insertions(+), 74 deletions(-) create mode 100644 App_Code/authToken.cs create mode 100644 App_Code/dbClass.cs create mode 100644 App_Code/globalClass.cs create mode 100644 BackEnd/Login.html create mode 100644 BackEnd/api/signin.ashx create mode 100644 BackEnd/api/signin.ashx.cs create mode 100644 BackEnd/assets/javascript/custom/Login.js create mode 100644 BackEnd/assets/javascript/custom/globalJS.js create mode 100644 BackEnd/assets/javascript/sha256.js create mode 100644 BackEnd/assets/vendor/jquery.cookie/jquery.cookie.js create mode 100644 packages.config diff --git a/App_Code/authToken.cs b/App_Code/authToken.cs new file mode 100644 index 0000000..58e2a13 --- /dev/null +++ b/App_Code/authToken.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Runtime.Serialization.Json; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Web.Services.Protocols; +using System.Security.Cryptography; +using System.Text; +using System.Web.SessionState; +using System.Data; +using System.Data.SqlClient; +using Dapper; +using Dapper.Contrib.Extensions; + +public class authToken +{ + + public string user_uid { get; set; } + public string user_id { get; set; } + public string user_name { get; set; } + public string user_perm { get; set; } + public Boolean user_isLogin { get; set; } + public string error_msg { get; set; } + public HttpRequest myRequest { get; set; } + + SqlConnection conn = new SqlConnection(globalClass.appsettings("DBConnectionString")); + + public authToken() { + conn.Execute("delete token where token_expireddate <= @token_expireddate", new { token_expireddate = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") }); + + HttpCookie tokenCookie = (HttpContext.Current.Request.Cookies["token"] == null) ? null : HttpContext.Current.Request.Cookies["token"]; + HttpCookie idCookie = (HttpContext.Current.Request.Cookies["id"] == null) ? null : HttpContext.Current.Request.Cookies["id"]; + + if (tokenCookie == null) + { + user_isLogin = false; + return; + } + + string token = tokenCookie["token"]; + string id = tokenCookie["uid"]; + + string tokenStr = string.Format("select * from token where token_key = '{0}' and user_uid = '{1}'", token, id); + + token loginToken = conn.QueryFirstOrDefault(tokenStr); + + if (loginToken == null) + { + tokenCookie.Expires = DateTime.Now.AddDays(-10); + tokenCookie.Values.Clear(); + HttpContext.Current.Response.Cookies.Set(tokenCookie); + HttpContext.Current.Response.Cookies.Add(new HttpCookie("token", "")); + user_isLogin = false; + return; + } + else { + idCookie.Expires = DateTime.Now.AddMinutes(60); + tokenCookie.Expires = DateTime.Now.AddDays(10); + + + HttpContext.Current.Response.Cookies.Add(tokenCookie); + HttpContext.Current.Response.Cookies.Add(idCookie); + } + + loginToken.token_expireddate = DateTime.Now.AddMinutes(60); + + conn.Update(loginToken); + + user_id = "admin"; + user_uid = "admin"; + user_name = "系統管理者"; + user_perm = "admin"; + user_isLogin = true; + + } +} \ No newline at end of file diff --git a/App_Code/dbClass.cs b/App_Code/dbClass.cs new file mode 100644 index 0000000..bf4e9c0 --- /dev/null +++ b/App_Code/dbClass.cs @@ -0,0 +1,29 @@ + +using Dapper.Contrib.Extensions; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +[Table("login")] +public class login +{ + [Key] + public string login_id { get; set; } = ""; + public string login_pwd { get; set; } = ""; +} + +[Table("token")] +public class token +{ + [JsonIgnore] + [Key] + public int token_sn { get; set; } = 0; + public string token_key { get; set; } = ""; + public string user_uid { get; set; } = ""; + public string user_id { get; set; } = ""; + public string token_isremember { get; set; } = ""; + public DateTime token_createdate { get; set; } = DateTime.Now; + public DateTime token_expireddate { get; set; } = DateTime.Now; +} \ No newline at end of file diff --git a/App_Code/globalClass.cs b/App_Code/globalClass.cs new file mode 100644 index 0000000..db06070 --- /dev/null +++ b/App_Code/globalClass.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Runtime.Serialization.Json; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Web.Services.Protocols; +using System.Security.Cryptography; +using System.Text; +using System.Web.SessionState; +using System.Data; +using System.Drawing.Imaging; +using System.Drawing; +using System.IO; +using System.Data.SqlClient; +using System.Configuration; + +public static class globalClass +{ + public static string CreateRandomCode(int Number) + { + string allChar = "0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"; + string[] allCharArray = allChar.Split(','); + string randomCode = ""; + + Random rand = new Random(Guid.NewGuid().GetHashCode()); + for (int i = 0; i <= Number - 1; i++) + { + int t = rand.Next(allCharArray.Length); + randomCode += allCharArray[t]; + } + return randomCode; + } + + public static string appsettings(string key) + { + + return ConfigurationManager.ConnectionStrings[key].ConnectionString; + } + + public static string SHA256_Encode(string value) + { + byte[] bytValue = System.Text.Encoding.UTF8.GetBytes(value); + try + { + SHA256 sha256 = new SHA256CryptoServiceProvider(); + byte[] retVal = sha256.ComputeHash(bytValue); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < retVal.Length; i++) + { + sb.Append(retVal[i].ToString("x2")); + } + return sb.ToString(); + } + catch (Exception ex) + { + throw new Exception("GetSHA256HashFromString() fail,error:" + ex.Message); + } + + } + +} \ No newline at end of file diff --git a/BackEnd/Index.aspx b/BackEnd/Index.aspx index 23792f2..ac8e38d 100644 --- a/BackEnd/Index.aspx +++ b/BackEnd/Index.aspx @@ -1,5 +1,26 @@ <%@ Page Title="" Language="C#" MasterPageFile="~/BackEnd/Main.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="abbott_2024_event.BackEnd.Index" %> + + +
+ +
+ +

Page title

+
+ + +
+ +
+ + +
+ +
+ +
+
diff --git a/BackEnd/Index.aspx.cs b/BackEnd/Index.aspx.cs index 44f8a73..ea12693 100644 --- a/BackEnd/Index.aspx.cs +++ b/BackEnd/Index.aspx.cs @@ -11,7 +11,7 @@ namespace abbott_2024_event.BackEnd { protected void Page_Load(object sender, EventArgs e) { - + } } } \ No newline at end of file diff --git a/BackEnd/Index.aspx.designer.cs b/BackEnd/Index.aspx.designer.cs index 1356d2b..5585645 100644 --- a/BackEnd/Index.aspx.designer.cs +++ b/BackEnd/Index.aspx.designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ -// +// <自動產生> // 這段程式碼是由工具產生的。 // // 變更這個檔案可能會導致不正確的行為,而且如果已重新產生 -// 變更將會遺失。 -// +// 程式碼,則會遺失變更。 +// //------------------------------------------------------------------------------ namespace abbott_2024_event.BackEnd diff --git a/BackEnd/Login.html b/BackEnd/Login.html new file mode 100644 index 0000000..45c2e37 --- /dev/null +++ b/BackEnd/Login.html @@ -0,0 +1,97 @@ + + + + + + + + 後台登入 + + + + + + + + + + + + + + + + + +
+
+

+ + + + + + + + + Sign In +

+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BackEnd/Main.Master b/BackEnd/Main.Master index bbb9731..4c6da90 100644 --- a/BackEnd/Main.Master +++ b/BackEnd/Main.Master @@ -137,22 +137,8 @@
-
- -
- -

Page title

-
- -
- -
- - - -
-
-
+ +
diff --git a/BackEnd/Main.Master.cs b/BackEnd/Main.Master.cs index c42adf2..9af5f60 100644 --- a/BackEnd/Main.Master.cs +++ b/BackEnd/Main.Master.cs @@ -7,11 +7,34 @@ using System.Web.UI.WebControls; namespace abbott_2024_event.BackEnd { + public partial class Main : System.Web.UI.MasterPage { + public authToken authToken; protected void Page_Load(object sender, EventArgs e) { + authToken = new authToken(); + if (authToken.user_isLogin == false) { + HttpCookie tokenCookie = (HttpContext.Current.Request.Cookies["token"] == null) ? null : HttpContext.Current.Request.Cookies["token"]; + HttpCookie idCookie = (HttpContext.Current.Request.Cookies["id"] == null) ? null : HttpContext.Current.Request.Cookies["id"]; + + HttpContext.Current.Response.Cookies["token"].Expires = DateTime.Now.AddDays(-1); + + if (tokenCookie != null) { + tokenCookie.Expires = DateTime.Now.AddDays(-10); + tokenCookie.Values.Clear(); + + HttpContext.Current.Response.Cookies.Set(tokenCookie); + + HttpContext.Current.Response.Cookies.Add(new HttpCookie("token", "")); + } + + + + Response.Redirect("Login.html?isLogout=true"); + return; + } } } } \ No newline at end of file diff --git a/BackEnd/api/signin.ashx b/BackEnd/api/signin.ashx new file mode 100644 index 0000000..f75f127 --- /dev/null +++ b/BackEnd/api/signin.ashx @@ -0,0 +1 @@ +<%@ WebHandler Language="C#" CodeBehind="signin.ashx.cs" Class="abbott_2024_event.BackEnd.api.signin" %> diff --git a/BackEnd/api/signin.ashx.cs b/BackEnd/api/signin.ashx.cs new file mode 100644 index 0000000..0feb379 --- /dev/null +++ b/BackEnd/api/signin.ashx.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Linq; +using System.Runtime.Serialization.Json; +using System.Web; +using System.Web.SessionState; +using Dapper; +using Dapper.Contrib.Extensions; + + +namespace abbott_2024_event.BackEnd.api +{ + /// + /// signin 的摘要描述 + /// + public class signin : IHttpHandler, IReadOnlySessionState + { + SqlConnection conn = new SqlConnection(globalClass.appsettings("DBConnectionString")); + + public void ProcessRequest(HttpContext context) + { + result objRet = new result(); + DataContractJsonSerializer json = new DataContractJsonSerializer(objRet.GetType()); + context.Response.ContentType = "application/json;charset=utf-8"; + + string id = (context.Request["id"] == null) ? "" : context.Request["id"].ToString(); + string pwd = (context.Request["pwd"] == null) ? "" : context.Request["pwd"].ToString(); + + login login = conn.QueryFirstOrDefault("select * from login where login_id = @login_id and login_pwd = @login_pwd", new { login_id = id, login_pwd = pwd }); + + if (login == null) + { + objRet.ret = "no"; + objRet.err_code = "0001"; + objRet.message = "帳號或密碼錯誤"; + json.WriteObject(context.Response.OutputStream, objRet); + return; + } + + string token_key = globalClass.CreateRandomCode(32); + string user_uid = id; + + token newToken = new token(); + newToken.user_uid = user_uid; + newToken.user_id = id; + newToken.token_key = token_key; + newToken.token_isremember = "N"; + newToken.token_expireddate = DateTime.Now.AddMinutes(60); + newToken.token_createdate = DateTime.Now; + + HttpCookie tokenCookie = new HttpCookie("token"); + HttpCookie idCookie = new HttpCookie("id"); + tokenCookie["token"] = token_key; + tokenCookie["uid"] = user_uid; + idCookie["id"] = id; + + tokenCookie.Expires = DateTime.Now.AddMinutes(60); + idCookie.Expires = DateTime.Now.AddDays(31); + + conn.Insert(newToken); + + context.Response.Cookies.Add(tokenCookie); + context.Response.Cookies.Add(idCookie); + + objRet.ret = "yes"; + json.WriteObject(context.Response.OutputStream, objRet); + } + + public class result + { + public string ret = "no"; + public string err_code = "0000"; + public string message = ""; + } + + + public bool IsReusable + { + get + { + return false; + } + } + } +} \ No newline at end of file diff --git a/BackEnd/assets/javascript/custom/Login.js b/BackEnd/assets/javascript/custom/Login.js new file mode 100644 index 0000000..49f5f44 --- /dev/null +++ b/BackEnd/assets/javascript/custom/Login.js @@ -0,0 +1,55 @@ + +$(document).ready(function () { + if ($.UrlParam("isLogout") != "true") { + if ($.cookie("token_key") != null) { + if ($.cookie("token_key") != "") { + location.href = "/BackEnd/Index.aspx"; + //alert("has token_key value :" + $.cookie("token_key")); + } + + } + } + + $('#login_btn').on('click', function () { + var id = $("#login_id").val(); + var pwd = $("#login_pwd").val(); + + var err_msg = ""; + + if (id === "") { + err_msg += "請輸入帳號!\n"; + } + + if (pwd === "") { + err_msg += "請輸入密碼!\n"; + } + + if (err_msg !== "") { + alert(err_msg); + return; + } + + pwd = sha256_digest(pwd); + + var formData = { + id: id, + pwd: pwd + } + + $.ajax({ + url: "api/signin.ashx", + type: "POST", + data: formData, + success: function (data, textStatus, jqXHR) { + if (data.ret == "yes") { + location.href = "Index.aspx"; + } else { + alert(data.message); + } + }, + error: function (jqXHR, textStatus, errorThrown) { + alert('网絡或伺服器发生错误,请稍后重试!'); + } + }); + }); +}); \ No newline at end of file diff --git a/BackEnd/assets/javascript/custom/globalJS.js b/BackEnd/assets/javascript/custom/globalJS.js new file mode 100644 index 0000000..70b605b --- /dev/null +++ b/BackEnd/assets/javascript/custom/globalJS.js @@ -0,0 +1,359 @@ +//const zhTw = require("../../vendor/fullcalendar/locale/zh-tw"); + +(function ($, document) { + (function ($) { + $.UrlParam = function (name) { + //宣告正規表達式 + var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); + /* + * window.location.search 獲取URL ?之後的參數(包含問號) + * substr(1) 獲取第一個字以後的字串(就是去除掉?號) + * match(reg) 用正規表達式檢查是否符合要查詢的參數 + */ + var r = window.location.search.substr(1).match(reg); + //如果取出的參數存在則取出參數的值否則回穿null + if (r != null) return unescape(r[2]); return null; + } + })(jQuery); + + if ($.cookie) { + $.cookieKey = function (CookieName, KeyName, Value, Options) { + var reg = new RegExp("(?:([^=]+)=([^&]*)&?)", "ig"), + match = null, + matches = []; + var cookieVal = $.cookie(CookieName); + while (match = reg.exec(cookieVal)) { + if (KeyName.toLowerCase() == match[1].toLowerCase()) { + if (Value) { //we are updating, collect all values + matches.push([match[1], Value]); + } + else { + return match[2]; //we are getting, sub key found just return it + } + } + else if (Value) { //we are updating, collect all values + matches.push([match[1], match[2]]); + } + } + + if (Value) { //we are updating, update values + updatedValue = "", + sep = ""; + for (i = 0; i < matches; i++) { + updatedValue += sep + matches[i][0] + "=" + matches[i][1]; + sep = "&" + } + $.cookie(CookieName, updatedValue, Options); + } + else return null;//we are getting, value not found + } + } +})(jQuery, document); + +// 對Date的擴充套件,將 Date 轉化為指定格式的String +// 月(M)、日(d)、小時(h)、分(m)、秒(s)、季度(q) 可以用 1-2 個佔位符, +// 年(y)可以用 1-4 個佔位符,毫秒(S)只能用 1 個佔位符(是 1-3 位的數字) +// 例子: +// (new Date()).format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 +// (new Date()).format("yyyy-M-d hⓜ️s.S") ==> 2006-7-2 8:9:4.18 +Date.prototype.format = function (fmt) { + var o = { + "M+": this.getMonth() + 1, //月份 + "d+": this.getDate(), //日 + "h+": this.getHours(), //小時 + "m+": this.getMinutes(), //分 + "s+": this.getSeconds(), //秒 + "q+": Math.floor((this.getMonth() + 3) / 3), //季度 + "S": this.getMilliseconds() //毫秒 + }; + if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); + for (var k in o) + if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); + return fmt; +} + +Date.prototype.addSeconds = function (seconds) { + this.setSeconds(this.getSeconds() + seconds); + return this; +} + +Date.prototype.addMinutes = function (minutes) { + this.setMinutes(this.getMinutes() + minutes); + return this; +} + +Date.prototype.addHours = function (hours) { + this.setHours(this.getHours() + hours); + return this; +} + +Date.prototype.addDays = function (days) { + this.setDate(this.getDate() + days); + return this; +} + +Date.prototype.addMonths = function (months) { + this.setMonth(this.getMonth() + months); + return this; +} + +Date.prototype.addYears = function (years) { + this.setFullYear(this.getFullYear() + years); + return this; +} + +function diffSeconds(milliseconds) { + return Math.floor(milliseconds / 1000); +} + +function diffMinutes(milliseconds) { + return Math.floor(milliseconds / 1000 / 60); +} + +function diffHours(milliseconds) { + return Math.floor(milliseconds / 1000 / 60 / 60); +} + +function diffDays(milliseconds) { + return Math.floor(milliseconds / 1000 / 60 / 60 / 24); +} + +function padding(num, length) { + for (var len = (num + "").length; len < length; len = num.length) { + num = "0" + num; + } + return num; +} + +function clearChildren(element) { + for (var i = 0; i < element.childNodes.length; i++) { + var e = element.childNodes[i]; + if (e.tagName) switch (e.tagName.toLowerCase()) { + case 'input': + switch (e.type) { + case "radio": + case "checkbox": break; + case "hidden": e.value = ''; break; + case "button": + case "submit": + case "text": e.value = ''; break; + case "image": break; + default: if (e.type != "checkbox") { e.value = ''; }; break; + } + break; + case 'select': e.selectedIndex = 0; break; + case 'textarea': e.innerHTML = ''; break; + default: clearChildren(e); + } + } + + $(element).children().find('textarea').each(function () { + $(this).val(''); + }); + + $(element).children().find('select').each(function () { + $(this).prop('selectedIndex', 0); + }); +} + + + +$(document).ready(function () { + $('.modal').on("hidden.bs.modal", function (e) { + clearChildren(this); + if ($('.modal:visible').length) { + $('.modal-backdrop').first().css('z-index', parseInt($('.modal:visible').last().css('z-index')) - 10); + $('body').addClass('modal-open'); + } + + + }).on("show.bs.modal", function (e) { + if ($('.modal:visible').length) { + $('.modal-backdrop.in').first().css('z-index', parseInt($('.modal:visible').last().css('z-index')) + 10); + $(this).css('z-index', parseInt($('.modal-backdrop.in').first().css('z-index')) + 10); + } + }); + + (function ($) { + $.UrlParam = function (name) { + //宣告正規表達式 + var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); + /* + * window.location.search 獲取URL ?之後的參數(包含問號) + * substr(1) 獲取第一個字以後的字串(就是去除掉?號) + * match(reg) 用正規表達式檢查是否符合要查詢的參數 + */ + var r = window.location.search.substr(1).match(reg); + //如果取出的參數存在則取出參數的值否則回穿null + if (r != null) return unescape(r[2]); return null; + } + })(jQuery); + + + +}); + +function logout() { + + if (confirm('確認要登出系統?')) { + $.ajax({ + url: "/AuthApi/logout", + type: "POST", + data: null, + success: function (data, textStatus, jqXHR) { + if (data.ret == "yes") { + location.href = "/Home/Login"; + //location.href = "/BackEnd/nounsList"; + + } else { + alert(data.message); + } + }, + error: function (jqXHR, textStatus, errorThrown) { + alert('網路或伺服器發生錯誤,請稍後重試!'); + } + }); + } + +} +String.prototype.isDate = function () { + var p; + var re1 = /(\d{4})[年./-](\d{1,2})[月./-](\d{1,2})[日]?$/; + var re2 = /(\d{1,2})[月./-](\d{1,2})[日./-](\d{2})[年]?$/; + var re3 = /(\d{1,2})[月./-](\d{1,2})[日./-](\d{4})[年]?$/; + if (re1.test(this)) { + p = re1.exec(this); + return new Date(p[1], p[2], p[3]); + } + if (re2.test(this)) { + p = re2.exec(this); + return new Date(p[3], p[1], p[2]); + } + if (re3.test(this)) { + p = re3.exec(this); + return new Date(p[3], p[1], p[2]); + } + return ""; +} + +String.prototype.isDigit = function () { + var s = this.Trim(); + return (s.replace(/\d/g, "").length == 0); +} + +/*** 檢查是否由數字字母和下劃線組成 ***/ +String.prototype.isAlpha = function () { + return (this.replace(/\w/g, "").length == 0); +} + +/*** 檢查是否為數 ***/ +String.prototype.isNumber = function () { + var s = this.Trim(); + return (s.search(/^[+-]?[0-9.]*$/) >= 0); +} + +/*** 返回字節數 ***/ +String.prototype.lenb = function () { + return this.replace(/[^\x00-\xff]/g, "**").length; +} + +/*** 檢查是否包含漢字 ***/ +String.prototype.isInChinese = function () { + return (this.length != this.replace(/[^\x00-\xff]/g, "**").length); +} + +/*** 刪除首尾空格 ***/ +String.prototype.Trim = function () { + return this.replace(/(^\s*)|(\s*$)/g, ""); +} +const apiUrl = '' /*** 有值的話需要斜線結尾喔 **/ + +/*** 簡單的email檢查 ***/ +String.prototype.isEmail = function () { + var strr; + var mail = this; + var re = /(\w+@\w+\.\w+)(\.{0,1}\w*)(\.{0,1}\w*)/i; + re.exec(mail); + if (RegExp.$3 != "" && RegExp.$3 != "." && RegExp.$2 != ".") + strr = RegExp.$1 + RegExp.$2 + RegExp.$3; + else + if (RegExp.$2 != "" && RegExp.$2 != ".") + strr = RegExp.$1 + RegExp.$2; + else + strr = RegExp.$1; + return (strr == mail); +} + +function checkUrl(url) { + var pattern = new RegExp('^(https?:\\/\\/)?' + // protocol + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name + '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address + '(\\:\\d+)?(\\/[-a-z\\d@%_.~+]*)*' + // port and path + '(\\?[;&a-z\\d@%_.~+=-]*)?' + // query string + '(\\#[-a-z\\d@_]*)?$', 'i'); // fragment locator + return !!pattern.test(url); +} + +//數字處理為有千分位 +function AppendComma(number) { + var parts = number.toString().split("."); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); + return parts.join("."); +} +function AppendComma2(n) { + if (!/^((-*\d+)|(0))$/.test(n)) { + var newValue = /^((-*\d+)|(0))$/.exec(n); + if (newValue != null) { + if (parseFloat(newValue, 10)) { + n = newValue; + } + else { + n = '0'; + } + } + else { + n = '0'; + } + } + if (parseFloat(n, 10) == 0) { + n = '0'; + } + else { + n = parseFloat(n, 10).toString(); + } + + n += ''; + var arr = n.split('.'); + var re = /(\d{1,3})(?=(\d{3})+$)/g; + return arr[0].replace(re, '$1,') + (arr.length == 2 ? '.' + arr[1] : ''); +} +//將有千分位的數值轉為一般數字 +function RemoveComma(n) { + return n.replace(/[,]+/g, ''); +} +//調整千分位 +function AdjustComma(item, length) { + var originalValue = $.trim($(item).val()).length > length + ? $.trim($(item).val()).substr(0, length) + : $.trim($(item).val()); + + $(item).val(AppendComma(originalValue)); +} +//動態調整輸入欄位的長度 +function TextAreaLength(item, length) { + if (item.value.length > length) { + item.value = item.value.substring(0, length); + } +} + +function thousands(value) { + if (value) { + value += ""; + var arr = value.split("."); + var re = /(\d{1,3})(?=(\d{3})+$)/g; + + return arr[0].replace(re, "$1,") + (arr.length == 2 ? "." + arr[1] : ""); + } else { + return '' + } +} \ No newline at end of file diff --git a/BackEnd/assets/javascript/sha256.js b/BackEnd/assets/javascript/sha256.js new file mode 100644 index 0000000..cd422dc --- /dev/null +++ b/BackEnd/assets/javascript/sha256.js @@ -0,0 +1,250 @@ +/* +* A JavaScript implementation of the SHA256 hash function. +* +* FILE: sha256.js +* VERSION: 0.8 +* AUTHOR: Christoph Bichlmeier +* +* NOTE: This version is not tested thoroughly! +* +* Copyright (c) 2003, Christoph Bichlmeier +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the copyright holder nor the names of contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* ====================================================================== +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS +* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* SHA256 logical functions */ +function rotateRight(n,x) { + return ((x >>> n) | (x << (32 - n))); +} +function choice(x,y,z) { + return ((x & y) ^ (~x & z)); +} +function majority(x,y,z) { + return ((x & y) ^ (x & z) ^ (y & z)); +} +function sha256_Sigma0(x) { + return (rotateRight(2, x) ^ rotateRight(13, x) ^ rotateRight(22, x)); +} +function sha256_Sigma1(x) { + return (rotateRight(6, x) ^ rotateRight(11, x) ^ rotateRight(25, x)); +} +function sha256_sigma0(x) { + return (rotateRight(7, x) ^ rotateRight(18, x) ^ (x >>> 3)); +} +function sha256_sigma1(x) { + return (rotateRight(17, x) ^ rotateRight(19, x) ^ (x >>> 10)); +} +function sha256_expand(W, j) { + return (W[j&0x0f] += sha256_sigma1(W[(j+14)&0x0f]) + W[(j+9)&0x0f] + +sha256_sigma0(W[(j+1)&0x0f])); +} + +/* Hash constant words K: */ +var K256 = new Array( + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +); + +/* global arrays */ +var ihash, count, buffer; +var sha256_hex_digits = "0123456789abcdef"; + +/* Add 32-bit integers with 16-bit operations (bug in some JS-interpreters: +overflow) */ +function safe_add(x, y) +{ + var lsw = (x & 0xffff) + (y & 0xffff); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xffff); +} + +/* Initialise the SHA256 computation */ +function sha256_init() { + ihash = new Array(8); + count = new Array(2); + buffer = new Array(64); + count[0] = count[1] = 0; + ihash[0] = 0x6a09e667; + ihash[1] = 0xbb67ae85; + ihash[2] = 0x3c6ef372; + ihash[3] = 0xa54ff53a; + ihash[4] = 0x510e527f; + ihash[5] = 0x9b05688c; + ihash[6] = 0x1f83d9ab; + ihash[7] = 0x5be0cd19; +} + +/* Transform a 512-bit message block */ +function sha256_transform() { + var a, b, c, d, e, f, g, h, T1, T2; + var W = new Array(16); + + /* Initialize registers with the previous intermediate value */ + a = ihash[0]; + b = ihash[1]; + c = ihash[2]; + d = ihash[3]; + e = ihash[4]; + f = ihash[5]; + g = ihash[6]; + h = ihash[7]; + + /* make 32-bit words */ + for(var i=0; i<16; i++) + W[i] = ((buffer[(i<<2)+3]) | (buffer[(i<<2)+2] << 8) | (buffer[(i<<2)+1] +<< 16) | (buffer[i<<2] << 24)); + + for(var j=0; j<64; j++) { + T1 = h + sha256_Sigma1(e) + choice(e, f, g) + K256[j]; + if(j < 16) T1 += W[j]; + else T1 += sha256_expand(W, j); + T2 = sha256_Sigma0(a) + majority(a, b, c); + h = g; + g = f; + f = e; + e = safe_add(d, T1); + d = c; + c = b; + b = a; + a = safe_add(T1, T2); + } + + /* Compute the current intermediate hash value */ + ihash[0] += a; + ihash[1] += b; + ihash[2] += c; + ihash[3] += d; + ihash[4] += e; + ihash[5] += f; + ihash[6] += g; + ihash[7] += h; +} + +/* Read the next chunk of data and update the SHA256 computation */ +function sha256_update(data, inputLen) { + var i, index, curpos = 0; + /* Compute number of bytes mod 64 */ + index = ((count[0] >> 3) & 0x3f); + var remainder = (inputLen & 0x3f); + + /* Update number of bits */ + if ((count[0] += (inputLen << 3)) < (inputLen << 3)) count[1]++; + count[1] += (inputLen >> 29); + + /* Transform as many times as possible */ + for(i=0; i+63> 3) & 0x3f); + buffer[index++] = 0x80; + if(index <= 56) { + for(var i=index; i<56; i++) + buffer[i] = 0; + } else { + for(var i=index; i<64; i++) + buffer[i] = 0; + sha256_transform(); + for(var i=0; i<56; i++) + buffer[i] = 0; + } + buffer[56] = (count[1] >>> 24) & 0xff; + buffer[57] = (count[1] >>> 16) & 0xff; + buffer[58] = (count[1] >>> 8) & 0xff; + buffer[59] = count[1] & 0xff; + buffer[60] = (count[0] >>> 24) & 0xff; + buffer[61] = (count[0] >>> 16) & 0xff; + buffer[62] = (count[0] >>> 8) & 0xff; + buffer[63] = count[0] & 0xff; + sha256_transform(); +} + +/* Split the internal hash values into an array of bytes */ +function sha256_encode_bytes() { + var j=0; + var output = new Array(32); + for(var i=0; i<8; i++) { + output[j++] = ((ihash[i] >>> 24) & 0xff); + output[j++] = ((ihash[i] >>> 16) & 0xff); + output[j++] = ((ihash[i] >>> 8) & 0xff); + output[j++] = (ihash[i] & 0xff); + } + return output; +} + +/* Get the internal hash as a hex string */ +function sha256_encode_hex() { + var output = new String(); + for(var i=0; i<8; i++) { + for(var j=28; j>=0; j-=4) + output += sha256_hex_digits.charAt((ihash[i] >>> j) & 0x0f); + } + return output; +} + +/* Main function: returns a hex string representing the SHA256 value of the +given data */ +function sha256_digest(data) { + sha256_init(); + sha256_update(data, data.length); + sha256_final(); + return sha256_encode_hex(); +} + +/* test if the JS-interpreter is working properly */ +function sha256_self_test() +{ + return sha256_digest("message digest") == +"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"; +} + + diff --git a/BackEnd/assets/vendor/jquery.cookie/jquery.cookie.js b/BackEnd/assets/vendor/jquery.cookie/jquery.cookie.js new file mode 100644 index 0000000..c7f3a59 --- /dev/null +++ b/BackEnd/assets/vendor/jquery.cookie/jquery.cookie.js @@ -0,0 +1,117 @@ +/*! + * jQuery Cookie Plugin v1.4.1 + * https://github.com/carhartl/jquery-cookie + * + * Copyright 2013 Klaus Hartl + * Released under the MIT license + */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // CommonJS + factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +}(function ($) { + + var pluses = /\+/g; + + function encode(s) { + return config.raw ? s : encodeURIComponent(s); + } + + function decode(s) { + return config.raw ? s : decodeURIComponent(s); + } + + function stringifyCookieValue(value) { + return encode(config.json ? JSON.stringify(value) : String(value)); + } + + function parseCookieValue(s) { + if (s.indexOf('"') === 0) { + // This is a quoted cookie as according to RFC2068, unescape... + s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); + } + + try { + // Replace server-side written pluses with spaces. + // If we can't decode the cookie, ignore it, it's unusable. + // If we can't parse the cookie, ignore it, it's unusable. + s = decodeURIComponent(s.replace(pluses, ' ')); + return config.json ? JSON.parse(s) : s; + } catch(e) {} + } + + function read(s, converter) { + var value = config.raw ? s : parseCookieValue(s); + return $.isFunction(converter) ? converter(value) : value; + } + + var config = $.cookie = function (key, value, options) { + + // Write + + if (value !== undefined && !$.isFunction(value)) { + options = $.extend({}, config.defaults, options); + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setTime(+t + days * 864e+5); + } + + return (document.cookie = [ + encode(key), '=', stringifyCookieValue(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // Read + + var result = key ? undefined : {}; + + // To prevent the for loop in the first place assign an empty array + // in case there are no cookies at all. Also prevents odd result when + // calling $.cookie(). + var cookies = document.cookie ? document.cookie.split('; ') : []; + + for (var i = 0, l = cookies.length; i < l; i++) { + var parts = cookies[i].split('='); + var name = decode(parts.shift()); + var cookie = parts.join('='); + + if (key && key === name) { + // If second argument (value) is a function it's a converter... + result = read(cookie, value); + break; + } + + // Prevent storing a cookie that we couldn't decode. + if (!key && (cookie = read(cookie)) !== undefined) { + result[name] = cookie; + } + } + + return result; + }; + + config.defaults = {}; + + $.removeCookie = function (key, options) { + if ($.cookie(key) === undefined) { + return false; + } + + // Must not alter options, thus extending a fresh object... + $.cookie(key, '', $.extend({}, options, { expires: -1 })); + return !$.cookie(key); + }; + +})); diff --git a/Web.config b/Web.config index 2344486..1072c43 100644 --- a/Web.config +++ b/Web.config @@ -4,38 +4,43 @@ https://go.microsoft.com/fwlink/?LinkId=169433 --> + + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/abbott_2024_event.csproj b/abbott_2024_event.csproj index d72c92c..acaf3c6 100644 --- a/abbott_2024_event.csproj +++ b/abbott_2024_event.csproj @@ -113,6 +113,7 @@ packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll + packages\System.Security.Cryptography.Pkcs.8.0.1\lib\net462\System.Security.Cryptography.Pkcs.dll @@ -394,6 +395,8 @@ + + @@ -562,6 +565,7 @@ + @@ -8701,6 +8705,7 @@ + @@ -13148,11 +13153,18 @@ + + + + + + signin.ashx + Index.aspx ASPXCodeBehind @@ -13162,6 +13174,7 @@ Main.Master + ASPXCodeBehind Main.Master @@ -13177,6 +13190,7 @@ + @@ -17638,6 +17652,8 @@ + + Web.config diff --git a/bin/abbott_2024_event.dll b/bin/abbott_2024_event.dll index 557cc9e132e79106a371676ce1111a1271eeb269..efc80f2cd56521a3ffb316866dba98cdf96acc1f 100644 GIT binary patch literal 14848 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

NBj#ZHGp78b^l>J3qO~ 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 CDj{V6 delta 1357 zcmZuxUu;uV82_Dn*N&FCU5Xp9HFDcq9pg{ab&V5;OXmixgN8z_gouuIz3a-lR@*xx z#@MQaOoNHbNqo@{F7<@AE-{OnJ2hf<9h{5=sud5jeCpo|K`+nb_ z^PSVv+i)7850<7@--s2kKGqO4H;V}1CBfYkpB|vqq#36P7|b=Gjm`hVfX4E-;m+H; zfMd!N2R_%A16w=J03Bps?FalapzW2XM87xgA<3lamG2456DOwXb;!@ePR|))v;hqH zlL+qctNm$A($wI>oJqVL5b{=Zc$*iygW9ex)v+Q56F|yG9#C23H zbzy3%TAe_h11OQk-$Df|_j6)Nb7Pfp3D0X?xI!UrTt$fVVWDt4;}u4S@qNaxIOby% zDdu&?e^~hiKMOAw5u#h@(Y`0|Us{(|cjG!OUR2oeq_)bv^l>ACniJINea)qhF;~_$ zJ%ceitIociS_a4Y&fCb|OXoa<2w{`A6Hl3{*m)d5jMOw+7q+LU0UfFO2B-dj)Ac~2 z3#T_E(l1M;64a`OAL-H_&OJzJ0$T{(*g@#U0HJ|F#{G=-Fi~DrINv$x112R|jz*S;;S! z>`|o8)f{^veA3S8;Y>pQ;qUI8v$J`mvITqkXeFDM-&nl{l35C?DiMbli+On=@Rtor}BrOSN2=` + + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..9c782f9 --- /dev/null +++ b/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file