1. 로그인 로그
- 로그를 남기는 방법은 많고 DB가 아닌 .txt파일이나 따로 파일에서 관리하는 경우도 많지만 회사에서 LOGIN LOG를 DB에 남기길래 나도 DB에 남겨보기로 했다
- 로그에 들어가는 정보
- 로그인한 회원 번호
- 로그인한 회원 아이디
- 로그인 했을 때의 외부 IP
- 로그인 했을 때 시간
- 여기에 권한적인 정보도 넣을까 하다가 솔직히 user번호 자체가 고유번호라서 아이디도 필요 없는데 그냥 일단 심심해서 넣었다
01. DB / TABLE
CREATE TABLE Book_User_Log(
user_no INT NULL
,user_id NVARCHAR(20) NULL
,enter_ip NVARCHAR(20) NULL
,enter_date DATETIME NULL
);
02. DB / PROCEDURE / BOOK_LOGIN_I1
- 첫 INSERT 프로시저이다!
- 솔직히 검사할것도 없고 그냥 다 찍어넣을거라 할게 없다
- 외부 IP는 알아두면 어디서 접속했는지 정보도 얻지만 나중에는 특정 IP외에 접속을 막을 수도 있다
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <문민승>
-- Create date: <2021.08.19>
-- Description: <도서관리프로그램, 로그인 로그>
-- =============================================
CREATE PROCEDURE [dbo].[BOOK_LOGIN_I1]
@USER_NO INT
,@USER_ID NVARCHAR(20)
,@ENTER_IP NVARCHAR(20)
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
INSERT INTO Book_User_Log VALUES(@USER_NO, @USER_ID, @ENTER_IP, GETDATE());
END
GO
03. Repository / IBookUserRepository.interface
namespace BookManagementProgram.Repository
{
interface IBookUserRepository
{
string Login(string userId, string userPw);
string FindById(string userNo, string userName);
bool FindByPassword(string userNo, string userId, string newPassword);
bool InsertLoginLog();
}
}
04. Repository / BookUserRepository.css
- 이제 크게 어렵지는 않을거라고 생각이 된다
- 이번에는 외부 아이피를 갖고오기 위해서 GetExternalIPAddress 메서드와 외부 ip를 못 받아올 경우를 대비해 GetInternalIPAddress 메서드로 내부 ip를 반환하는 메서드를 추가했다
using BookManagementProgram.Model;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
namespace BookManagementProgram.Repository
{
class BookUserRepository : IBookUserRepository
{
private SqlConnection conn = null;
private SqlCommand cmd = null;
public static List<User> userList = null;
public static User user = null;
public BookUserRepository()
{
conn = DBConn.DBConn.getConn();
cmd = new SqlCommand();
cmd.Connection = conn;
}
// 로그인 로직
public string Login(string userId, string userPw)
{
try
{
string sql = $"EXEC BOOK_LOGIN_S1 @USER_ID = '{userId}', @USER_PW = '{Encryption(userPw)}';";
cmd.CommandText = sql;
conn.Open();
// 데이터 출력
using (SqlDataReader SR = cmd.ExecuteReader())
{
while (SR.Read())
{
user = new User(
SR[0].ToString()
, SR[1].ToString()
, SR[2].ToString()
, SR[3].ToString()
, SR[4].ToString()
, SR[5].ToString()
, SR[6].ToString()
, SR[7].ToString()
, SR[8].ToString()
);
}
SR.Close();
}
return user.userNo;
}
catch(SqlException e)
{
return e.Message;
}
finally
{
conn.Close();
}
}
// SHA256 암호화 메서드
public static string Encryption(string data)
{
SHA256 sha = new SHA256Managed();
byte[] hash = sha.ComputeHash(Encoding.ASCII.GetBytes(data));
StringBuilder stringBuilder = new StringBuilder();
foreach (byte b in hash)
{
stringBuilder.AppendFormat("{0:x2}", b);
}
return stringBuilder.ToString();
}
// 아이디 찾기 로직
public string FindById(string userNo, string userName)
{
try
{
string sql = $"EXEC BOOK_LOGIN_S2 @USER_NO = {userNo}, @USER_NAME = '{userName}';";
string user_id = string.Empty;
cmd.CommandText = sql;
conn.Open();
// 데이터 출력
using (SqlDataReader SR = cmd.ExecuteReader())
{
while (SR.Read())
{
user_id = SR[0].ToString();
}
SR.Close();
}
return $"아이디 : {user_id} 입니다.";
}
catch (SqlException e)
{
return e.Message;
}
finally
{
conn.Close();
}
}
// 비밀번호 찾기 로직
public bool FindByPassword(string userNo, string userId, string newPassword)
{
try
{
string sql = $"EXEC BOOK_LOGIN_U1 @USER_NO = {userNo}, @USER_ID = '{userId}', @NEW_PW = '{Encryption(newPassword)}';";
cmd.CommandText = sql;
conn.Open();
return cmd.ExecuteNonQuery() > 0 ? true : false;
}
catch (SqlException e)
{
return false;
}
finally
{
conn.Close();
}
}
public bool InsertLoginLog()
{
try
{
string sql = $"EXEC BOOK_LOGIN_I1 @USER_NO = {user.userNo}, @USER_ID = '{user.userId}', @ENTER_IP = '{GetExternalIPAddress()}';";
cmd.CommandText = sql;
conn.Open();
return cmd.ExecuteNonQuery() > 0 ? true : false;
}
catch (SqlException e)
{
return false;
}
finally
{
conn.Close();
}
}
// 외부 IP
public static string GetExternalIPAddress()
{
string externalip = new WebClient().DownloadString("http://ipinfo.io/ip").Trim();
return externalip ?? GetInternalIPAddress();//null경우 내부 IP 반환;
}
// 내부
// DNS, 단순 도메인 이름 확인 기능이 제공
// GetGostEntity, 호스트 이름 또는 IP 주소를 IPHostEntry 인스턴스로 확인
// GetHostName, 로컬 컴퓨터의 호스트 이름을 가져온다
public static string GetInternalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("인터넷 연결 없음");
}
}
}
05. LOGIN / Login.css
- 간단히 4줄이 추가가 되었다
- 프로시저에서 번호와 아이디를 받는건 어차피 로그인정보 다 확인했으니 Repository에서 처리하게 해두었고 만약에 Insert가 안된다면 인터넷 문제일 확률이 높기에 DB와 TABLE이 날라가거나 수정이 되지 않는한 그래서 false를 반환하면 경로 문을 띄워준다
using BookManagementProgram.Repository;
using System;
using System.Linq;
using System.Windows.Forms;
namespace BookManagementProgram.LOGIN
{
public partial class Login : Form
{
BookUserRepository userRepository = new BookUserRepository();
public Login()
{
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
if(Properties.Settings.Default.isLoginIdSave)
{
id_txt.Text = Properties.Settings.Default.LoginId;
idSaveCheck_chk.Checked = Properties.Settings.Default.isLoginIdSave;
}
}
// TextBox Focussing
private void label1_Click(object sender, EventArgs e)
{
id_txt.Focus();
}
private void label2_Click(object sender, EventArgs e)
{
pw_txt.Focus();
}
// 비밀번호 보이기 체크박스
private void showPassword_chk_CheckedChanged(object sender, EventArgs e)
{
if (showPassword_chk.Checked)
pw_txt.PasswordChar = default(char);
else
pw_txt.PasswordChar = '*';
}
// 아이디 저장 체크박스
private static void NotSaveID()
{
Properties.Settings.Default.isLoginIdSave = false;
Properties.Settings.Default.Save();
}
private void SaveID()
{
Properties.Settings.Default.LoginId = id_txt.Text;
Properties.Settings.Default.isLoginIdSave = true;
Properties.Settings.Default.Save();
}
// 아이디 찾기 라벨
private void findId_lbl_Click(object sender, EventArgs e)
{
(new FindById()).ShowDialog();
}
// 비밀번호 찾기 라벨
private void findPassword_lbl_Click(object sender, EventArgs e)
{
(new FindByPassword()).ShowDialog();
}
// 로그인 버튼
private void login_btn_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(id_txt.Text))
{
MessageBox.Show("아이디를 입력해주세요.");
id_txt.Focus();
return;
}
if (string.IsNullOrEmpty(pw_txt.Text))
{
MessageBox.Show("비밀번호를 입력해주세요.");
pw_txt.Focus();
return;
}
string result = userRepository.Login(id_txt.Text, pw_txt.Text);
if (!result.All(char.IsDigit))
{
MessageBox.Show(result);
return;
}
if (!userRepository.InsertLoginLog())
{
MessageBox.Show("인터넷을 연결해주세요.");
return;
}
if (idSaveCheck_chk.Checked)
{
SaveID();
}
else
{
NotSaveID();
}
Main main = new Main();
main.Show();
main.BringToFront();
this.Hide();
}
}
}