最近看了一下传智播客的免费公开课视频,有一个登录界面的小练习。其中涉及到登录时间的限定功能,并未实现。所以我就自己动手写了一个,大家可以看看。
首先编写SqlHelper类,通过SqlHelper类实现SQL单语句操作数据库。代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data.SqlClient; 6 using System.Data; 7 8 namespace 登录界面 9 { 10 class SqlHelper 11 { 12 //此处没有设置为App.Config文件里的配置,只是想简单实现本项目。 13 private static string connectStr = @"Data Source=.\SQLEXPRESS;Initial Catalog=JJLogin;Integrated Security=True;"; 14 15 public static void ExecuteNonQuery(string sql, params SqlParameter[] parameters) 16 { 17 using (SqlConnection conn = new SqlConnection(connectStr)) 18 { 19 conn.Open(); 20 using (SqlCommand cmd = conn.CreateCommand()) 21 { 22 cmd.CommandText = sql; 23 cmd.Parameters.AddRange(parameters); 24 cmd.ExecuteNonQuery(); 25 } 26 } 27 } 28 29 public static object ExecuteScalar(string sql, params SqlParameter[] parameters) 30 { 31 using (SqlConnection conn = new SqlConnection(connectStr)) 32 { 33 conn.Open(); 34 using (SqlCommand cmd = conn.CreateCommand()) 35 { 36 cmd.CommandText = sql; 37 cmd.Parameters.AddRange(parameters); 38 object obj = cmd.ExecuteScalar(); 39 return obj; 40 } 41 } 42 } 43 44 public static DataTable ExecuteDataTable(string sql, params SqlParameter[] parameters) 45 { 46 using (SqlConnection conn = new SqlConnection(connectStr)) 47 { 48 conn.Open(); 49 using (SqlCommand cmd =conn.CreateCommand()) 50 { 51 cmd.CommandText = sql; 52 cmd.Parameters.AddRange(parameters); 53 SqlDataAdapter adapter = new SqlDataAdapter(cmd); 54 DataTable ds = new DataTable(); 55 adapter.Fill(ds); 56 return ds; 57 } 58 } 59 } 60 } 61 }
因最近半个月都在学习WPF技术,所以本示例采用的是WPF显示层技术。XAML代码如下:
1 <Window x:Class="登录界面.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="微博登录" Height="200" Width="250" ResizeMode="NoResize"> 5 <Grid> 6 <Grid.RowDefinitions> 7 <RowDefinition Height="Auto"></RowDefinition> 8 <RowDefinition Height="Auto"></RowDefinition> 9 <RowDefinition Height="Auto"></RowDefinition> 10 <RowDefinition Height="*"></RowDefinition> 11 </Grid.RowDefinitions> 12 <Grid.ColumnDefinitions> 13 <ColumnDefinition Width="Auto"></ColumnDefinition> 14 <ColumnDefinition Width="*"></ColumnDefinition> 15 <ColumnDefinition Width="Auto"></ColumnDefinition> 16 </Grid.ColumnDefinitions> 17 <TextBlock Grid.Row="0" Grid.Column="0" Text="用户名"></TextBlock> 18 <TextBlock Grid.Row="1" Grid.Column="0" Text="密码"></TextBlock> 19 <TextBox x:Name="txt_UserName" Grid.Row="0" Grid.Column="1" Margin="3"></TextBox> 20 <PasswordBox x:Name="txt_PassWord" Grid.Row="1" Grid.Column="1" Margin="3"></PasswordBox> 21 <DockPanel x:Name="btndock_login" Grid.Row="2" Grid.Column="1" Margin="3" Button.Click="btndock_login_Click"> 22 <Button x:Name="btn_loginIn" Content="登录" HorizontalAlignment="Left" Margin="3"></Button> 23 <Button x:Name="btn_loginOut" Content="取消" HorizontalAlignment="Right" Margin="3"></Button> 24 </DockPanel> 25 <TextBlock x:Name="tbk_LoginMessage" Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Stretch" TextWrapping="Wrap"></TextBlock> 26 </Grid> 27 </Window>
逻辑层代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Windows; 6 using System.Windows.Controls; 7 using System.Windows.Data; 8 using System.Windows.Documents; 9 using System.Windows.Input; 10 using System.Windows.Media; 11 using System.Windows.Media.Imaging; 12 using System.Windows.Navigation; 13 using System.Windows.Shapes; 14 using System.Data.SqlClient; 15 using System.Data; 16 using System.ComponentModel; 17 18 namespace 登录界面 19 { 20 /// <summary> 21 /// MainWindow.xaml 的交互逻辑 22 /// </summary> 23 public partial class MainWindow : Window 24 { 25 private Message message; 26 public MainWindow() 27 { 28 InitializeComponent(); 29 30 message = new Message(); 31 tbk_LoginMessage.SetBinding(TextBlock.TextProperty, new Binding("LoginInfo") { Source = message }); 32 } 33 34 private void btndock_login_Click(object sender, RoutedEventArgs e) 35 { 36 if (e.OriginalSource is Button) 37 { 38 switch (((Button)e.OriginalSource).Name) 39 { 40 case "btn_loginIn": 41 { 42 LoginIn(txt_UserName.Text, txt_PassWord.Password); 43 } break; 44 case "btn_loginOut": 45 { 46 txt_UserName.Text = string.Empty; 47 txt_PassWord.Password = string.Empty; 48 } 49 break; 50 default: break; 51 } 52 } 53 else 54 { 55 throw new Exception("请点击登录或取消按钮"); 56 } 57 } 58 59 private void LoginIn(string userName, string password) 60 { 61 //查询SQLServer默认情况下不区分大小写,因此对于用户名需要采取区分大小写的查询方式,此程序有待升级 62 string sqlSelect = "select * from T_User where [email protected]"; 63 DataTable table = SqlHelper.ExecuteDataTable(sqlSelect, new SqlParameter("@UserName", txt_UserName.Text)); 64 int count = table.Rows.Count; 65 if (count <= 0) 66 { 67 message.LoginInfo = "用户不存在"; 68 return; 69 } 70 else if (count > 1) 71 { 72 message.LoginInfo = "不好啦,用户名重复啦"; 73 return; 74 } 75 DataRow row = table.Rows[0]; 76 long id = (long)row["ID"]; 77 string dbPassword = (string)row["PassWord"]; 78 int frequency = (int)row["Frequency"]; 79 DateTime dbTime = (DateTime)row["ErrorTime"]; 80 if (frequency >= 3) 81 { 82 //如果frequency>=4就会比较dbTime与DateTime.Now之间的时间间隔是否大于15s 83 if (frequency >= 4) 84 { 85 TimeSpan timespan = DateTime.Now - dbTime; 86 double d = timespan.TotalSeconds; 87 if (d < 15.0) 88 { 89 message.LoginInfo = "请15s后再来登陆"; 90 return; 91 } 92 //只要超过15s间隔再登陆就应该设置错误次数=0 93 frequency = 0; 94 goto passwordValidate; 95 } 96 97 string sqlUpdateErrorTime = "Update T_User set [email protected],[email protected]+1"; 98 SqlHelper.ExecuteNonQuery(sqlUpdateErrorTime, new SqlParameter("@ErrorTime", DateTime.Now), new SqlParameter("@Frequency", frequency)); 99 message.LoginInfo = "输入错误次数太多,请耐心等待15s后再登陆"; 100 return; 101 } 102 103 passwordValidate: 104 if (dbPassword != password) 105 { 106 string sqlUpdateFrequency = "Update T_User set [email protected]+1"; 107 SqlHelper.ExecuteNonQuery(sqlUpdateFrequency, new SqlParameter("@Frequency", frequency)); 108 message.LoginInfo = "密码输入错误"+(frequency+1)+"次"; 109 return; 110 } 111 else 112 { 113 frequency = 0; 114 string sqlUpdateFrequency = "Update T_User set [email protected]"; 115 SqlHelper.ExecuteNonQuery(sqlUpdateFrequency, new SqlParameter("@Frequency", frequency)); 116 message.LoginInfo = "登陆成功"; 117 } 118 } 119 } 120 121 public class Message : INotifyPropertyChanged 122 { 123 private string loginInfo; 124 public string LoginInfo 125 { 126 get { return loginInfo; } 127 set 128 { 129 loginInfo = value; 130 if (PropertyChanged != null) 131 { 132 PropertyChanged(this, new PropertyChangedEventArgs("LoginInfo")); 133 } 134 } 135 } 136 137 public event PropertyChangedEventHandler PropertyChanged; 138 } 139 }
数据库设计:
1 //ID bigint 2 //UserName nvchar(20) 3 //PassWord nvchar(20) 4 //Frequency int 5 //ErrorTime datetime
关于登录时间的处理,本示例的思路是:如果连续三次登录错误,就会将当前系统时间存进数据库,但是输入错误次数(frequency)不清零。当你再次尝试登录时,会从数据库中读出本用户上次错误登录时的时间,将该值与现在的系统时间进行比较,如果小于规定的时间间隔,则提示用户耐心等待。如果时间间隔(timespan)大于规定的时间间隔,则将错误登录次数清零,接着进行经常的密码验证流程。
本示例是一个简单版本,当然会有很多考虑不周到的地方。目前的不足:
1.数据库查询如何进行大小写敏感设置,目前我还不会如何设置,所以数据库中的用户名输入大小写都能登录成功,这个肯定要改。
2.因为是用WPF做的界面,所以关于登录信息提示的TextBlock,其文本属性应该绑定到Message消息中,所以可设置一个Message类,将提示信息文本保存在Message类的LoginInfo属性中。但是用户名和密码等输入文本框也应该以Mode=OneWayToSource绑定到User类的UserName与Password属性中。并对输入的数据进行Validation,通过自定义UserNameValidationRule和PasswordValidationRule。这些细节目前还没完全实现,正在编码中。在接下来的WPF学习中会继续研究。
最后,大家如有好的意见可以留言。