【無私分享:從入門到精通ASP.NET MVC】從0開始,一起搭框架、做專案(5.1) 登入功能的實現,開始接觸Spring IOC、DI
索引
簡述
今天我們做登入,今天的東西比較多,用到了Spring的IOC和DI、介面的使用、驗證等,希望大家多多討論
專案準備
我們用的工具是:VS 2013 + SqlServer 2012 + IIS7.5
希望大家對ASP.NET MVC有一個初步的理解,理論性的東西我們不做過多解釋,有些地方不理解也沒關係,會用就行了,用的多了,用的久了,自然就理解了。
專案開始
一、首先簡單介紹一下IOC和DI
Ioc:Inversion of Control,即“控制反轉”,他不是什麼新的技術,而是一種設計思想。
通常我們是這麼理解,我們一般的設計思想是在物件內部直接控制,而IOC是將設計好的物件交給容器控制,而不是傳統的在物件內部直接控制。
打個比方:我們租房子,在我們和房主之間插入了一箇中間人(房介),我們只需要跟房介提出我們的要求,比如房子要三室一廳、臥室向陽、房東是女的(*^_^* )、樓層不要太低、遮光不要太長等等等等,然後房介就會按照我們的要求給我們提供一個房產資訊,我們滿意就跟租賃、入住,如果我們不滿意(丟擲異常),房介就會幫我們做後續處理。整個過程不再是由我們控制,而是由房介這麼一個容器去控制。所有的類都會在spring容器中登記,告訴spring你是個什麼東西,你需要什麼東西,然後spring會在系統執行到適當的時候,把你要的東西主動給你,同時也把你交給其他需要你的東西。所有的類的建立、銷燬都由 spring來控制,也就是說控制物件生存週期的不再是引用它的物件,而是spring。對於某個具體的物件而言,以前是它控制其他物件,現在是所有物件都被spring控制,所以這叫控制反轉。
DI:IOC的一個重點是在系統執行中,動態的向某個物件提供它所需要的其他物件。這一點是通過DI(Dependency Injection,依賴注入)來實現的。比如物件A需要操作資料庫,以前我們總是要在A中自己編寫程式碼來獲得一個Connection物件,有了 spring我們就只需要告訴spring,A中需要一個Connection,至於這個Connection怎麼構造,何時構造,A不需要知道。在系統執行時,spring會在適當的時候製造一個Connection,然後像打針一樣,注射到A當中,這樣就完成了對各個物件之間關係的控制。A需要依賴 Connection才能正常執行,而這個Connection是由spring注入到A中的,依賴注入的名字就這麼來的。那麼DI是如何實現的呢? 有一個重要特徵是反射(reflection),它允許程式在執行的時候動態的生成物件、執行物件的方法、改變物件的屬性,spring就是通過反射來實現注入的。
如果你還不理解,那麼請移步百度~~
二、理論性的東西,通過程式碼的實現更容易理解一些,現在我們來做使用者管理的介面
我們首先在Service類庫下面建立兩個資料夾 IService(存放介面)、ServiceImp(實現類),並且在這兩個資料夾下面再分別建兩個資料夾ComManage和SysManage,使用者區分管理我們的介面和實現類
我們建立系統管理使用者SYS_USER的介面:右擊IService/SysManage→新增→新建項→介面 我們叫做IUserManage
SysManage只是我們為了區分管理我們的介面檔案,我們修改一下名稱空間,讓他們統一在Service.IService空間下,並且我們繼承基礎資料操作介面 IRepository
我們新增介面方法:驗證登入、判斷是否為管理員、根據使用者ID獲取使用者名稱
程式碼:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Service.IService 8 { 9 /// <summary> 10 /// Service層基本使用者資訊介面 11 /// add yuangang by 2016-05-12 12 /// </summary> 13 public interface IUserManage:IRepository<Domain.SYS_USER> 14 { 15 /// <summary> 16 /// 管理使用者登入驗證,並返回使用者資訊與許可權集合 17 /// </summary> 18 /// <param name="username">使用者賬號</param> 19 /// <param name="password">使用者密碼</param> 20 /// <returns></returns> 21 Domain.SYS_USER UserLogin(string useraccount, string password); 22 /// <summary> 23 /// 是否超級管理員 24 /// </summary> 25 /// <param name="userId">使用者ID</param> 26 /// <returns></returns> 27 bool IsAdmin(int userId); 28 /// <summary> 29 /// 根據使用者ID獲取使用者名稱,不存在返回空 30 /// </summary> 31 /// <param name="userId">使用者ID</param> 32 /// <returns></returns> 33 string GetUserName(int userId); 34 } 35 }View Code
我們建立使用者介面的實現類:右擊ServiceImp/SysManage→新增→類→ 我們叫做UserManage
同樣,我們修改一下名稱空間,讓他們統一在Service.ServiceImp空間下,並且我們繼承基礎資料操作類 RepositoryBase和他的介面IUserManage
我們分別實現介面的方法:
程式碼:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Service.ServiceImp 8 { 9 public class UserManage:RepositoryBase<Domain.SYS_USER>,IService.IUserManage 10 { 11 /// <summary> 12 /// 管理使用者登入驗證 13 /// add yuangang by 2016-05-12 14 /// </summary> 15 /// <param name="useraccount">使用者名稱</param> 16 /// <param name="password">加密密碼(AES)</param> 17 /// <returns></returns> 18 public Domain.SYS_USER UserLogin(string useraccount, string password) 19 { 20 var entity = this.Get(p => p.ACCOUNT == useraccount); 21 22 //因為我們用的是AES的動態加密演算法,也就是沒有統一的金鑰,那麼兩次同樣字串的加密結果是不一樣的,所以這裡要通過解密來匹配 23 //而不能通過再次加密輸入的密碼來匹配 24 if (entity != null && new Common.CryptHelper.AESCrypt().Decrypt(entity.PASSWORD) == password) 25 { 26 return entity; 27 } 28 return null; 29 } 30 31 /// <summary> 32 /// 是否超級管理員 33 /// </summary> 34 public bool IsAdmin(int userId) 35 { 36 //這裡我們還沒有做使用者角色 所以先返回個True,後面我們做角色的時候再回來修改 37 return true; 38 } 39 40 /// <summary> 41 /// 根據使用者ID獲取使用者名稱 42 /// </summary> 43 /// <param name="Id">使用者ID</param> 44 /// <returns></returns> 45 public string GetUserName(int Id) 46 { 47 var query = this.LoadAll(c => c.ID == Id); 48 if (query == null || !query.Any()) 49 { 50 return ""; 51 } 52 return query.First().NAME; 53 } 54 } 55 }View Code
重要:在這說明一下,因為之前我一直用的DES加密演算法,後來改成了AES動態加密,但是Common類庫下面的CryptHelper/DESCrypt一直沒有改過來,這給很多細心的朋友造成了困擾,命名方法體就是 【C#公共幫助類】給大家分享一些加密演算法 (DES、HashCode、RSA、AES等) 裡的AES,這裡怎麼是DES呢?首先,我表示抱歉,在這呢,一勞永逸,我修改了Common/CryptHelper/DESCrypt,目前我們用到的地方只有兩個,一個就是我們上面程式碼中的 UserLogin,另一個是Common/Utils.cs 第1611行, 獲得當前完整Url地址。大家修改一下。
我重新把修改後的程式碼貼一下:
1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Security.Cryptography; 6 using System.Text; 7 8 namespace Common.CryptHelper 9 { 10 public class AESCrypt 11 { 12 public const string RET_ERROR = "x07x07x07x07x07"; 13 private byte[] _IV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; 14 private byte[] _Key = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 15 private const string CRYPTO_KEY = "WKMVCYUANGANG"; 16 private int CRYPTO_KEY_LENGTH = 32; 17 18 private AesCryptoServiceProvider m_aesCryptoServiceProvider; 19 private string m_message; 20 public string Message 21 { 22 get { return m_message; } 23 set { m_message = value; } 24 } 25 private bool m_containKey; 26 /// <summary> 27 /// True:密文中包含金鑰 28 /// False:密文中不包含金鑰 29 /// </summary> 30 public bool ContainKey 31 { 32 get { return m_containKey; } 33 set { m_containKey = value; } 34 } 35 public AESCrypt() 36 { 37 m_aesCryptoServiceProvider = new AesCryptoServiceProvider(); 38 m_containKey = true; 39 m_message = string.Empty; 40 } 41 public AESCrypt(bool containKey) 42 : this() 43 { 44 m_containKey = containKey; 45 } 46 private string Encrypt(string s_crypto, byte[] key) 47 { 48 string s_encryped = string.Empty; 49 byte[] crypto, encrypted; 50 ICryptoTransform ct; 51 52 try 53 { 54 crypto = string2Byte(s_crypto); 55 m_aesCryptoServiceProvider.Key = key; 56 m_aesCryptoServiceProvider.IV = _IV; 57 ct = m_aesCryptoServiceProvider.CreateEncryptor(); 58 encrypted = ct.TransformFinalBlock(crypto, 0, crypto.Length); 59 if (m_containKey) 60 { 61 s_encryped += byte2HexString(key); 62 } 63 s_encryped += byte2HexString(encrypted); 64 return s_encryped; 65 } 66 catch (Exception ex) 67 { 68 m_message = ex.ToString(); 69 return RET_ERROR; 70 } 71 } 72 /// <summary> 73 /// 指定金鑰對明文進行AES加密 74 /// </summary> 75 /// <param name="s_crypto">明文</param> 76 /// <param name="s_key">加密金鑰</param> 77 /// <returns></returns> 78 public string Encrypt(string s_crypto, string s_key) 79 { 80 byte[] key = new byte[CRYPTO_KEY_LENGTH]; 81 82 byte[] temp = string2Byte(s_key); 83 if (temp.Length > key.Length) 84 { 85 m_message = "Key too long,need less than 32 Bytes key."; 86 return RET_ERROR; 87 } 88 key = string2Byte(s_key.PadRight(key.Length)); 89 return Encrypt(s_crypto, key); 90 } 91 /// <summary> 92 /// 動態生成金鑰,並對明文進行AES加密 93 /// </summary> 94 /// <param name="s_crypto">明文</param> 95 /// <returns></returns> 96 public string Encrypt(string s_crypto) 97 { 98 byte[] key = new byte[CRYPTO_KEY_LENGTH]; 99 100 m_aesCryptoServiceProvider.GenerateKey(); 101 key = m_aesCryptoServiceProvider.Key; 102 return Encrypt(s_crypto, key); 103 } 104 105 private string Decrypt(string s_encrypted, byte[] key) 106 { 107 string s_decrypted = string.Empty; 108 byte[] encrypted, decrypted; 109 ICryptoTransform ct; 110 111 try 112 { 113 encrypted = hexString2Byte(s_encrypted); 114 m_aesCryptoServiceProvider.Key = key; 115 m_aesCryptoServiceProvider.IV = _IV; 116 ct = m_aesCryptoServiceProvider.CreateDecryptor(); 117 decrypted = ct.TransformFinalBlock(encrypted, 0, encrypted.Length); 118 s_decrypted += byte2String(decrypted); 119 return s_decrypted; 120 } 121 catch (Exception ex) 122 { 123 m_message = ex.ToString(); 124 m_message = "Decrypt fail."; 125 return RET_ERROR; 126 } 127 } 128 /// <summary> 129 /// 從密文中解析出金鑰,並對密文進行解密 130 /// </summary> 131 /// <param name="s_encrypted">密文</param> 132 /// <returns></returns> 133 public string Decrypt(string s_encrypted) 134 { 135 string s_key = string.Empty; 136 byte[] key = new byte[CRYPTO_KEY_LENGTH]; 137 138 if (s_encrypted.Length <= CRYPTO_KEY_LENGTH * 2) 139 { 140 m_message = "Encrypted string invalid."; 141 return RET_ERROR; 142 } 143 if (m_containKey) 144 { 145 s_key = s_encrypted.Substring(0, CRYPTO_KEY_LENGTH * 2); 146 s_encrypted = s_encrypted.Substring(CRYPTO_KEY_LENGTH * 2); 147 } 148 key = hexString2Byte(s_key); 149 return Decrypt(s_encrypted, key); 150 } 151 /// <summary> 152 /// 指定金鑰,並對密文進行解密 153 /// </summary> 154 /// <param name="s_encrypted">密文</param> 155 /// <param name="s_key">金鑰</param> 156 /// <returns></returns> 157 public string Decrypt(string s_encrypted, string s_key) 158 { 159 byte[] key = new byte[CRYPTO_KEY_LENGTH]; 160 161 byte[] temp = string2Byte(s_key); 162 if (temp.Length > key.Length) 163 { 164 m_message = "Key invalid.too long,need less than 32 Bytes"; 165 return RET_ERROR; 166 } 167 key = string2Byte(s_key.PadRight(key.Length)); 168 if (m_containKey) 169 { 170 s_encrypted = s_encrypted.Substring(CRYPTO_KEY_LENGTH * 2); 171 } 172 return Decrypt(s_encrypted, key); 173 } 174 175 #region 私有方法 176 private string byte2HexString(byte[] bytes) 177 { 178 StringBuilder sb = new StringBuilder(); 179 foreach (byte b in bytes) 180 { 181 sb.AppendFormat("{0:X2}", b); 182 } 183 return sb.ToString(); 184 } 185 private byte[] hexString2Byte(string hex) 186 { 187 int len = hex.Length / 2; 188 byte[] bytes = new byte[len]; 189 for (int i = 0; i < len; i++) 190 { 191 bytes[i] = (byte)(Convert.ToInt32(hex.Substring(i * 2, 2), 16)); 192 } 193 return bytes; 194 } 195 private byte[] string2Byte(string str) 196 { 197 return Encoding.UTF8.GetBytes(str); 198 } 199 private string byte2String(byte[] bytes) 200 {