該方案使用RSA加密和解密。javascript
每次登陸前,客戶端從服務器端獲取公鑰和隨機值。java
公鑰用於加密明文;git
隨機值能夠增強每一次操做的安全性,隨機值也加入明文中一併加密,服務端對隨機值進行校驗,校驗後從緩存中銷燬,這樣就算被別人拿到加密後的密文再次發起請求,因爲隨機值已失效,請求也是無效的。github
下面以js客戶端爲例,演示一下流程:web
一、假設客戶的密碼以SHA256加密後存在數據庫中spring
2.、客戶輸入用戶名和密碼點擊 「登陸」後,客戶端發起請求,從服務器端獲取公鑰和隨機值。數據庫
{ "rand": "SAXpJg", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK+oqElHP94+1BhhiTKX0pzziepN+C5Ff/qgmind2XvD35eWlCqzypGIXBoki526ZbsqrssbxTy5imhthe4eUTenLGUKkUgYUmDWrus8NmJm6IlXuqbGHaEY1zocsnlqVezOMj0AIUq5L65Y6e5XnEf1ludSzTF73MtFTjW8TRyQIDAQAB" }
三、客戶端將用戶輸入的密碼使用SHA256加密apache
<!--下載地址:https://github.com/Caligatio/jsSHA --> <script type="text/javascript" src="sha.js"></script> <!--下載地址:https://github.com/travist/jsencrypt--> <script type="text/javascript" src="jsencrypt.js"></script> <script> //用戶輸入的密碼 var password1 = '123456'; //從服務端得到的公鑰 var publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK+oqElHP94+1BhhiTKX0pzziepN+C5Ff/qgmind2XvD35eWlCqzypGIXBoki526ZbsqrssbxTy5imhthe4eUTenLGUKkUgYUmDWrus8NmJm6IlXuqbGHaEY1zocsnlqVezOMj0AIUq5L65Y6e5XnEf1ludSzTF73MtFTjW8TRyQIDAQAB"; //從服務端得到的隨機值 var rand = 'SAXpJg'; //SHA-256加密 var shaObj = new jsSHA("SHA-256", "TEXT"); shaObj.update(password1); var hash = shaObj.getHash("HEX"); //組裝明文:由加密後的密碼和隨機值組成 var text = hash + '|' + rand; console.log("待加密的文本: " + text); //使用RSA公鑰加密 var encrypt = new JSEncrypt(); encrypt.setPublicKey(publicKey); // password就能夠發送到服務端進行解密校驗了 var password = encrypt.encrypt(text); console.log("加密後的密文:" + password); </script>
控制檯打印出來的結果:緩存
待加密的明文:8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92|SAXpJg
加密後的密文:dgUBkZPZgL76+zMbKckAxb3C072I8b4nqAZlWUD/24Hp7UpAgiKx4P90xgs1UhWM2qputsjgpsgXLCNUg2vtO9MxpQk6zWUbyh4cxL08UcmMv3KIMO5rnbFxKEmuIbQ2G/3UZT8c+w899ERLCpDVyHrKSijdpvVoKrB6PzyjP+w=
而後將加密後的密文傳到服務器端便可。安全
四、服務器端代碼
RSAUtils.java
import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.SecureRandom; import java.security.Security; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import javax.crypto.Cipher; import org.apache.commons.codec.binary.Base64; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** * RSA 工具 * @author Luxh * */ public class RSAUtils { private static final String ALGORITHM = "RSA"; private static final String PROVIDER = "BC"; private static final String TRANSFORMATION = "RSA/None/PKCS1Padding"; private static final int KEY_SIZE = 1024; private static KeyPair keyPair = null; /** * 初始化密鑰對 */ static { try{ Security.addProvider(new BouncyCastleProvider()); SecureRandom secureRandom = new SecureRandom(); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER); keyPairGenerator.initialize(KEY_SIZE, secureRandom); keyPair = keyPairGenerator.generateKeyPair(); }catch(Exception e) { e.printStackTrace(); } } /** * 獲取公鑰 * @return */ public static RSAPublicKey getRSAPublicKey() { return (RSAPublicKey)keyPair.getPublic(); } /** * 獲取Base64編碼的公鑰 * @return */ public static String getBase64PublicKey() { RSAPublicKey publicKey = getRSAPublicKey(); //return new String(Base64.encodeBase64(publicKey.getEncoded())); return Base64.encodeBase64String(publicKey.getEncoded()); } /** * 使用公鑰加密 * @param data * @return */ public static String encrypt(byte[] data) { String ciphertext = ""; try { Cipher cipher = Cipher.getInstance(keyPair.getPublic().getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); ciphertext = Base64.encodeBase64String(cipher.doFinal(data)); } catch (Exception e) { e.printStackTrace(); } return ciphertext; } /** * 使用私鑰解密 * @param ciphertext * @return */ public static String decrypt(String ciphertext) { String plaintext = ""; try { Security.addProvider(new BouncyCastleProvider()); Cipher cipher = Cipher.getInstance(TRANSFORMATION, PROVIDER); RSAPrivateKey pbk = (RSAPrivateKey)keyPair.getPrivate(); cipher.init(Cipher.DECRYPT_MODE, pbk); byte[] data = cipher.doFinal(Base64.decodeBase64(ciphertext)); plaintext = new String(data); }catch (Exception e) { e.printStackTrace(); } return plaintext; } }
DemoController.java
import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.RandomStringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.google.common.collect.Maps; import io.caimi.util.RSAUtils; @RestController public class DemoController { /** * 獲取公鑰和隨機值 * */ @RequestMapping("/secret") public Map<String, Object> secret(HttpServletRequest request) { Map<String, Object> resultMap = Maps.newHashMap(); // 獲取公鑰 String publicKey = RSAUtils.getBase64PublicKey(); resultMap.put("publicKey", publicKey); // 生成隨機值 String rand = RandomStringUtils.randomAlphabetic(6); resultMap.put("rand", rand); // 將生成的隨機值存到session中,實際使用能夠存到第三方緩存中,並設置失效時間 request.getSession().setAttribute("rand", rand); return resultMap; } /** * 校驗 * */ @RequestMapping(value="/check", method=RequestMethod.POST) public String check(HttpServletRequest request) { // 取得密文 String password = request.getParameter("password"); // 解密 String plaintext = RSAUtils.decrypt(password); String[] arr = plaintext.split("\\|"); // 校驗隨機值 String rand = arr[1]; String randInSession = (String) request.getSession().getAttribute("rand"); //隨機值失效 request.getSession().removeAttribute("rand"); if(!rand.equals(randInSession)) { return "非法的請求"; } // 校驗密碼 String passwd = arr[0]; // 實際中根據用戶名從數據庫中查詢出密碼 String realPasswd = "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92"; if(!realPasswd.equals(passwd)) { return "密碼輸入錯誤"; } return "校驗經過"; } }
maven依賴的一些jar
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.54</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency>