Since
security in a log-in scenario appears to be your goal here, I'd base the "architecture" of the code on
that. Of course, there's different
degrees of security, ranging from lightweight techniques designed to stop "casual" hacking, then on to using Windows cryptography tools in .NET, up to very expensive professional third-party tools that integrate with databases and networks.
I'll illustrate with a sketch of a WinForms architecture for a "light-weight" secure log-in that is based on the following principles:
1. the Application's Main Form should not be
created until the user has successfully logged in.
2. there should be absolutely no "coupling" (dependency) between the log-in process and the main Application. The Log-in Form should have no "knowledge or awareness" of the Main Form, and the Main Form have no knowledge or awareness of the Log-in Form.
Structure:
1. Two Forms, LogIn, and MainForm.
a. LogIn: Buttons btnCancel and btnLogIn, Textbox tbUserName for user name, MaskedTextBox tbMaskedPassword for password entry.
b. MainForm: one Label, 'lblUserInfo
2. The .NET WinForms app is set to be a single start-up Project, and the Program.cs file, as usual, set to be the start-up Object.
The Program.cs file Main method:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new LogIn());
Application.Run(new MainForm());
}
This variation on the standard WinForms Program.cs file exhibits the following behavior:
1. first a LogIn Form will be created and shown, and the Application will not continue until the LogIn Form is disposed (closed).
2. after the LogIn Form is closed, then the MainForm is created and shown.
3. calling Application.Exit() at any time in the LogIn Form will terminate the Application.
LogIn Form:
public partial class LogIn : Form
{
public LogIn()
{
InitializeComponent();
}
private static int nTries = 3;
private static int tryNumber = 1;
private void btnLogin_Click(object sender, EventArgs e)
{
if (tryNumber > nTries)
{
this.Hide();
MessageBox.Show("Access denied");
Application.Exit();
}
tryNumber++;
if(tbMaskedPassword.Text == "true" && tbUserName.Text == "User")
{
LogInData.logInOk = true;
LogInData.uName = tbUserName.Text;
this.Close();
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
LogInData static Class:
public static class LogInData
{
public static bool logInOk { get; set; }
public static string uName { get; set; }
}
Discussion:
1. static variables are used in the LogIn Form to count the number of attempts to log-in; if the limit is exceeded, a log-in denied message is shown, and the Application is terminated.
2. if the log-in is successful, the user name and password entered is saved by setting the static variables in the static LogInData class, at which point the LogIn Form is closed.
In the MainForm's Load EventHandler:
private void Form1_Load(object sender, EventArgs e)
{
if(LogInData.logInOk)
{
lblUserInfo.Text += LogInData.uName + " logged in: " + DateTime.Now.ToLongDateString();
}
else
{
Application.Exit();
}
}
There's an extra ... and really unnecessary level of further validation sketched in here just for the sake of raising the possibility of two-level validtion. The way the code is now, the MainForm should never be loaded/shown if the log-in process failed.
Big picture:
1. in this case the static LogInData Class is used as a
repository for the result of a successful log-in