SpringBoot秒殺系統實戰09-使用兩次MD5實現登錄功能

兩次MD5:

  1. 用戶端:PASS=MD5(明文+固定Salt)
  2. 服務端:PASS=MD5(用戶輸入處理+隨機Salt)

登錄界面:

數據庫裏面存的是做了兩次MD5的用戶密碼與其對應的salt值

現在我們登錄的時候,要去取得數據庫裏面對應用戶的密碼和salt值,然後後臺接收了前端做了一次MD5的密碼formPass,然後將這個formPass去和數據庫裏面的salt一起再做一次MD5,然後檢測是否與數據庫裏面存的那個密碼一致。

問題引用:

第二次MD5所用的隨機saltDB爲什麼要保存在數據庫裏?
當數據庫被侵入,做MD5之後的密碼和saltDB一起被盜的話,用戶密碼不就泄露了嗎?
但是如果不保存這個隨機的saltDB,下次用戶登錄的時候不就沒法和數據庫保存的密碼進行校驗了嗎?

問題思考:

實際上做MD5也不是絕對安全的,但是我們可以使得破解的難度指數級增長md5是不可逆的,不能反向解密的,網上所謂的“解密”都是把“加密”結果存儲到數據庫再比對的只能暴力破解,即有一個字典,從字典中讀取一條記錄,將密碼用加salt鹽值做MD5來對比數據庫裏面的值是否相等。

因爲好事者收集常用的密碼,然後對他們執行 MD5,然後做成一個數據量非常龐大的數據字典,然後對泄露的數據庫中的密碼進行對比,如果你的原始密碼很不幸的被包含在這個數據字典中,那麼花不了多長時間就能把你的原始密碼匹配出來,這個數據字典很容易收集,假設有600w個密碼。壞人們可以利用他們數據字典中的密碼,加上我們泄露數據庫中的 Salt,然後散列,然後再匹配。

但是由於我們的 Salt 是隨機產生的,每條數據都要加上 Salt 後再散列,假如我們的用戶數據表中有 30w 條數據,數據字典中有 600w 條數據,壞人們如果想要完全覆蓋的話,他們就必須加上 Salt 後再散列的數據字典數據量就應該是 300000* 6000000 = 1800000000000,所以說幹壞事的成本太高了吧。但是如果只是想破解某個用戶的密碼的話,只需爲這 600w 條數據加上 Salt,然後散列匹配。可見 Salt 雖然大大提高了安全係數,但也並非絕對安全。

實際項目中,Salt 不一定要加在最前面或最後面,也可以插在中間嘛,也可以分開插入,也可以倒序,程序設計時可以靈活調整,都可以使破解的難度指數級增長。

引入文章:https://www.xttblog.com/?p=986

步驟:

  • 創建秒殺User的domain類
public class MiaoshaUser {
	private Long id;
	private String nickname;
	private String pwd;
	private String salt;
	private String head;
	private Date registerDate;
	private Date lastLoginDate;
	private Integer loginCount;
	
	public String getPwd() {
		return pwd;
	}
	public void setPwd(String pwd) {
		this.pwd = pwd;
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getNickname() {
		return nickname;
	}
	public void setNickname(String nickname) {
		this.nickname = nickname;
	}
	
	public String getSalt() {
		return salt;
	}
	public void setSalt(String salt) {
		this.salt = salt;
	}
	public String getHead() {
		return head;
	}
	public void setHead(String head) {
		this.head = head;
	}
	public Date getRegisterDate() {
		return registerDate;
	}
	public void setRegisterDate(Date registerDate) {
		this.registerDate = registerDate;
	}
	public Date getLastLoginDate() {
		return lastLoginDate;
	}
	public void setLastLoginDate(Date lastLoginDate) {
		this.lastLoginDate = lastLoginDate;
	}
	public Integer getLoginCount() {
		return loginCount;
	}
	public void setLoginCount(Integer loginCount) {
		this.loginCount = loginCount;
	}
}
  • 新建MiaoshaUserDao
@Mapper
public interface MiaoshaUserDao {
@Select("select * from miaosha_user where id=#{id}")  //這裏#{id}通過後面參數來爲其賦值
public MiaoshaUser getById(@Param("id")long id);    //綁定

//綁定在對象上面了----@Param("id")long id,@Param("pwd")long pwd 效果一致
@Update("update miaosha_user set pwd=#{pwd} where id=#{id}")
public void update(MiaoshaUser toupdateuser);   
//public boolean update(@Param("id")long id);    //綁定   
}
  • 新建MiaoshaUserService
@Service
public class MiaoshaUserService {
public static final String COOKIE1_NAME_TOKEN="token";

@Autowired
MiaoshaUserDao miaoshaUserDao;
@Autowired
RedisService redisService;
/**
 * 根據id取得對象,先去緩存中取
 * @param id
 * @return
 */
public MiaoshaUser getById(long id) {
    //1.取緩存 ---先根據id來取得緩存
    MiaoshaUser user=redisService.get(MiaoshaUserKey.getById, ""+id, MiaoshaUser.class);
    //能再緩存中拿到
    if(user!=null) {
        return user;
    }
    //2.緩存中拿不到,那麼就去取數據庫
    user=miaoshaUserDao.getById(id);
    //3.設置緩存
    if(user!=null) {
        redisService.set(MiaoshaUserKey.getById, ""+id, user);
    }
    return user;
}
}
  • 新建LoginController
@RequestMapping("/login")
@Controller
public class LoginController{
@Autowired
UserService userService;
@Autowired
RedisService redisService;
@Autowired
MiaoshaUserService miaoshaUserService;  
//slf4j
private static Logger log=(Logger) LoggerFactory.getLogger(Logger.class);   
@RequestMapping("/to_login")
public String toLogin() {
    return "login";// 返回頁面login
}
@RequestMapping("/do_login") // 作爲異步操作
@ResponseBody
public CodeMsg doLogin(LoginVo loginVo) {// 0代表成功
    // log.info(loginVo.toString());
    if (loginVo == null) {
        return CodeMsg.SERVER_ERROR;
    }
    // 驗證
    String formPass = loginVo.getPassword();
    String mobile = loginVo.getMobile();
    // 驗證用戶
    MiaoshaUser user = miaoshaUserService.getById(Long.parseLong(mobile));
    if (user == null) {
        return CodeMsg.MOBILE_NOTEXIST;
    }
    // 驗證密碼
    String dbPass = user.getPwd();
    String dbSalt = user.getSalt();
    System.out.println("dbPass:" + dbPass + "   dbSalt:" + dbSalt);
    // 驗證密碼,計算二次MD5出來的pass是否與數據庫一致
    String tmppass = MD5Util.formPassToDBPass(formPass, dbSalt);
    System.out.println("formPass:" + formPass);
    System.out.println("tmppass:" + tmppass);
    if (!tmppass.equals(dbPass)) {
        return CodeMsg.PASSWORD_ERROR;
    }
    return CodeMsg.SUCCESS;
}
}
  • 前端login.html

引入bootstrap
引入相關的js和css

核心代碼:
(最下面有完整的代碼)

var pass=$("#password").val();
//pass='111111';
//固定salt
var salt='1a2b3c4d';
var str=""+salt.charAt(0)+salt.charAt(2)+pass+salt.charAt(5)+salt.charAt(4);
var password=md5(str);
//alert(salt);
//alert(pass);
//alert(password);
//與後臺Md5規則一致
//var str=""+salt.charAt(0)+salt.charAt(2)+formPass+salt.charAt(5)+salt.charAt(4);
$.ajax({
	url:"/login/do_login",
	type:"POST",
	data:{
		mobile:$("#phone").val(),
		password:password,
	},
	success:function(data){
		if(data.code==0){
			alert("success");
			//成功後跳轉
			window.location.href="/goods/to_list";
		}else{
			alert(data.msg);
		}
	},
	error:function(data){
		alert("error");
		//alert(data.msg);
	}
});

完整的前端login.html代碼:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html" charset="UTF-8">
    <title>登錄</title>
    <!-- jquery -->
    <script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
    <!-- bootstrap -->
    <link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}" />
    <script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script>
    <!-- jquery-validator -->
    <script type="text/javascript" th:src="@{/jquery-validation/jquery.validate.min.js}"></script>
    <script type="text/javascript" th:src="@{/jquery-validation/localization/messages_zh.min.js}"></script>
    <!-- layer -->
    <script type="text/javascript" th:src="@{/layer/layer.js}"></script>
    <!-- md5.js -->
    <script type="text/javascript" th:src="@{/js/md5.min.js}"></script>
    <!-- common.js -->
    <script type="text/javascript" th:src="@{/js/common.js}"></script>
    <script type= "text/javascript" th:src="@{/js/common.js}"> </script>

</head>
<body>
<form name="loginForm" id="loginForm" method="post"  style="width:50%; margin:0 auto">

    <h2 style="text-align:center; margin-bottom: 20px">用戶登錄</h2>

    <div class="form-group">
        <div class="row">
            <label class="form-label col-md-4">請輸入手機號碼</label>
            <div class="col-md-5">
                <input id="mobile" name = "mobile" class="form-control" type="text" placeholder="手機號碼" required="true"  minlength="11" maxlength="11" />
            </div>
            <div class="col-md-1">
            </div>
        </div>
    </div>

    <div class="form-group">
        <div class="row">
            <label class="form-label col-md-4">請輸入密碼</label>
            <div class="col-md-5">
                <input id="password" name="password" class="form-control" type="password"  placeholder="密碼" required="true" minlength="6" maxlength="16" />
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-5">
            <button class="btn btn-primary btn-block" type="reset" onclick="reset()">重置</button>
        </div>
        <div class="col-md-5">
            <button class="btn btn-primary btn-block" type="submit" onclick="login()">登錄</button>
        </div>
    </div>

</form>
</body>
<script>
    function login() {
        $("#loginForm").validate({
            submitHandler:function (form) {

                doLogin();

            }
        });
    }
    function doLogin(){

        g_showLoading();
        var inputPass = $("#password").val();
        var salt = g_passsword_salt;
        var str = "" + salt.charAt(0) + salt.charAt(2) + inputPass + salt.charAt(5) + salt.charAt(4);
        var password = md5(str);
        $.ajax({
           url:"/login/do_login",
           type:"POST",
           data:{
               mobile:$("#mobile").val(),
               password:password
           },
            success:function (data) {
               layer.closeAll();
               if (data.code == 0){
                   layer.msg("成功");
                   window.location.href="/goods/to_list";
               }else {
                   layer.msg(data.msg);
               }

            },
            error:function () {
                layer.closeAll();

            },
        });
    }
</script>

</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章