Second kill mall project -- twice MD5 encryption + Redis cache + Token to realize distributed Session login

I Twice MD5 encryption

①. Purpose of MD5 encryption

Purpose of MD5 encryption

  • If nothing is done, the plaintext password will be transmitted on the network. If the data is obtained by a malicious user in the transmission process, the password can be obtained, so it is not safe

②. The purpose of MD5 encryption twice

The purpose of MD5 encryption twice

  • The purpose of the second encryption is to prevent the database from being invaded and the password is found out by people through the rainbow table. Therefore, after the server receives the password, it does not write it directly to the database, but generates a random salt, and then stores it in the database after MD5 encryption

③. Realize MD5 twice encryption function

package com.xizi.miaosha.util;

import org.apache.commons.codec.digest.DigestUtils;

/**
 * @author xizizzz
 * @description:  Twice md5 encryption algorithm
 * @date 2021-6-23 07:48 PM
 */
public class EncyptionUtil {

    //Import md5 encryption algorithm into commons codec package
    public static String md5(String str) {
        return DigestUtils.md5Hex(str);
    }

    //md5 encryption + salt
    public static String md5WithSalt(String pwd, String salt) {
        //Take the characters at the position of string 0 2 5 4 and splice them into a new string
        String str = "" + salt.charAt(0) + salt.charAt(2) + pwd + salt.charAt(5) + salt.charAt(4);
        return md5(str);
    }


    public static void main(String[] args) {
        System.out.println(md5WithSalt("123456", "1a2b3c4d"));
        System.out.println(md5WithSalt(md5WithSalt("123456", "1a2b3c4d"), "1a2b3c4d"));
    }
}

II Implementation of distributed Session

①. Problems arising

Problems arising

  • The second kill service is actually distributed multiple servers. At this time, if the user logs in to the first server, the first request goes to the first server, but the second request goes to the second server, the user's session information will be lost.

②. Solution

Solution

  • Implementation idea: after the user logs in successfully, generate a sessionid for the user (identify the user with a token), write it to the cookie and pass it to the client; Then, in subsequent access, the client passes the token in the cookie. After the server obtains the token, it obtains the corresponding session information according to the token (the token is generated by uuid)

③. Threadlocal stores the User object

// Threadlocal storage user will not affect the object. Each time a request comes in, it will generate its own thread variable to store
public class UserContext {

	//ThreadLocal is used to access user information. One thread saves one user information
	private static ThreadLocal<User> userHolder = new ThreadLocal<User>();
	//obtain
	public static void setUser(User user) {
		userHolder.set(user);
	}
	//set up
	public static User getUser() {
		return userHolder.get();
	}

}

④. The login function creates a token and stores it in Redis and cookies

Business code of login function

   // Login implementation
    public ResultEnum login(LoginVO loginVO, HttpServletResponse response) {
        log.info("[[login authentication] user information:{}", loginVO.toString());
        if (loginVO == null) {
            // Throw parameter error
            throw new CustomException(ResultEnum.PARAM_ERROR);
        }
        String mobile = loginVO.getMobile();
        //TKMybatis internal method implementation direct call
        User user = userMapper.selectByPrimaryKey(mobile);
        if (user == null) {
            // Thrown user does not exist
            throw new CustomException(ResultEnum.USER_NOT_EXIST);
        }
        //Get salt value
        String salt = user.getSalt();
        //Call the tool class to md5 encrypt twice for password comparison
        String loginPwd = EncyptionUtil.md5WithSalt(loginVO.getPassword(), salt);
        if (!loginPwd.equals(user.getPassword())) {
            //Throw password error
            throw new CustomException(ResultEnum.PASSWORD_ERROR);
        }
        //Set user to ThreadLocalh
        UserContext.setUser(user);
        // Login is successful. Call UUID tool class to generate token
        String token = UUIDUtil.getUUID();
        //Store the token in the cookie, and store the user information and token in redis
        addUpdateSession(response, token, user);
        //Return login success enumeration class properties
        return ResultEnum.LOGIN_SUCCESS;
    }

The session is added for the first visit, and the session is updated for subsequent visits

    // The session is added for the first access, and the session is updated for subsequent access (updating is to regenerate the session to maintain the lifetime of the session)
    private void addUpdateSession(HttpServletResponse response, String token, User user) {
        //Create a cookie object
        Cookie cookie = new Cookie(CookieProperties.COOKIE_NAME, token);
        //Set the expiration time of the cookie
        cookie.setMaxAge(UserPrefix.getByCookie.getExpireSeconds());
        cookie.setPath("/");
        response.addCookie(cookie);
        // Store the token and user information in redis
        redisService.set(UserPrefix.getByCookie, token, user);
    }

⑤. Configure the automatic binding of User information to the Controller layer interface every time a request is made

Its function is to parse the User parameter of request request and automatically bind the data to the input parameter of Controller

package com.xizi.miaosha.config;


/**
 * @author xizizzz
 * @description: Its function is to parse the request parameters and bind the data to the input parameters of the Controller
 * @date 2021-6-23 07:48 PM
 */

// To customize a parameter parser, you need to implement the HandlerMethodArgumentResolver interface,
// Override the supportsParameter and resolveArgument methods, and add the resolver configuration to the configuration file.
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {

    @Autowired
    private UserService userService;

    //Returns the User object type
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        Class<?> clazz = methodParameter.getParameterType();
        return (clazz == User.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        //Returns the user stored in ThreadLocal
        return UserContext.getUser();
    }

}

⑤. Add UserArgumentResolver user parameter resolver in custom configuration

⑤. The access interface tests the token effect and user information binding

Added by gb75 on Mon, 24 Jan 2022 09:18:39 +0200