Conversational technology remember password 1, 2, 3

In the previous articles, we introduced cookies and other session-related concepts in detail. We know that cookies are a technique for storing session information on the client, and their appearance greatly simplifies the programmer's work. Cookie is a simple, convenient and practical client session technology, its importance is self-evident. Today we will look at several application scenarios of cookies.

Resource allocation diagram

Cookie translated into Chinese called cookies, small cookies, is still very appropriate, as shown above, each small cookie can have different patterns, indicating that the storage of small and important information.

1. Simple implementation of remembering password

Remember the password function everyone must have experienced, such as QQ, after entering the password once, we do not need to enter the password again for a long time. Let's see how it should be implemented.

Resource allocation diagram

This part of the work is mainly in the front end, the front end code is given below, first is Login.jsp.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="./js/jquery.cookie.js"></script>
<script src="./js/login.js"></script>

<title>Insert title here</title>
</head>
<body>
	<form id="loginForm" action="xxx" method="post">
		<input id="username" name="username" type="text"/>
		<br />
		<input id="password" name="password" type="password" />
		<br />
    	<!-- form提交前存储Cookie -->
		<button type="button" οnclick="loginSubmit()">提交</button>
	</form>
</body>
</html>

The download link of jquery.cookie.js is as follows: https://github.com/carhartl/jquery-cookie/tree/master/src

The code in login.js is as follows, which defines the loginSubmit method and page initialization action.

//页面DOM加载完毕后执行
//如果本地存储了账号、密码,则将其填充到输入框中
$(document).ready(function(){
	const cookieName = $.cookie('loginName');
	const cookiePwd = $.cookie('loginPwd');
	console.log(cookieName + "," + cookiePwd);
	if(cookieName != '' && cookieName != null){
		$("#username").val(cookieName);
	}
	if(cookiePwd != '' && cookiePwd != null){
		$("#password").val(cookiePwd);
	}
})

//登录
function loginSubmit(){
	console.log("执行了js中的方法");
	//将账号名和密码存入Cookie中
	const userName = $("#username").val();
	$.cookie('loginName', userName);
	const password = $("#password").val();
	$.cookie('loginPwd', password);
  //阻断form表单提交,测试时使用
  //return false;
	$("#loginForm").submit();
}

Here we will not give a screenshot of the test, the code has been given, and you can test it yourself. For the function of remembering the password, it is only the obvious function of automatically filling in the login. Here, because the cookie has been set, each request will automatically carry the account password information, so we can add an interceptor on the back end, even if When it expires, we can also create a new one after the password is verified, so that we don't need to log in again.

2. Use token to enhance security

In the above example, we stored the password in clear text on the browser side. Although the browser will encrypt the password and store it, in network transmission, the password is still transmitted in clear text, so the security Lower. In order to solve this problem, in general, when we store, we will not directly store the inscription password on the client, but store a token on the client through encryption technology.

Token can be understood as a token. Let's take the above remember password as an example. After the user logs in successfully, we can encrypt the password and other more private information to obtain an encrypted string and store it in the customer At the end, wait for the next time if necessary (such as Session expired), directly verify the validity of the token, rather than directly use the plain text account password to verify.

For the sake of safety and simplicity, we simply use MD5 for encryption (the symmetric encryption algorithm AES is recommended here). We also put the encryption work on the back end, after verifying that the username and password are correct.

Here we look at the code of the doGet method in LoginServlet:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
  request.setCharacterEncoding("UTF-8");
  response.setContentType("text/html;charset=utf-8");

  String userName = request.getParameter("username");
  String password = request.getParameter("password");
 
  UserService userService = UserService.getInstance();
  try {
    String result = userService.loginByNameAndPassword(userName, password);
    boolean isSuccess = false;
    // 登录成功
    if ("Success".equals(result)) {
      isSuccess = true;
      // 创建Session
      HttpSession session = request.getSession();
      // 生成一个token,当Session过期时,可以通过此token来验证是否登录过
      // 规则,userName-password 的md5值
      String token = MD5Util.encodeByMD5(userName + "-" + password);
      Cookie loginCookie = new Cookie("loginToken", token);
      Cookie nameCookie = new Cookie("userName", userName);
      // 将Cookie添加到response中
      response.addCookie(loginCookie);
      response.addCookie(nameCookie);
    } else {
      // 登录失败
      System.out.println("登录失败的原因为:" + result);
      // 跳转登录失败页面
      // response.sendRedirect("/FirstProject/index.jsp");
    }

    // 输出页面
    // ...
}

Wherein embodied portion may be omitted and Service reference above . In the above example, we concatenate the username and password and encrypt it. Because MD5 encryption is not decryptable, we also create a cookie to save the username. The next time you need to verify the token information, you only need to query the password in the database based on the loginName in the cookie, calculate the MD5 encrypted value after splicing, and compare it with the token. The simple sample code is as follows:

/**
	 * 根据登录名校验token是否正确
	 * 
	 * @param loginName
	 * @param loginToekn
	 * @return true || false
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 */
public boolean checkToken(String loginName, String loginToekn) throws ClassNotFoundException, SQLException {
  UserDao userDao = new UserDao();
  User user = userDao.findUserByName(loginName);
  String password = user.getPassword();
  String newToken = MD5Util.encodeByMD5(loginName + "-" + password);
  if (newToken.equals(loginToekn)) {
    System.out.println("token检验正确");
    return true;
  }
  System.out.println("token检验错误");
  return false;
}

The code of MD5Util is as follows:

package xxx;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Util {

  /**
	 * 对传入的字符串进行MD5加密
	 * 
	 * @param str
	 * @return 加密后的字符串
	 */
  public static String encodeByMD5(String str) {
    byte[] bytes = null;
    try {
      MessageDigest md5 = MessageDigest.getInstance("MD5");
      bytes = md5.digest(str.getBytes("utf-8"));
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
    return bytesTo16BString(bytes);
  }

  /**
	 * 将字节数组转换为16进制格式的字符串
	 * 
	 * @param bytes
	 * @return
	 */
  private static String bytesTo16BString(byte[] bytes) {
    StringBuffer res = new StringBuffer();
    int num = 0;
    for (int i = 0; i < bytes.length; i++) {
      num = bytes[i] > 0 ? bytes[i] : 255 + bytes[i];
      String hex = Integer.toHexString(num);
      res.append(hex.length() < 2 ? 0 + hex : hex);
    }
    return res.toString();
  }

}

3. Learn from JWT's ideas

The JWT (JSON Web Token) technology is an open industry standard RFC 7519 method, is a compact, URL safe method for indicating in a statement to be transferred between two parties. The declaration in JWT is encoded as a JSON object, used as the payload of the JWS (JSON Web Signature) structure or the plain text of the JWE (JSON Web Encryption) structure, so that the declaration can be digitally signed or integrity protected with a message verification code (MAC) and / or encrypted messages.

In a word, JWT is to put sensitive information into a JSON object, add header, signature and other information, and then encrypt the entire object into a string and store it on the client. This article also refers to the idea of ​​JWT, simple manual implementation.

First, let's simply define an object to store information (you can also directly create a JSONObject), the code is as follows:

public class JwtDataPojo {

	//登录用户
	private String name;
	
	// 其他信息...
	
	//登录时间
	private Date loginDate;

	// Getter、Setter方法
	// ...
}

We modified the code for successful login in LoginServlet as follows:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
  //...
 
  UserService userService = UserService.getInstance();
  try {
    String result = userService.loginByNameAndPassword(userName, password);
    boolean isSuccess = false;
    // 登录成功
    if ("Success".equals(result)) {
      isSuccess = true;
      JwtDataPojo dataPojo = new JwtDataPojo();
      dataPojo.setName(userName);
      dataPojo.setLoginDate(new Date());
      // 生成加密后的字符串
      String jwtToken = AESUtil.encrypt(JSON.toJSONString(dataPojo), "123456");
      Cookie cookie = new Cookie("JwtToken", jwtToken);
      // 将Cookie添加到response中
      response.addCookie(cookie);
    } else {
      // 登录失败
      System.out.println("登录失败的原因为:" + result);
      // 跳转登录失败页面
      // response.sendRedirect("/FirstProject/index.jsp");
    }

    // 输出页面
    // ...
}

Because there is a login time in JwtDataPojo, we can specifically verify whether it times out on the server side. The resolveJwtToken method in the above code is as follows:

/**
	 * 根据传入的字符换和密码,解析为JwtToken对象
	 * 
	 * @param content
	 * @param key
	 * @return JwtDataPojo
	 * @throws Exception
	 */
public JwtDataPojo resolveJwtToken(String content, String key) throws Exception {
  String jsonString = AESUtil.decrypt(content, key);
  if(StringUtils.isNullOrEmpty(jsonString)) {
    throw new Exception("token错误");
  }
  JwtDataPojo dataPojo = JSON.parseObject(jsonString, JwtDataPojo.class);
  // 超过三十分钟过期
  if (System.currentTimeMillis() - dataPojo.getLoginDate().getTime() > 1000 * 60 * 30) {
    throw new Exception("登录过期");
  }
  return dataPojo;
}

For the above timeout, we can modify it ourselves, and we can adopt the strategy of maximum idle interval of Session, and update the LoginDate after verifying that JwtToken passes in each interceptor.

The code of AESUtil can refer to this article: java Use AES to encrypt and decrypt data

Fastjson used in the above, the download address of the jar package .

4. Summary

In the previous article, we explained the maintenance of the login status. In this article, we modify the validity period of the cookie (or the aging of JwtToken) to achieve the same effect. Moreover, after we encrypt through the server, the security Can also be guaranteed.

For JWT, the knowledge in this article draws on a little thought. The function of the completed JWT will be stronger. If you are interested, you can learn by yourself. I will write an article about JWT separately later.

Reference reading:

  1. Detailed explanation of cookies in conversation technology

  2. Session technology

  3. Conservation of the login state of the conversation technology

  4. JWT official website introduction

  5. What is JWT – JSON WEB TOKEN


Below the dividing line, this article is over. The content of this article is all sorted out by the blogger and summarized in accordance with his own understanding. If there are any errors, please criticize and correct.

This column of Java web will be a series of blogs. If you like it, you can continue to follow it. If this article is helpful to you, please also like, comment and follow.

If you have any questions, you can leave a comment in the comment area.

Published 56 original articles · Like 973 · Visit 130,000+

Guess you like

Origin blog.csdn.net/qq_34666857/article/details/105639767