牛骨文教育服务平台(让学习变的简单)
博文笔记

Java Web 登录采用非对称加密(RSA算法)+单例模式+redis+登录锁定5次

创建时间:2018-01-26 投稿人: 浏览次数:752

通过单例生成公钥和私钥(单例生成的好处除了优化程序外,也可以防止多IP登录造成干扰),前台只接受明文,由redis保存,每次登录失败、退出等只要是跳转到登录页面的都要将明文传过来。

在这里封装成一个方法:getRsaKey()

	public RSAPrivateKey getRsaKey() throws NoSuchAlgorithmException{
		//判断redis中有就不再生成,没有就再生成一次
		RSAPrivateKey key = RedisCluster.use().get("privateKey");
		
		if(key==null){
			HashMap<String, Object> map = RSAUtils.getKeys(); 
			//生成公钥和私钥    
			  RSAPublicKey publicKey = (RSAPublicKey) map.get("public");  
			  RSAPrivateKey privateKey = (RSAPrivateKey) map.get("private");  
			  
			       //私钥保存在session中,用于解密  
				  //公钥信息保存在页面,用于加密  
				  String publicKeyExponent = publicKey.getPublicExponent().toString(16);  
				  String publicKeyModulus = publicKey.getModulus().toString(16); 
				  RedisCluster.use().set("privateKey", privateKey);
				  RedisCluster.use().set("publicKeyExponent", publicKeyExponent);
				  RedisCluster.use().set("publicKeyModulus", publicKeyModulus);
				 
		}
		return null;
	}
在加载登录页面前执行该方法:

public void index() throws NoSuchAlgorithmException {
		
		this.getRsaKey();
		
		JSession jsession = getJSession();
		logger.info("jsession=" + jsession);
		logger.info("USER_FLAG=" + jsession.getAttribute(BaseConst.USER_FLAG));
		if (jsession == null
				|| jsession.getAttribute(BaseConst.USER_FLAG) == null) {
		
			  setAttr("publicKeyExponent", RedisCluster.use().get("publicKeyExponent"));
			  setAttr("publicKeyModulus", RedisCluster.use().get("publicKeyModulus"));
			
			render("login.jsp");
		} else {
			
			render("index.jsp");
		}
	}
登录页面,RSA对密码解密,md5加密(后台存密文),登录5次会锁定,每次登录错误提示剩余次数(解锁走定时任务),只要是跳转到登录页面的都要将明文传过来,逻辑可看注释:

/**
	 * 登录
	 * @throws Exception 
	 */
	@Unauth
	@Before({FormValidator.class,LoginValidator.class})
	public void login() throws Exception {
		
		
	
		
		
    
		LoginInfo loginInfo = new LoginInfo();
		Des desObj = new Des();
		loginInfo.set("uuid", ToolKit.getUuidByJdk(true));
		
		loginInfo.set("logn_time", DateKit.getCurrentDate("yyyy-MM-dd HH:mm:ss"));
		loginInfo.set("logn_ip", WebKit.getIpAddr(this.getRequest()));
		String loginname = getPara("loginname");
		loginInfo.set("user_id",loginname); 
		String desPasswd = getPara("despasswd");
		System.out.println(desPasswd);
		String logPsw =null;
		
		//非对称解密
		JSession jsession = getJSession();
		 
		
		RSAPrivateKey rawPasswd = (RSAPrivateKey)RedisCluster.use().get("privateKey");  
        if(rawPasswd!=null){  
            long time1 = System.currentTimeMillis();  
            logPsw = RSAUtils.decryptByPrivateKey(desPasswd, rawPasswd);  
        }  
        //md5加盐加密
        String loginPsw = PasswdUtils.crypt(logPsw);
		
		logger.info("user login, loginname=" + loginname + ",passwd="
				+ loginPsw);
		//每次登录时,已经锁定直接禁止登录
		Record isLocked = UserService.me.isLocked(loginname);
		if (isLocked != null) {
			setAttr("validateAuthCodeFailed", "密码输入错误已超过上限,请于24小时之后重新登录");
			  setAttr("publicKeyExponent",RedisCluster.use().get("publicKeyExponent"));
			  setAttr("publicKeyModulus",RedisCluster.use().get("publicKeyModulus"));
			render("login.jsp");
			return;
		}
		 
		
		//没有锁定就判定,如果失败就把失败次数+1,如果成功就重新置0
		//失败次数+1后判断失败次数是否是5,是5的话将锁定标志置为真,并设上时间。
		User huser = User.me.findFirst("SELECT * FROM hp_user where  username = ?", loginname);
		if (huser == null) {
			setAttr("validateAuthCodeFailed", "用户名或密码错误");
			setAttr("publicKeyExponent",RedisCluster.use().get("publicKeyExponent"));
			setAttr("publicKeyModulus",RedisCluster.use().get("publicKeyModulus"));
			render("login.jsp");
			return;
		}
		
		Record user = UserService.me.login(loginname, loginPsw);
		if (user == null) {
			logger.info("user login error, 用户名或密码错误, loginname=" + loginname
					+ ",passwd=" + loginPsw);
			loginInfo.set("login_status", "1");
			loginInfo.set("login_msg", "用户名或密码错误");
			loginInfo.save();
			
			huser.set("failure_time", String.valueOf(Integer.parseInt((String) huser.get("failure_time"))+1) );
			setAttr("validateAuthCodeFailed", "用户名或密码错误,你还有"+Integer.toString(5-Integer.parseInt((String) huser.get("failure_time")))+"次尝试机会");
			
			if("5".equals(huser.get("failure_time"))){
				
				   Date date=new Date();//取时间
				   Calendar calendar = new GregorianCalendar();
				   calendar.setTime(date);
				   calendar.add(calendar.DATE,1);//把日期往后增加一天.整数往后推,负数往前移动
				   date=calendar.getTime(); //这个时间就是日期往后推一天的结果 
				   SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				   String lock_time = formatter.format(date);
				 
				 
				 
				huser.set("lock_sign", "1");
				huser.set("lock_time", lock_time);
				setAttr("validateAuthCodeFailed", "密码输入错误已超过上限,请于24小时之后重新登录");
			}
			
			huser.update();
			setAttr("publicKeyExponent",RedisCluster.use().get("publicKeyExponent"));
			  setAttr("publicKeyModulus",RedisCluster.use().get("publicKeyModulus"));
			render("login.jsp");
			return;
		}


前台js:为方便对提交数据进行修改,将form直接提交方法改成 onsubmit="return loadNow()"

<form id="formsmill" name="formsmill"  action="${ctx}/application/login"  method="post" enctype="multipart/form-data"  onsubmit="return loadNow()">
                     <input type="hidden" id="publicKeyExponent"  value="${publicKeyExponent}" />
				     <input type="hidden" id="publicKeyModulus"  value="${publicKeyModulus}" />
                 </form>




  function loadNow(){ 
    	debugger
    	 
    	//待加密字符串
			var orgPwd = $("#passwd").val();
			var publicKeyExponent = $("#publicKeyExponent").val();
			var publicKeyModulus = $("#publicKeyModulus").val();
			RSAUtils.setMaxDigits(200);  
			var key = new RSAUtils.getKeyPair(publicKeyExponent, "", publicKeyModulus);  
			var encrypedPwd = RSAUtils.encryptedString(key,orgPwd.split("").reverse().join(""));  
			$("#despasswd").val(encrypedPwd);  
			$("#passwd").val(encrypedPwd);  
    	 
    	var params = $("#formsmill").serialize();
    	$("#formsmill").attr("action","${ctx}/application/login?"+params);
    	
    }
对于RSA加密逻辑,可以看上篇文章


声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。