令牌桶生成令牌_使用令牌的经典ASP登录系统

令牌桶生成令牌

This demonstration started out as a follow up to some recently posted questions on the subject of logging in: http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_28634665.html and http://www.experts-exchange.com/Programming/Languages/Scripting/ASP/Q_28641013.html. The Asker was originally trying to find a way to eliminate the scenario where his users are staring at the screen thinking and writing only to click the submit button and find out that they were logged out and their updates are lost.

该演示首先是对最近发布的有关登录主题的一些问题的跟踪: http: //www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_28634665.htmlhttp:// www。 expert-exchange.com/Programming/Languages/Scripting/ASP/Q_28641013.html 。 Asker最初试图找到一种方法来消除这种情况,即他的用户盯着屏幕进行思考和写作,只是单击“提交”按钮并发现他们已注销并且更新丢失。

I know many people may try adjusting the script timeout or the session timeout only to find this does not work as expected. The reason is they forget about the idle timeout that is set to the default of 5 minutes in IIS: https://technet.microsoft.com/en-us/library/cc771956%28v=ws.10%29.aspx

我知道很多人可能会尝试调整脚本超时或会话超时,只是发现这无法按预期工作。 原因是他们忘记了IIS中设置为5分钟默认值的空闲超时: https : //technet.microsoft.com/zh-cn/library/cc771956%28v=ws.10%29.aspx

Simply adjusting the idle timeout to something longer coupled with adjusting your session.timeout=60 (for 60 minutes) is not the best solution. Session variables can be reset if the app pool recycles or crashes: http://weblogs.asp.net/owscott/why-is-the-iis-default-app-pool-recycle-set-to-1740-minutes

简单地将空闲超时调整为更长的时间再调整会话。timeout= 60(60分钟)不是最佳解决方案。 如果应用程序池回收或崩溃,则可以重置会话变量: http : //weblogs.asp.net/owscott/why-is-the-iis-default-app-pool-recycle-set-to-1740-minutes

An alternative is to issue a new and unique token at each log in. Then set a cookie using the generated token as well as storing the token in a user table in your database. On each page load, look up the cookie with the token, and look for a match in the user login table. If a match is found, test if the expiration date is valid. When everything checks out, give access to the page.

一种替代方法是在每次登录时发出一个新的唯一令牌。然后使用生成的令牌设置cookie,并将该令牌存储在数据库的用户表中。 在每次页面加载时,请使用令牌查找cookie,然后在用户登录表中查找匹配项。 如果找到匹配项,则测试到期日期是否有效。 一切都检查完后,请访问该页面。

I have created a more detailed sample using this process that also allows for user levels. This demonstration is intended to show the process and is not meant to be a production-ready login system.

我使用此过程创建了一个更详细的示例,该示例也允许用户使用。 该演示旨在显示过程,而不是成为可用于生产的登录系统。

I am using MSSQL SERVER 2012 with two tables and one view. The first table is a users table where I am storing the user's name, email, hashed password and user level.

我正在使用具有两个表和一个视图的MSSQL SERVER 2012。 第一个表是一个用户表,其中存储了用户名,电子邮件,哈希密码和用户级别。

The second table is the login transaction table where I store the tokens with a user id, the token expiration, the IP used to log in and and the timestamp of the log in. I have the field LoggedTimeStamp set to the current date/time using getdate().

第二个表是登录事务表,我在其中存储带有用户ID的令牌,令牌到期,用于登录的IP和登录的时间戳。使用以下命令将LoggedTimeStamp字段设置为当前日期/时间: getdate()。

The view of logged in users selects rows of data from the Log In Trans table where the TokenExpires is greater than the current timestamp and the token field is not blank.

已登录用户的视图从Log In Trans表中选择数据行,其中TokenExpires大于当前时间戳,并且token字段不为空。

CREATE TABLE [dbo].[ee_tUsers](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [FirstName] [nvarchar](50) NULL,
    [LastName] [nvarchar](50) NULL,
    [Email] [nvarchar](150) NULL,
    [UserName] [nvarchar](50) NULL,
    [Password] [nvarchar](350) NULL,
    [UserLevel] [nvarchar](50) NULL
) ON [PRIMARY]

CREATE TABLE [dbo].[ee_tLoginTrans](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [UserID] [int] NULL,
    [UserName] [nvarchar](50) NULL,
    [Token] [nvarchar](350) NULL,
    [TokenExpires] [datetime] NULL,
    [LoggedIP] [nvarchar](50) NULL,
    [LoggedTimeStamp] [datetime] NULL,
) ON [PRIMARY]


CREATE VIEW [dbo].[ee_vLoggedInUsers]
AS
SELECT 
    dbo.ee_tLoginTrans.UserID, dbo.ee_tLoginTrans.UserName, dbo.ee_tLoginTrans.Token,         dbo.ee_tLoginTrans.TokenExpires, dbo.ee_tLoginTrans.LoggedIP, dbo.ee_tLoginTrans.LoggedTimeStamp, dbo.ee_tUsers.UserLevel

FROM 
    dbo.ee_tUsers RIGHT OUTER JOIN
    dbo.ee_tLoginTrans ON dbo.ee_tUsers.ID = dbo.ee_tLoginTrans.UserID

WHERE
    (dbo.ee_tLoginTrans.TokenExpires > GETDATE()) AND (dbo.ee_tLoginTrans.Token <> N'')
     

A side benefit of using a log in transaction table like this is you can run a report of who logged in with the time stamp and IP.  You can also view the current list of logged in users and if you like, remove their token and thus logging them out.

使用这样的登录事务表的一个附带好处是,您可以生成有关谁使用时间戳和IP登录的报告。 您还可以查看当前登录用户列表,如果愿意,可以删除其令牌并注销。

The three flow charts below shows the process of logging in, creating the token and testing authentication on each page.

下面的三个流程图显示了登录,创建令牌以及在每个页面上测试身份验证的过程。

Login.jpg



Login2.jpg



Login3.jpg
The file structure I am using in this demo uses an includes folder where I have a config.asp, functions.asp and nav.asp.

我在此演示中使用的文件结构使用一个include文件夹,其中包含config.asp,functions.asp和nav.asp。

The config.asp is used to store my database connection and other variables such as the amount of time to be logged in and a secret key used in the hashing functions.

config.asp用于存储数据库连接和其他变量,例如登录时间和哈希函数中使用的密钥。

The Functions.asp is used to store functions I will use on multiple pages such as sha256, setting passwords, setting the token, looking up the token.

Functions.asp用于存储我将在多个页面上使用的函数,例如sha256,设置密码,设置令牌,查找令牌。

The nav.asp are the navigation links.

nav.asp是导航链接。

The first step is to create the two tables and view. Make sure the LoggedTimeStamp field in the ee_tLoginTrans table will default to the current date/time.

第一步是创建两个表和视图。 确保ee_tLoginTrans表中的LoggedTimeStamp字段将默认为当前日期/时间。

You will have to make some adjustments in your code. For my own testing, I have placed the files in a sub folder of one of my domains and you may see code to include a file like . It is important you adjust the path to where you actually store the files. If your includes folder is at the top level of the site, then change to . Note that I could have simply used but purposely didn't. In order to use that method, you would need to have parent paths turned on in IIS and that is a security risk. I see a lot of videos and tutorials demonstrating how to turn parent paths off because it is easier to migrate your old code, but don't do this.

您将不得不在代码中进行一些调整。 为了进行我自己的测试,我将文件放在了我的一个域的子文件夹中,您可能会看到包含这样的文件的代码。 调整实际存储文件的路径非常重要。 如果您的include文件夹位于网站的顶层,则更改为。 请注意,我本可以简单使用,但故意不使用。 为了使用该方法,您需要在IIS中打开父路径,这存在安全风险。 我看到很多视频和教程演示了如何关闭父路径,因为这样可以更轻松地迁移旧代码,但不要这样做。

Next, add the pages that will go on the main level

接下来,添加将在主级别上显示的页面

INDEX.ASP

索引文件

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include virtual="/ee/login/includes/config.asp" -->
<!--#include virtual="/ee/login/includes/functions.asp" -->
<%
if request.form("username")<>"" then
dim cmd,rs
	Set cmd = Server.CreateObject ("ADODB.Command")
	cmd.ActiveConnection = conEE
	cmd.CommandText = "SELECT ID, UserName,UserLevel, Password FROM dbo.ee_tUsers WHERE Username = ?" 
	cmd.Parameters.Append cmd.CreateParameter("username", 202, 1, 350, request.form("username")) 'string
	cmd.Prepared = true
	Set rs = cmd.Execute
		
		if not rs.eof then
			if passwordHash(request.form("username"),request.form("password"))=rs("password") then 'the hash's match
		
			
				setToken rs("ID"),rs("UserName") ' function to set username
				redirectLoginLevel rs("UserLevel") ' function to redirect
				
			end if
		end if

	rs.Close()
	Set rs = Nothing
	
end if
%>
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="UTF-8">
<title>Log In</title>
</head>

<body>

<div id="login">
<h1>Log In</h1>
<form method="post" action="" autocomplete="off">
	<input name="username" placeholder="Username">
    <input name="password" type="password" placeholder="Password">
    <button type="submit">Submit</button>
</form>
</div>
</body>
</html> 

ADMIN.ASP

管理员程序

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include virtual="/ee/login/includes/config.asp" -->
<!--#include virtual="/ee/login/includes/functions.asp" -->
<%
' ---------- test for log in and level ------------
if logged_in_status = "0" OR logged_in_level<>"Admin" then
	response.Redirect("index.asp")
end if
%>
<%
dim cmd,arrUsers,UserID,FirstName,LastName,Email,UserName,UserLevel,rs,i
Set cmd = Server.CreateObject ("ADODB.Command")
cmd.ActiveConnection = conEE
cmd.CommandText = "SELECT ID, FirstName,LastName,Email, UserName,UserLevel FROM dbo.ee_tUsers" 
cmd.Prepared = true
Set rs = cmd.Execute

if not rs.eof then
	arrUsers = rs.getrows()
end if

rs.Close()
Set rs = Nothing

%>

<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="UTF-8">
<title>Admin</title>
</head>

<body>
<!--#include virtual="/ee/login/includes/nav.asp" -->
<h1>Admin</h1>
<h2>User List</h2>
<%
If IsArray(arrUsers) Then
	response.write "<table class='usertable'>"
	response.write "<tr><th>Edit</th><th>First</th><th>Last</th><th>Level</th></tr>"
    	For i = LBound(arrUsers, 2) To UBound(arrUsers, 2)
        	UserID 			= arrUsers(0, i) ' order based on sql statment,  ID, FirstName,LastName,Email, UserName,UserLevel 
			FirstName 	= arrUsers(1, i)
       	 	LastName 	= arrUsers(2, i)
			Email			= arrUsers(3, i)
			UserName	= arrUsers(4, i)
			UserLevel 	= arrUsers(5, i)
	
			response.write "<tr><td><a href='userEdit.asp?userid="&UserID&"'>Edit</a></td><td>"&FirstName&"</td><td>"&LastName&"</td><td>"&UserLevel&" </td></tr>"		
			
    	Next
		response.write "</table>"
	End If 
%>

</body>
</html> 

LOGGEDINUSERS.ASP

LOGGEDINUSERS.ASP

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include virtual="/ee/login/includes/config.asp" -->
<!--#include virtual="/ee/login/includes/functions.asp" -->
<%
' ---------- test for log in and level ------------
if logged_in_status = "0" OR logged_in_level<>"Admin" then
	response.Redirect("index.asp")
end if
%>
<%
dim cmd,arrUsers,UserID,UserName,IP,LogInTime,TokenExpires,UserLevel,rs,i,logoutform

if request.form("logoutuser")<>"" then
	if isnumeric(request.form("logoutuser")) then
		logOutToken(request.form("logoutuser"))
	end if
end if

Set cmd = Server.CreateObject ("ADODB.Command")
cmd.ActiveConnection = conEE
cmd.CommandText = "SELECT UserID, UserName, Token, TokenExpires, LoggedIP, LoggedTimeStamp, UserLevel FROM ee_vLoggedInUsers" 
cmd.Prepared = true
Set rs = cmd.Execute

if not rs.eof then
	arrUsers = rs.getrows()
end if

rs.Close()
Set rs = Nothing

%>

<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="UTF-8">
<title>Admin</title>
</head>

<body>
<!--#include virtual="/ee/login/includes/nav.asp" -->
<h1>Admin</h1>
<h2>User List</h2>
<%
If IsArray(arrUsers) Then
	response.write "<table class='usertable'>"
	response.write "<tr><th>Log Out</th><th>User</th><th>Expires</th><th>IP</th><th>TimeStamp</th><th>Level</th></tr>"
    	For i = LBound(arrUsers, 2) To UBound(arrUsers, 2)
        	UserID 			= arrUsers(0, i) ' order based on sql statment,
			UserName 		= arrUsers(1, i)
       	 	Token 				= arrUsers(2, i)
			TokenExpires	= arrUsers(3, i)
			IP						= arrUsers(4, i)
			LogInTime 		= arrUsers(5, i)
			UserLevel			= arrUsers(6, i)
	
			logoutform= "<form method='post' action=''><input type='hidden' name='logoutuser' value='"&UserID&"'><button type='submit'>Submit</button></form>"
	
			response.write "<tr><td>"&logoutform&"</td><td>"&UserName&"</td><td>"&TokenExpires&"</td><td>"&IP&" </td><td>"&LogInTime&"</td><td>"&UserLevel&" </td></tr>"		
			
    	Next
		response.write "</table>"
	End If 
%>

</body>
</html> 

SALES.ASP

销售网站

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include virtual="/ee/login/includes/config.asp" -->
<!--#include virtual="/ee/login/includes/functions.asp" -->
<%
' ---------- test for log in and level ------------
if logged_in_status = "0" OR instr("Admin Sales",logged_in_level)<1 then
	response.Redirect("index.asp")
end if
%>

<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Sales</title>
</head>

<body>
<!--#include virtual="/ee/login/includes/nav.asp" -->
<h1>Sales </h1>
</body>
</html> 

SUPPORT.ASP

支持

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include virtual="/ee/login/includes/config.asp" -->
<!--#include virtual="/ee/login/includes/functions.asp" -->
<%
' ---------- test for log in and level ------------
if logged_in_status = "0" OR  instr("Support Admin",logged_in_level)<1 then
	response.Redirect("index.asp")
end if
%>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Support</title>
</head>

<body>
<!--#include virtual="/ee/login/includes/nav.asp" -->
<h1>Support</h1>
<div>This section can be seen by Support and Admin</div>
<%
if logged_in_level = "Admin" then
%>
<div>This section can be seen ONLY by Admin</div>
<%
end if
%>
</body>
</html> 

USERADD.ASP

USERADD.ASP

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include virtual="/ee/login/includes/config.asp" -->
<!--#include virtual="/ee/login/includes/functions.asp" -->

<%
' ---------- test for log in and level ------------
if logged_in_status = "0" OR logged_in_level<>"Admin" then
	response.Redirect("index.asp")
end if
%>
<%
Dim rs,cmd, userid,cmdNewPassword




' ---------- CREATE CONTACT -------------
if request.form("action")="createContact" then
	dim update_response, validation, validation_username,validation_password, cmdFirstName, cmdLastName, cmdEmail, cmdUsername, cmdLevel


		validation=0 	' set to not validate
		validation_username=1  'set to validate (in case of blank)
		validation_password = 1  'set to validate (in case of blank)
		
		cmdFirstName = request.form("firstname")
		cmdLastName = request.form("lastname")
		cmdEmail = request.form("email")
		cmdUsername = request.form("username")
		cmdLevel = request.form("userlevel")
		
		'------- validation --------------
		if len(cmdFirstName)<3 then validation=validation+1 end if ' first name should be at least 3 characters
		if len(cmdLastName)<3 then validation=validation+1 end if ' last name should be at least 3 characters
		if len(cmdEmail)<7 then validation=validation+1 end if ' email should be at least 7
		if instr(cmdEmail,"@")<1 then validation=validation+1 end if ' email should contain @ or use RegEX
		if len(cmdLevel)<3 then validation=validation+1 end if ' level should be at least 3 characters
		
		' check if username is unique
		if cmdUsername<>"" then ' allow blank usernames
			Set cmd = Server.CreateObject ("ADODB.Command")
			cmd.ActiveConnection = conEE
			cmd.CommandText = "SELECT ID FROM dbo.ee_tUsers WHERE UserName = ?" 
			cmd.Parameters.Append cmd.CreateParameter("username", 202, 1, 50, cmdUsername) ' adDouble
			cmd.Prepared = true
			Set rs = cmd.Execute
			
			if not rs.eof then ' the username is in use, don't update
				validation_username=0
			end if
		
			rs.Close()
			Set rs = Nothing
		end if
		
		' validate password
		cmdNewPassword = ""
		if request.form("newpassword")<>"" then ' try and add new password
			if validatePassword(request.form("newpassword"))= True then
				cmdNewPassword=request.form("newpassword")
				cmdNewPassword= passwordHash(cmdUsername,cmdNewPassword)
				else
				validation_password=1
			end if
		end if
		' -------- end validation ----------
		if validation = 0 AND validation_username = 1 AND validation_password = 1   then ' if no errros proceed
		
			Set cmd = Server.CreateObject ("ADODB.Command")
			cmd.ActiveConnection = conEE
			cmd.CommandText = "INSERT INTO dbo.ee_tUsers ( FirstName, LastName,Email, UserLevel,Username, Password) VALUES (?,?,?,?,?,?)" 
			cmd.Prepared = true
			cmd.Parameters.Append cmd.CreateParameter("first", 202, 1, 50, cmdFirstName) ' adVarWChar
			cmd.Parameters.Append cmd.CreateParameter("last", 202, 1, 50, cmdLastName) ' adVarWChar
			cmd.Parameters.Append cmd.CreateParameter("email", 202, 1, 150, cmdEmail) ' adVarWChar
			cmd.Parameters.Append cmd.CreateParameter("level", 202, 1, 50, cmdLevel) ' adVarWChar
			cmd.Parameters.Append cmd.CreateParameter("username", 202, 1, 50, cmdUsername) ' adVarWChar
			cmd.Parameters.Append cmd.CreateParameter("password", 202, 1, 350, cmdNewPassword) ' adVarWChar
			cmd.Execute
			cmd.ActiveConnection.Close
			
			update_response="success|User Updated"
			
			' check if username should be updated
		
			
			
			
			else
			
			update_response="error|You have "&validation&" errors.  Please set all fields"
			if validation_password = 0 then
				update_response=update_response&"<br>Invalid Password"
			end if
			if validation_username = 0 then
				update_response=update_response&"<br>Username in use"
			end if
		end if

	' reset
	cmdFirstName="" 
	cmdLastName=""
	cmdEmail=""
	cmdUsername=""
	cmdLevel=""

end if

%>

<%
 'setPassword "password",1
%>
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="UTF-8">
<title>Contact Edit</title>
</head>

<body>
<!--#include virtual="/ee/login/includes/nav.asp" -->
<%

dim arrMessage,messageClass,messageContent
' if there is a form update, show mesage
	if update_response<>"" then ' this is delimted by a pipe class|message
		arrMessage = split(update_response,"|")
		messageClass = arrMessage(0)
		messageContent = arrMessage(1)
		response.write "<div class='"&messageClass&"'>"&messageContent&"</div>"
		'response.write messageContent
	end if
%>
<form method="post" action=""  autocomplete="off">
<input type="hidden" name="action" value="createContact">
<input type="hidden" name="contactid">
<label>First Name</label>
<input name="firstname" >
<label>Last Name</label>
<input name="lastname" >
<label>Email</label>
<input name="email">
<label>username</label>
<input name="username"  >
<label>User Level</label>
<select name="userlevel">
	<option value="">No Log In</option>
    <option value="Admin">Admin</option>
    <option value="Support">Support</option>
    <option value="Sales">Sales</option>
</select>

<p class="notice">Leave blank for no password</p>
<label>New Password</label>
<input name="newpassword">
<button type="submit">Submit</button>
</form>

</body>
</html> 

USEREDIT.ASP

USEREDIT.ASP

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include virtual="/ee/login/includes/config.asp" -->
<!--#include virtual="/ee/login/includes/functions.asp" -->

<%
' ---------- test for log in and level ------------
if logged_in_status = "0" OR logged_in_level<>"Admin" then
	response.Redirect("index.asp")
end if
%>
<%
Dim rs,cmd, userid,cmdPassword

' ---------- SET USER ID FROM QUERYSTRING -------------
userid=0
if request.QueryString("userid")<>"" then
	if isnumeric(request.QueryString("userid")) then
	 	userid = request.QueryString("userid") 
	end if
end if

' ---------- UPDATE CONTACT -------------
if request.form("action")="updateContact" then
	dim update_response, validation, validation_username, cmdFirstName, cmdLastName, cmdEmail, cmdUsername, cmdLevel


		validation=0
		validation_username=1
		
		cmdFirstName = request.form("firstname")
		cmdLastName = request.form("lastname")
		cmdEmail = request.form("email")
		cmdUsername = request.form("username")
		cmdLevel = request.form("userlevel")
		
		'------- validation --------------
		if len(cmdFirstName)<3 then validation=validation+1 end if ' first name should be at least 3 characters
		if len(cmdLastName)<3 then validation=validation+1 end if ' last name should be at least 3 characters
		if len(cmdEmail)<7 then validation=validation+1 end if ' email should be at least 7
		if instr(cmdEmail,"@")<1 then validation=validation+1 end if ' email should contain @ or use RegEX
		if len(cmdLevel)<3 then validation=validation+1 end if ' level should be at least 3 characters
		
		' check if username is unique
		if cmdUsername<>"" then ' allow blank usernames
			Set cmd = Server.CreateObject ("ADODB.Command")
			cmd.ActiveConnection = conEE
			cmd.CommandText = "SELECT ID FROM dbo.ee_tUsers WHERE UserName = ? AND ID<>?" 
			cmd.Parameters.Append cmd.CreateParameter("username", 202, 1, 50, cmdUsername) ' adDouble
			cmd.Parameters.Append cmd.CreateParameter("userid", 5, 1, -1, userid) ' adDouble
			cmd.Prepared = true
			Set rs = cmd.Execute
			
			if not rs.eof then ' the username is in use, don't update
				validation_username=0
			end if
		
			rs.Close()
			Set rs = Nothing
		end if
		' -------- end validation ----------
		if validation = 0 then ' if no errros proceed
		
			Set cmd = Server.CreateObject ("ADODB.Command")
			cmd.ActiveConnection = conEE
			cmd.CommandText = "UPDATE dbo.ee_tUsers SET FirstName = ?, LastName =?, Email =?, UserLevel=? WHERE ID = ?" 
			cmd.Prepared = true
			cmd.Parameters.Append cmd.CreateParameter("first", 202, 1, 50, cmdFirstName) ' adVarWChar
			cmd.Parameters.Append cmd.CreateParameter("last", 202, 1, 50, cmdLastName) ' adVarWChar
			cmd.Parameters.Append cmd.CreateParameter("email", 202, 1, 150, cmdEmail) ' adVarWChar
			cmd.Parameters.Append cmd.CreateParameter("level", 202, 1, 50, cmdLevel) ' adVarWChar
			cmd.Parameters.Append cmd.CreateParameter("userid", 5, 1, -1, userid) ' adDouble
			cmd.Execute
			cmd.ActiveConnection.Close
			
			update_response="success|User Updated"
			
			' check if username should be updated
			if validation_username=1 then
				Set cmd = Server.CreateObject ("ADODB.Command")
				cmd.ActiveConnection = conEE
				cmd.CommandText = "UPDATE dbo.ee_tUsers SET Username = ? WHERE ID = ?" 
				cmd.Prepared = true
				cmd.Parameters.Append cmd.CreateParameter("username", 202, 1, 50, cmdUsername) ' adVarWChar
				cmd.Parameters.Append cmd.CreateParameter("userid", 5, 1, -1, userid) ' adDouble
				cmd.Execute
				cmd.ActiveConnection.Close
				
				else
				
				update_response=update_response&"<br><div class='error'>Username in use, not updated</div>"
				
			end if
			
			' check if password should be updated
			if request.form("newpassword")<>"" then 'password was submitted
			
				cmdPassword = request.form("newpassword") 
				
				if validatePassword(cmdPassword)=True then ' password is valid
					if setPassword(cmdPassword,userid) = 1 then ' run function to update password and it is good
						update_response=update_response&"<br>Password Updated"
					end if
					
				else
					update_response=update_response&"<br><div class='error'>Password did not validate and was not updated</div>"
				end if
				
			end if
			
			else
			
			update_response="error|You have "&validation&" errors.  Please set all fields"
		end if

	' reset
	cmdFirstName="" 
	cmdLastName=""
	cmdEmail=""
	cmdUsername=""
	cmdLevel=""

end if

' ---------- CONTACT RECORDSET -------------
Set cmd = Server.CreateObject ("ADODB.Command")
cmd.ActiveConnection = conEE
cmd.CommandText = "SELECT ID, FirstName,LastName,Email, UserName,UserLevel FROM dbo.ee_tUsers WHERE ID = ?" 
cmd.Parameters.Append cmd.CreateParameter("userid", 5, 1, -1, userid) ' adDouble
cmd.Prepared = true
Set rs = cmd.Execute
%>

<%
 'setPassword "password",1
%>
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="UTF-8">
<title>Contact Edit</title>
</head>

<body>
<!--#include virtual="/ee/login/includes/nav.asp" -->
<%
if not rs.eof or not rs.bof then ' only show form if good recordset
dim arrMessage,messageClass,messageContent
' if there is a form update, show mesage
	if update_response<>"" then ' this is delimted by a pipe class|message
		arrMessage = split(update_response,"|")
		messageClass = arrMessage(0)
		messageContent = arrMessage(1)
		response.write "<div class='"&messageClass&"'>"&messageContent&"</div>"
		'response.write messageContent
	end if
%>
<form method="post" action=""  autocomplete="off">
<input type="hidden" name="action" value="updateContact">
<input type="hidden" name="contactid" value="<%=rs("ID")%>">
<label>First Name</label>
<input name="firstname" value="<%=rs("FirstName")%>">
<label>Last Name</label>
<input name="lastname" value="<%=rs("LastName")%>">
<label>Email</label>
<input name="email" value="<%=rs("Email")%>">
<label>username</label>
<input name="username"  value="<%=rs("UserName")%>">
<label>User Level</label>
<select name="userlevel">
	<option value="">No Log In</option>
    <option value="Admin"<%if rs("UserLevel")="Admin" then response.write " selected" end if%>>Admin</option>
    <option value="Support"<%if rs("UserLevel")="Support" then response.write " selected" end if%>>Support</option>
    <option value="Sales"<%if rs("UserLevel")="Sales" then response.write " selected" end if%>>Sales</option>
</select>
<p class="notice">
NOTE:<br>The current password can not be displayed.<br>
If the user forgot their password, you may set a new password by entering the password field below.
</p>
<p class="notice">Leave blank to keep current password.</p>
<label>New Password</label>
<input name="newpassword">
<button type="submit">Submit</button>
</form>
<%
else
	response.write "ERROR: Bad User ID"
end if
%>
</body>
</html>
<%
rs.Close()
Set rs = Nothing
%> 

STYLE.CSS

样式

.nav{
	padding:10px;
	font-size:115%;
	border-bottom:solid;
}
#login{
	width:500px;
	margin-left:auto;
	margin-right:auto;
}
#login form{
	font-size:110%;
}
#login input{
	
	color:grey;

}
.notice {
	font-size:85%;
	color:red;
}
.error {
	
	background-color:pink;
}
.success {
	background-color:LightGreen;
}
label{
	width:100px;
	 display:block
}
input,select{
	display:block;
	margin-bottom:10px;
}
table.usertable {
	width:600px;
}

table.usertable tr:nth-child(even) {
    background-color: #eee;
}
table.usertable tr:nth-child(odd) {
   background-color:#fff;
}
table.usertable th	{
    background-color: black;
    color: white;
} 

INCLUDES/CONFIG.ASP

包含/配置ASP

<%
option explicit 
' config and function variables
Dim conEE,secret_key,login_session_minutes,main_login_page
conEE = "Provider=SQLOLEDB; datasource=localhost; Database=db_name; Uid=username; Pwd=password"

' ---- config set up ---- 
secret_key 					= "aBc123Xyz456LMNop" ' use a long random string '
login_session_minutes	= 60 ' total time per session in minutes
main_login_page 		= "/ee/login/"  'where to redirect for the main log in page

%> 

INCLUDES/FUNCTIONS.ASP

包含/功能

<%
DIM strAuth,logged_in_status,logged_in_user,logged_in_level,foundToken,arrAuth
DIM passHash,zPass,zUserID,zUserName,cmdFunction,rsFunction,fnPassword,token_expires,token,logged_in_expires

' to be placed at the top on every page you want secured'
logged_in_status = "0"
strAuth = lookupToken ' find token using function'
if strAuth <> "0" then ' has data
	arrAuth=split(strAuth,"|") ' create an array from our pipe delimited return'
	logged_in_status 	= arrAuth(0)
	logged_in_user 		= arrAuth(1)
	logged_in_level 		= arrAuth(2) ' Level_1, level_2, Level_3, Admin, Author, User'
	logged_in_expires   = arrAuth(3)
end if


' ---- Set a token to cookie and db ----'
function setToken(zUserID,zUserName)
	' logout any remaining tokens
	 logOutToken(zUserID)

	'set token in cookie and log in table for 1 hour
	token_expires = dateAdd("n",login_session_minutes,now)
	token = sha256(now&zUserName&secret_key)
	Response.Cookies("domaintoken")=token
	Response.Cookies("domaintoken").Expires=cdate(token_expires)

	Set cmdFunction = Server.CreateObject ("ADODB.Command")
	cmdFunction.ActiveConnection = conEE
	cmdFunction.CommandText = "INSERT INTO  ee_tLoginTrans (UserID,UserName,Token,TokenExpires,LoggedIP,LoggedTimeStamp) VALUES(?,?,?,?,?,?)"
	cmdFunction.Parameters.Append cmdFunction.CreateParameter("username", 5, 1, -1, zUserID) 'int user id
	cmdFunction.Parameters.Append cmdFunction.CreateParameter("username", 202, 1, 50, zUserName) 'string username
	cmdFunction.Parameters.Append cmdFunction.CreateParameter("username", 202, 1, 350,token ) 'string token
	cmdFunction.Parameters.Append cmdFunction.CreateParameter("username", 135, 1, -1, token_expires) 'string
	cmdFunction.Parameters.Append cmdFunction.CreateParameter("username", 202, 1, 50,  Request.ServerVariables("REMOTE_ADDR")) 'string
	cmdFunction.Parameters.Append cmdFunction.CreateParameter("username", 135, 1, -1, now) 'string
	cmdFunction.Prepared = true
	cmdFunction.Execute
	cmdFunction.ActiveConnection.Close

end function

' ------- LOG OUT - Remove token string from login trans ---------
function logOutToken(zUserID)
		Set cmdFunction = Server.CreateObject ("ADODB.Command")
		cmdFunction.ActiveConnection = conEE
		cmdFunction.CommandText = "UPDATE dbo.ee_tLoginTrans  SET Token='' WHERE UserID = ? AND Token<>''" 
		cmdFunction.Prepared = true
		cmdFunction.Parameters.Append cmdFunction.CreateParameter("userid", 5, 1, -1, zUserID) ' adDouble
		cmdFunction.Execute
		cmdFunction.ActiveConnection.Close

end function

' ---- look up cookie----'
function lookupToken()
dim rsFoundToken
	lookupToken="0" ' return bad '
	foundToken = Request.Cookies("domaintoken")
	
	if foundToken<>"" then ' if token cookie found then look up db'
		Set cmdFunction = Server.CreateObject ("ADODB.Command")
		cmdFunction.ActiveConnection = conEE
		cmdFunction.CommandText = "SELECT UserID, UserName, Token, TokenExpires, LoggedIP, LoggedTimeStamp, UserLevel FROM            ee_vLoggedInUsers WHERE Token = ?" 
		cmdFunction.Parameters.Append cmdFunction.CreateParameter("domaintoken", 202, 1, 350, request.Cookies("domaintoken")) '
		cmdFunction.Prepared = true
		Set rsFoundToken= cmdFunction.Execute
		
		If not rsFoundToken.bof or not rsFoundToken.eof then
			' pipe delimited return  username|admin'
			lookupToken="1|"&rsFoundToken("UserName")&"|"&rsFoundToken("UserLevel")&"|"&rsFoundToken("TokenExpires")
		end if
		
		rsFoundToken.Close()
		Set rsFoundToken = Nothing
	end if
end function


' ----Check Authentication ----'
' if you do not use levels, you can simply run this function at the top of every page.
function authenticate()
	if cstr(logged_in_status)="0" then
		response.redirect main_login_page ' not logged in, send away'
	end if
end function

' ---- Redirect Based On User Level ----'
function redirectLoginLevel(logged_in_level)

	select case logged_in_level
		case "Admin"
			response.Redirect("admin.asp")
		case "Support"
			response.Redirect("support.asp")
		case "Sales"
			response.Redirect("sales.asp")
	end select
	
end function


' ---- Update User Password----'
function setPassword(zPass,zUserID)

	' get the username
	zUserName=""
	
	Set cmdFunction = Server.CreateObject ("ADODB.Command")
	cmdFunction.ActiveConnection = conEE
	cmdFunction.CommandText = "SELECT * FROM dbo.ee_tUsers WHERE ID = ?" 
	cmdFunction.Parameters.Append cmdFunction.CreateParameter("userid", 4, 1, -1, zUserID) '
	cmdFunction.Prepared = true
	Set rsFunction = cmdFunction.Execute
	
	if not rsFunction.eof then
		zUserName=rsFunction("UserName")
	end if

	
	rsFunction.Close()
	Set rsFunction = Nothing

	' create the password hash
	if zUserName<> "" then
		passHash = passwordHash(zUserName,zPass)
		
		Set cmdFunction = Server.CreateObject ("ADODB.Command")
		cmdFunction.ActiveConnection = conEE
		cmdFunction.CommandText = "UPDATE dbo.ee_tUsers SET Password = ? WHERE ID = ?" 
		cmdFunction.Prepared = true
		cmdFunction.Parameters.Append cmdFunction.CreateParameter("param1", 202, 1, 350, passHash) ' adVarWChar
		cmdFunction.Parameters.Append cmdFunction.CreateParameter("param2", 5, 1, -1, zUserID) ' adDouble
		cmdFunction.Execute
		cmdFunction.ActiveConnection.Close
		
		setPassword = 1
		
		else
		
		setPassword=0
		
	end if
	
end function


function validatePassword(fnPassword)
	'http://www.mikesdotnetting.com/article/24/regular-expressions-and-vbscript
	' http://regexlib.com/Search.aspx?k=password&c=4&m=5&ps=20
	' This regex can be used to restrict passwords to a length of 8 to 20 aplhanumeric characters 
	'and select special characters. The password also can not start with a digit, underscore or special character
	' and must contain at least one digit.
	'Matches	password1 | pa$$word2 | pa!@#$%3
	'Non-Matches	password | 1stpassword | $password#
	'Author Michael Ash
				
	Dim re, targetString
	Set re = New RegExp
	With re
  		.Pattern = "^(?=[^\d_].*?\d)\w(\w|[!@#$%]){7,20}"
  		.Global = False
  		.IgnoreCase = False
	End With
	targetString = fnPassword

	validatePassword = re.Test(targetString)
	
							
end function



' ---- Hash a password ----'
function passwordHash(zUserName,zPass)
	passwordHash=sha256(zUserName&zPass&secret_key)
end function


' ---- SHA 256 FUNCTION ----
' See the VB6 project that accompanies this sample for full code comments on how
' it works.
'
' ASP VBScript code for generating a SHA256 'digest' or 'signature' of a string. The
' MD5 algorithm is one of the industry standard methods for generating digital
' signatures. It is generically known as a digest, digital signature, one-way
' encryption, hash or checksum algorithm. A common use for SHA256 is for password
' encryption as it is one-way in nature, that does not mean that your passwords
' are not free from a dictionary attack.
'
' If you are using the routine for passwords, you can make it a little more secure
' by concatenating some known random characters to the password before you generate
' the signature and on subsequent tests, so even if a hacker knows you are using
' SHA-256 for your passwords, the random characters will make it harder to dictionary
' attack.
'
' NOTE: Due to the way in which the string is processed the routine assumes a
' single byte character set. VB passes unicode (2-byte) character strings, the
' ConvertToWordArray function uses on the first byte for each character. This
' has been done this way for ease of use, to make the routine truely portable
' you could accept a byte array instead, it would then be up to the calling
' routine to make sure that the byte array is generated from their string in
' a manner consistent with the string type.
'
' This is 'free' software with the following restrictions:
'
' You may not redistribute this code as a 'sample' or 'demo'. However, you are free
' to use the source code in your own code, but you may not claim that you created
' the sample code. It is expressly forbidden to sell or profit from this source code
' other than by the knowledge gained or the enhanced value added by your own code.
'
' Use of this software is also done so at your own risk. The code is supplied as
' is without warranty or guarantee of any kind.
'
' Should you wish to commission some derivative work based on this code provided
' here, or any consultancy work, please do not hesitate to contact us.
'
' Web Site:  http://www.frez.co.uk
' E-mail:    sales@frez.co.uk

Private m_lOnBits(30)
Private m_l2Power(30)
Private K(63)

Private Const BITS_TO_A_BYTE = 8
Private Const BYTES_TO_A_WORD = 4
Private Const BITS_TO_A_WORD = 32

m_lOnBits(0) = CLng(1)
m_lOnBits(1) = CLng(3)
m_lOnBits(2) = CLng(7)
m_lOnBits(3) = CLng(15)
m_lOnBits(4) = CLng(31)
m_lOnBits(5) = CLng(63)
m_lOnBits(6) = CLng(127)
m_lOnBits(7) = CLng(255)
m_lOnBits(8) = CLng(511)
m_lOnBits(9) = CLng(1023)
m_lOnBits(10) = CLng(2047)
m_lOnBits(11) = CLng(4095)
m_lOnBits(12) = CLng(8191)
m_lOnBits(13) = CLng(16383)
m_lOnBits(14) = CLng(32767)
m_lOnBits(15) = CLng(65535)
m_lOnBits(16) = CLng(131071)
m_lOnBits(17) = CLng(262143)
m_lOnBits(18) = CLng(524287)
m_lOnBits(19) = CLng(1048575)
m_lOnBits(20) = CLng(2097151)
m_lOnBits(21) = CLng(4194303)
m_lOnBits(22) = CLng(8388607)
m_lOnBits(23) = CLng(16777215)
m_lOnBits(24) = CLng(33554431)
m_lOnBits(25) = CLng(67108863)
m_lOnBits(26) = CLng(134217727)
m_lOnBits(27) = CLng(268435455)
m_lOnBits(28) = CLng(536870911)
m_lOnBits(29) = CLng(1073741823)
m_lOnBits(30) = CLng(2147483647)

m_l2Power(0) = CLng(1)
m_l2Power(1) = CLng(2)
m_l2Power(2) = CLng(4)
m_l2Power(3) = CLng(8)
m_l2Power(4) = CLng(16)
m_l2Power(5) = CLng(32)
m_l2Power(6) = CLng(64)
m_l2Power(7) = CLng(128)
m_l2Power(8) = CLng(256)
m_l2Power(9) = CLng(512)
m_l2Power(10) = CLng(1024)
m_l2Power(11) = CLng(2048)
m_l2Power(12) = CLng(4096)
m_l2Power(13) = CLng(8192)
m_l2Power(14) = CLng(16384)
m_l2Power(15) = CLng(32768)
m_l2Power(16) = CLng(65536)
m_l2Power(17) = CLng(131072)
m_l2Power(18) = CLng(262144)
m_l2Power(19) = CLng(524288)
m_l2Power(20) = CLng(1048576)
m_l2Power(21) = CLng(2097152)
m_l2Power(22) = CLng(4194304)
m_l2Power(23) = CLng(8388608)
m_l2Power(24) = CLng(16777216)
m_l2Power(25) = CLng(33554432)
m_l2Power(26) = CLng(67108864)
m_l2Power(27) = CLng(134217728)
m_l2Power(28) = CLng(268435456)
m_l2Power(29) = CLng(536870912)
m_l2Power(30) = CLng(1073741824)

K(0) = &H428A2F98
K(1) = &H71374491
K(2) = &HB5C0FBCF
K(3) = &HE9B5DBA5
K(4) = &H3956C25B
K(5) = &H59F111F1
K(6) = &H923F82A4
K(7) = &HAB1C5ED5
K(8) = &HD807AA98
K(9) = &H12835B01
K(10) = &H243185BE
K(11) = &H550C7DC3
K(12) = &H72BE5D74
K(13) = &H80DEB1FE
K(14) = &H9BDC06A7
K(15) = &HC19BF174
K(16) = &HE49B69C1
K(17) = &HEFBE4786
K(18) = &HFC19DC6
K(19) = &H240CA1CC
K(20) = &H2DE92C6F
K(21) = &H4A7484AA
K(22) = &H5CB0A9DC
K(23) = &H76F988DA
K(24) = &H983E5152
K(25) = &HA831C66D
K(26) = &HB00327C8
K(27) = &HBF597FC7
K(28) = &HC6E00BF3
K(29) = &HD5A79147
K(30) = &H6CA6351
K(31) = &H14292967
K(32) = &H27B70A85
K(33) = &H2E1B2138
K(34) = &H4D2C6DFC
K(35) = &H53380D13
K(36) = &H650A7354
K(37) = &H766A0ABB
K(38) = &H81C2C92E
K(39) = &H92722C85
K(40) = &HA2BFE8A1
K(41) = &HA81A664B
K(42) = &HC24B8B70
K(43) = &HC76C51A3
K(44) = &HD192E819
K(45) = &HD6990624
K(46) = &HF40E3585
K(47) = &H106AA070
K(48) = &H19A4C116
K(49) = &H1E376C08
K(50) = &H2748774C
K(51) = &H34B0BCB5
K(52) = &H391C0CB3
K(53) = &H4ED8AA4A
K(54) = &H5B9CCA4F
K(55) = &H682E6FF3
K(56) = &H748F82EE
K(57) = &H78A5636F
K(58) = &H84C87814
K(59) = &H8CC70208
K(60) = &H90BEFFFA
K(61) = &HA4506CEB
K(62) = &HBEF9A3F7
K(63) = &HC67178F2

Private Function LShift(lValue, iShiftBits)
    If iShiftBits = 0 Then
        LShift = lValue
        Exit Function
    ElseIf iShiftBits = 31 Then
        If lValue And 1 Then
            LShift = &H80000000
        Else
            LShift = 0
        End If
        Exit Function
    ElseIf iShiftBits < 0 Or iShiftBits > 31 Then
        Err.Raise 6
    End If

    If (lValue And m_l2Power(31 - iShiftBits)) Then
        LShift = ((lValue And m_lOnBits(31 - (iShiftBits + 1))) * m_l2Power(iShiftBits)) Or &H80000000
    Else
        LShift = ((lValue And m_lOnBits(31 - iShiftBits)) * m_l2Power(iShiftBits))
    End If
End Function

Private Function RShift(lValue, iShiftBits)
    If iShiftBits = 0 Then
        RShift = lValue
        Exit Function
    ElseIf iShiftBits = 31 Then
        If lValue And &H80000000 Then
            RShift = 1
        Else
            RShift = 0
        End If
        Exit Function
    ElseIf iShiftBits < 0 Or iShiftBits > 31 Then
        Err.Raise 6
    End If

    RShift = (lValue And &H7FFFFFFE) \ m_l2Power(iShiftBits)

    If (lValue And &H80000000) Then
        RShift = (RShift Or (&H40000000 \ m_l2Power(iShiftBits - 1)))
    End If
End Function

Private Function AddUnsigned(lX, lY)
    Dim lX4
    Dim lY4
    Dim lX8
    Dim lY8
    Dim lResult

    lX8 = lX And &H80000000
    lY8 = lY And &H80000000
    lX4 = lX And &H40000000
    lY4 = lY And &H40000000

    lResult = (lX And &H3FFFFFFF) + (lY And &H3FFFFFFF)

    If lX4 And lY4 Then
        lResult = lResult Xor &H80000000 Xor lX8 Xor lY8
    ElseIf lX4 Or lY4 Then
        If lResult And &H40000000 Then
            lResult = lResult Xor &HC0000000 Xor lX8 Xor lY8
        Else
            lResult = lResult Xor &H40000000 Xor lX8 Xor lY8
        End If
    Else
        lResult = lResult Xor lX8 Xor lY8
    End If

    AddUnsigned = lResult
End Function

Private Function Ch(x, y, z)
    Ch = ((x And y) Xor ((Not x) And z))
End Function

Private Function Maj(x, y, z)
    Maj = ((x And y) Xor (x And z) Xor (y And z))
End Function

Private Function S(x, n)
    S = (RShift(x, (n And m_lOnBits(4))) Or LShift(x, (32 - (n And m_lOnBits(4)))))
End Function

Private Function R(x, n)
    R = RShift(x, CInt(n And m_lOnBits(4)))
End Function

Private Function Sigma0(x)
    Sigma0 = (S(x, 2) Xor S(x, 13) Xor S(x, 22))
End Function

Private Function Sigma1(x)
    Sigma1 = (S(x, 6) Xor S(x, 11) Xor S(x, 25))
End Function

Private Function Gamma0(x)
    Gamma0 = (S(x, 7) Xor S(x, 18) Xor R(x, 3))
End Function

Private Function Gamma1(x)
    Gamma1 = (S(x, 17) Xor S(x, 19) Xor R(x, 10))
End Function

Private Function ConvertToWordArray(sMessage)
    Dim lMessageLength
    Dim lNumberOfWords
    Dim lWordArray()
    Dim lBytePosition
    Dim lByteCount
    Dim lWordCount
    Dim lByte

    Const MODULUS_BITS = 512
    Const CONGRUENT_BITS = 448

    lMessageLength = Len(sMessage)

    lNumberOfWords = (((lMessageLength + ((MODULUS_BITS - CONGRUENT_BITS) \ BITS_TO_A_BYTE)) \ (MODULUS_BITS \ BITS_TO_A_BYTE)) + 1) * (MODULUS_BITS \ BITS_TO_A_WORD)
    ReDim lWordArray(lNumberOfWords - 1)

    lBytePosition = 0
    lByteCount = 0
    Do Until lByteCount >= lMessageLength
        lWordCount = lByteCount \ BYTES_TO_A_WORD

        lBytePosition = (3 - (lByteCount Mod BYTES_TO_A_WORD)) * BITS_TO_A_BYTE

        lByte = AscB(Mid(sMessage, lByteCount + 1, 1))

        lWordArray(lWordCount) = lWordArray(lWordCount) Or LShift(lByte, lBytePosition)
        lByteCount = lByteCount + 1
    Loop

    lWordCount = lByteCount \ BYTES_TO_A_WORD
    lBytePosition = (3 - (lByteCount Mod BYTES_TO_A_WORD)) * BITS_TO_A_BYTE

    lWordArray(lWordCount) = lWordArray(lWordCount) Or LShift(&H80, lBytePosition)

    lWordArray(lNumberOfWords - 1) = LShift(lMessageLength, 3)
    lWordArray(lNumberOfWords - 2) = RShift(lMessageLength, 29)

    ConvertToWordArray = lWordArray
End Function

Public Function SHA256(sMessage)
    Dim HASH(7)
    Dim M
    Dim W(63)
    Dim a
    Dim b
    Dim c
    Dim d
    Dim e
    Dim f
    Dim g
    Dim h
    Dim i
    Dim j
    Dim T1
    Dim T2

    HASH(0) = &H6A09E667
    HASH(1) = &HBB67AE85
    HASH(2) = &H3C6EF372
    HASH(3) = &HA54FF53A
    HASH(4) = &H510E527F
    HASH(5) = &H9B05688C
    HASH(6) = &H1F83D9AB
    HASH(7) = &H5BE0CD19

    M = ConvertToWordArray(sMessage)

    For i = 0 To UBound(M) Step 16
        a = HASH(0)
        b = HASH(1)
        c = HASH(2)
        d = HASH(3)
        e = HASH(4)
        f = HASH(5)
        g = HASH(6)
        h = HASH(7)

        For j = 0 To 63
            If j < 16 Then
                W(j) = M(j + i)
            Else
                W(j) = AddUnsigned(AddUnsigned(AddUnsigned(Gamma1(W(j - 2)), W(j - 7)), Gamma0(W(j - 15))), W(j - 16))
            End If

            T1 = AddUnsigned(AddUnsigned(AddUnsigned(AddUnsigned(h, Sigma1(e)), Ch(e, f, g)), K(j)), W(j))
            T2 = AddUnsigned(Sigma0(a), Maj(a, b, c))

            h = g
            g = f
            f = e
            e = AddUnsigned(d, T1)
            d = c
            c = b
            b = a
            a = AddUnsigned(T1, T2)
        Next

        HASH(0) = AddUnsigned(a, HASH(0))
        HASH(1) = AddUnsigned(b, HASH(1))
        HASH(2) = AddUnsigned(c, HASH(2))
        HASH(3) = AddUnsigned(d, HASH(3))
        HASH(4) = AddUnsigned(e, HASH(4))
        HASH(5) = AddUnsigned(f, HASH(5))
        HASH(6) = AddUnsigned(g, HASH(6))
        HASH(7) = AddUnsigned(h, HASH(7))
    Next

    SHA256 = LCase(Right("00000000" & Hex(HASH(0)), 8) & Right("00000000" & Hex(HASH(1)), 8) & Right("00000000" & Hex(HASH(2)), 8) & Right("00000000" & Hex(HASH(3)), 8) & Right("00000000" & Hex(HASH(4)), 8) & Right("00000000" & Hex(HASH(5)), 8) & Right("00000000" & Hex(HASH(6)), 8) & Right("00000000" & Hex(HASH(7)), 8))
End Function
%> 

INCLUDES/NAV.ASP

包含/NAV.ASP

<div><%
 if isArray(arrAuth) then ' 'if logged in
 	response.write "Welcome "&logged_in_user&" your session expires "&logged_in_expires
 end if 
 %>
</div>
<div class="nav">
	<a href="index.asp">Log In</a> |
    <a href="userAdd.asp">New User</a> |
	<a href="admin.asp">Admin </a> |
	<a href="loggedinusers.asp">Logged In Users |</a>
    <a href="sales.asp">Sales |</a>
    <a href="support.asp">Support |</a>
</div> 

To add yourself as the first user, you will need to do this without having to log in. At the top of the UserAdd.asp, comment out the line 8, response.Redirect("index.asp"). We need this removed to allow you on the page. For the user level, set yourself as "Admin". The password must start with a character and contain at least one digit. For regular Expression help, you can do an advanced search on Experts Exchange http://www.experts-exchange.com/advancedSearch.jsp, click Question and select the topic Regular Expression. You can also find help at http://www.mikesdotnetting.com/article/24/regular-expressions-and-vbscript or http://regexlib.com/Search.aspx?k=password&c=4&m=5&ps=20.

要将您自己添加为第一个用户,您将无需登录就可以执行此操作。在UserAdd.asp的顶部,注释掉第8行,response.Redirect(“ index.asp”)。 我们需要将其删除以允许您进入页面。 对于用户级别,将自己设置为“管理员”。 密码必须以字符开头,并且至少包含一位数字。 要获取正则表达式帮助,可以在Experts Exchange http://www.experts-exchange.com/advancedSearch.jsp上进行高级搜索,单击“问题”,然后选择正则表达式主题。 您还可以在http://www.mikesdotnetting.com/article/24/regular-expressions-and-vbscripthttp://regexlib.com/Search.aspx?k=password&c=4&m=5&ps=20上找到帮助。

Once you have created yourself as a user, add back the code on line 8,response.Redirect("index.asp"). Now you can go to your index page and log in. For this demo site, the Admin page lists all the users you have created. The LoggedInUsers.asp page shows who is logged in by using the view you created. Try creating a second user, then log in as that user on another browser. With your admin credentials, if you refresh the LoggedInUsers.asp page you can see two users. If you click the Submit button under the logout column for your second user, that user will be logged out and on the next page refresh will be sent back to main log in page. This is because we removed the token from the login transaction table. If you look at your database, you will see the row still exists, but the token field is missing.

创建用户身份后,请在第8行上添加代码,response.Redirect(“ index.asp”)。 现在,您可以转到索引页面并登录。对于此演示站点,“管理”页面列出了您创建的所有用户。 LoggedInUsers.asp页显示使用创建的视图登录的人。 尝试创建第二个用户,然后以该用户身份登录另一个浏览器。 使用管理员凭据,如果刷新LoggedInUsers.asp页,则可以看到两个用户。 如果您单击第二个用户的注销列下的“提交”按钮,则该用户将被注销,刷新将在下一页发送回主登录页面。 这是因为我们从登录事务表中删除了令牌。 如果查看数据库,将看到该行仍然存在,但是令牌字段丢失。

Because there are several user levels, I created a function that will forward users to the different page once logged in based on their user credentials.

由于存在多个用户级别,因此我创建了一个函数,该函数将根据用户的凭据将用户登录后转到其他页面。

Now for the details of how this works. Starting with the first flow chart, we have a form with a username and password. Once submitted, the username is used to look up the users table with the following SQL where the ? = the form data for the username.

现在详细了解其工作原理。 从第一个流程图开始,我们有一个带有用户名和密码的表格。 提交后,用户名将用于通过以下SQL查找users表: =用户名的表单数据。

SELECT ID, UserName,UserLevel, Password FROM dbo.ee_tUsers WHERE Username = ?  

Assuming we find the username, the next step is to match the password entered with the password stored. I have hashed the password so there is no direct way to lookup the password directly from the form data. If you look at your users table, you will see the password looks something like this: cb0ef439f1865799621cfe8beff06261642ae1902145e9e35d8adfa7c0fb99da. It is an sha256 hash using the username password and secret key as the salt. This way two people with the same password will have a different hash because the usernames are unique. There are more secure hashing algorithm's and no matter which you choose; somebody will have an opinion. Since this is classic ASP, there is a readily available source to create this hash and that is why I have chosen it. There is a good article on this subject at crackstation https://crackstation.net/hashing-security.htm. As an alternative to hashing with VBscript, you can also do this on SQL Server with HASHBYTES https://msdn.microsoft.com/en-us/library/ms174415.aspx. If you go this route, you will want to create a stored procedure for these functions. This article will be talking about maintining the hashing functions in VBscript.

假设我们找到了用户名,下一步就是将输入的密码与存储的密码进行匹配。 我已经对密码进行了哈希处理,因此没有直接方法可以直接从表单数据中查找密码。 如果查看用户表,您将看到密码如下所示: cb0ef439f1865799621cfe8beff06261642ae1902145e9e35d8adfa7c0fb99da 。 它是一个sha256哈希,使用用户名密码和密钥作为盐。 这样,具有相同密码的两个人将具有不同的哈希,因为用户名是唯一的。 无论选择哪种方式,都有更安全的哈希算法。 会有人发表意见。 由于这是经典的ASP,因此有一个随时可用的源来创建此哈希,这就是我选择它的原因。 在crackstation https://crackstation.net/hashing-security.htm上有一篇很好的文章。 作为使用VBscript进行哈希处理的替代方法,您还可以在SQL Server上使用HASHBYTES https://msdn.microsoft.com/zh-cn/library/ms174415.aspx进行此操作 。 如果走这条路线,您将要为这些功能创建一个存储过程。 本文将讨论维护VBscript中的哈希函数。

DECLARE @HashThis nvarchar(4000);
SET @HashThis = CONVERT(nvarchar(4000),'dslfdkjLK85kldhnv$n000#knf');
SELECT HASHBYTES('SHA1', @HashThis); 

Because the stored password is hashed in our database, the way to check if the password is correct is to run the submitted password through our hashing function then match the result with what is stored in the database.

因为存储的密码在我们的数据库中是散列的,所以检查密码是否正确的方法是通过我们的散列函数运行提交的密码,然后将结果与数据库中存储的内容进行匹配。

The function that does this is below. The sha256 function is the large function at the bottom of the functions.asp page. It is simply creating the hash as sha256("sometext"). For the text, we are concatenating the username, password and secret key. This is what is referred to as salting.

执行此操作的功能如下。 sha256函数是functions.asp页底部的大型函数。 它只是将哈希创建为sha256(“ sometext”)。 对于文本,我们将用户名,密码和密钥串联在一起。 这就是所谓的盐化。

' ---- Hash a password ----'
function passwordHash(zUserName,zPass)
    passwordHash=sha256(zUserName&zPass&secret_key)
end function 

On the index.asp code if there is a submitted form with the field username, we our look up of the user table. Then run the submitted password through the passwordHash function and match that result with the password found in the database.

在index.asp代码上,如果存在带有用户名字段的提交表单,我们将查询user表。 然后通过passwordHash函数运行提交的密码,并使结果与数据库中找到的密码匹配。

If the password is a match, we create a token which is another salted hash. This hash is set as a cookie and stored in the LoginTrans table using the field named Token.

如果密码匹配,我们将创建一个令牌,该令牌是另一个带盐的哈希。 将此哈希设置为cookie,并使用名为Token的字段存储在LoginTrans表中。

On every subsequent page load, we look up the cookie to get the token, then search for the token in the LoginTrans table. If found, test that the expiration date is good and proceed.

在随后的每次页面加载中,我们都会查询cookie以获取令牌,然后在LoginTrans表中搜索令牌。 如果找到,请测试有效期限并继续。

At the top of ever page we get the status, logged in user, their level and expires from the log in trans. The lookupToken function searches the database for the token found in the cookie then returns a pipe delimited response.

在每页的顶部,我们会获得状态,已登录的用户,其级别,并从trans的登录到期。 lookupToken函数在数据库中搜索在cookie中找到的令牌,然后返回管道分隔的响应。

logged_in_status = "0"
strAuth = lookupToken ' find token using function'
if strAuth <> "0" then ' has data
    arrAuth=split(strAuth,"|") ' create an array from our pipe delimited return'
    logged_in_status     = arrAuth(0)
    logged_in_user         = arrAuth(1)
    logged_in_level     = arrAuth(2) ' Level_1, level_2, Level_3, Admin, Author, User'
    logged_in_expires   = arrAuth(3)
end if

' ---- look up cookie----'
function lookupToken()
dim rsFoundToken
    lookupToken="0" ' return bad '
    foundToken = Request.Cookies("domaintoken")
    
    if foundToken<>"" then ' if token cookie found then look up db'
        Set cmdFunction = Server.CreateObject ("ADODB.Command")
        cmdFunction.ActiveConnection = conEE
        cmdFunction.CommandText = "SELECT UserID, UserName, Token, TokenExpires, LoggedIP, LoggedTimeStamp, UserLevel FROM            ee_vLoggedInUsers WHERE Token = ?" 
        cmdFunction.Parameters.Append cmdFunction.CreateParameter("domaintoken", 202, 1, 350, request.Cookies("domaintoken")) '
        cmdFunction.Prepared = true
        Set rsFoundToken= cmdFunction.Execute
        
        If not rsFoundToken.bof or not rsFoundToken.eof then
            ' pipe delimited return  username|admin'
            lookupToken="1|"&rsFoundToken("UserName")&"|"&rsFoundToken("UserLevel")&"|"&rsFoundToken("TokenExpires")
        end if
        
        rsFoundToken.Close()
        Set rsFoundToken = Nothing
    end if
end function 

It is important to have unique usernames and there is a function that helps when a username is selected. In both the Edit and Add user pages, there is a look up that makes sure the username is unique.  

拥有唯一的用户名很重要,并且有一个功能可以在选择用户名时提供帮助。 在“编辑”和“添加用户”页面中,都有一个查询来确保用户名是唯一的。

    ' check if username is unique
        if cmdUsername<>"" then ' allow blank usernames
            Set cmd = Server.CreateObject ("ADODB.Command")
            cmd.ActiveConnection = conEE
            cmd.CommandText = "SELECT ID FROM dbo.ee_tUsers WHERE UserName = ?" 
            cmd.Parameters.Append cmd.CreateParameter("username", 202, 1, 50, cmdUsername)
            cmd.Prepared = true
            Set rs = cmd.Execute
            
            if not rs.eof then ' the username is in use, don't update
                validation_username=0
            end if
        
            rs.Close()
            Set rs = Nothing
        end if 

For the page authentication, at the top of every page you will see code below. What is happening is the functions.asp page is loading and retrieving the cookie with the token and doing the look up of the user and placing the data into several fields. One field is status and another is user level. In the code directly below we are looking to see if the status is "0" or if the words, "Admin" or "Support" are not found. If that is the case, we redirect them to the index page. If you have multiple user levels that are allowed on the page, just add more seperated by a space.  

对于页面身份验证,在每个页面的顶部,您将在下面看到代码。 发生的是functions.asp页正在加载和检索带有令牌的cookie,并进行用户查找并将数据放入几个字段中。 一个字段是状态,另一个字段是用户级别。 在下面的代码中,我们正在查看状态是否为“ 0”,或者是否未找到“ Admin”或“ Support”一词。 如果是这种情况,我们会将其重定向到索引页面。 如果页面上允许有多个用户级别,则只需添加多个以空格分隔的用户即可。

if logged_in_status = "0" OR  instr("Support Admin",logged_in_level)<1 then
	response.Redirect("index.asp")
end if 

If you allow multiple user levels on one page and want to restrict a specific portion of one page to only one user level, you can use the sample code in the support.asp page. Here we are using the variable logged_in_level and testing if it is set to "Admin". If it is, then show the html inside the if/then.

如果在一页上允许多个用户级别,并且要将页面的特定部分限制为仅一个用户级别,则可以使用support.asp页中的示例代码。 在这里,我们使用变量logging_in_level并测试它是否设置为“ Admin”。 如果是,则在if / then内显示html。

<h1>Support</h1>
<div>This section can be seen by Support and Admin</div>
<%
if logged_in_level = "Admin" then
%>
<div>This section can be seen ONLY by Admin</div>
<%
end if
%> 

If your page will only have one user level, you can use the code below instead. 

如果您的页面只有一个用户级别,则可以改用下面的代码。

if logged_in_status = "0" OR logged_in_level<>"Admin" then
	response.Redirect("index.asp")
end if 

Because we are adding a row of data to the login trans table, you could develop a report that shows all past logins, how many times any one user logged in, how many times all users logged in between two dates or there are APIs you can use to lookup an IP and get its location.

由于我们正在向登录转换表中添加一行数据,因此您可以开发一个报告,以显示所有过去的登录信息,一个用户登录的次数,两个日期之间所有用户登录的次数或可以使用的API用于查找IP并获取其位置。

Another option you may want to impliment for your users is showing the amount of time they have left until their session expires. A simple way is to simply display the logged_in_expires field.

您可能要隐含给用户的另一种选择是显示他们的会话期满之前所剩下的时间。 一种简单的方法是仅显示logging_in_expires字段。

response.write logged_in_expires 

This will show the full date and time staticly. If you would like to show some type of countdown timer, I would suggest creating an animated countdown timer in javascript or use a ready made project. For the start time, you can use the field logged_in_expires.

这将静态显示完整的日期和时间。 如果您想显示某种倒数计时器,建议您在javascript中创建一个动画的倒数计时器或使用现成的项目。 对于开始时间,您可以使用字段logging_in_expires。

You choose an animated countdown timer such as http://hilios.github.io/jQuery.countdown/. They show a code sample:

您可以选择一个动画的倒数计时器,例如http://hilios.github.io/jQuery.countdown/ 。 他们显示了一个代码示例:

<div id="getting-started"></div>
 <script type="text/javascript">
   $("#getting-started")
   .countdown("2016/01/01", function(event) {
     $(this).text(
       event.strftime('%D days %H:%M:%S')
     );
   });
 </script> 

The modification you would need to make could be:

您需要进行的修改可能是:

<div id="getting-started"></div>
 <script type="text/javascript">
<%
dim countdown_time
countdown_time=year(logged_in_expires)&"/"& right("0"&month(logged_in_expires),2)&"/"&right("0"&day(logged_in_expires),2)
%>
   $("#getting-started")
   .countdown("<%=countdown_time%>", function(event) {
     $(this).text(
       event.strftime('%D days %H:%M:%S')
     );
   });
 </script> 

On the config.asp page, we are setting the default log in time for 60 minutes. Feel free to change that to whatever number you want. If you prefer to allow people to be logged in for a specific number of days, weeks or months instead of  minutes, you can either do the conversion to minutes yourself, or find the line of code in the functions.asp page that sets the token_expires variable. The letter "n" signifies minutes. If you want to use days, use a "d" or "m" for months. http://www.w3schools.com/vbscript/func_dateadd.asp   

在config.asp页上,我们将默认登录时间设置为60分钟。 随意将其更改为您想要的任何数字。 如果您希望允许人们登录特定天数,数周或数月而不是数分钟,则可以自己转换为分钟数,也可以在functions.asp页面中找到设置token_expires的代码行变量。 字母“ n”表示分钟。 如果要使用几天,请使用“ d”或“ m”数月。 http://www.w3schools.com/vbscript/func_dateadd.asp

token_expires = dateAdd("n",login_session_minutes,now) 

You will also see a secret_key variable on the config.asp page. Make sure to change this to something else. It can be any combination of characters you want. Just make sure it is random and long.

您还将在config.asp页面上看到secret_key变量。 确保将其更改为其他内容。 它可以是您想要的字符的任意组合。 只要确保它是随机且长的即可。

翻译自: https://www.experts-exchange.com/articles/18259/Classic-ASP-Login-System-Utilizing-a-Token.html

令牌桶生成令牌

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值