小程序後端token生成機制

小程序後端token生成機制以及與前端交互處理

一、token的生成

/**
* 校驗和創建token
 * @param userId
 * @param sign
 * @param timestamp
 * @return
 */
public TokenRespVO checkoutAndCreateToken(String token,String userId,
										  String sign,String timestamp) {
	//step1:判斷之前是否獲取過token,如果沒有獲取,生成token。如果獲取了,判斷token的剩餘時間是否小於1分鐘,如果大於則返回之前的token
	long ttl = redisDao.ttl(RedisKeyPrefix.TOKEN + token);
	if (ttl > Constants.REPEATED_REQUEST_TIME) {
		TokenRespVO resp = setNotExpireToken(token, ttl);
		return resp;
	}
	//step2:判斷用戶是否存在,不存在不讓獲取token
	User user = commonUserService.checkVaildUser(userId);
	//step3:判斷用戶的登錄有效時間是否過期
	long sessionTimeOut = user.getSessionTimeOut().toInstant(ZoneOffset.of("+8")).toEpochMilli();
	long currentTimeMillis = System.currentTimeMillis();
	if (sessionTimeOut <= currentTimeMillis) {
		throw new ServiceException(ExceptionCode.ACCOUNT_EXPIRE, "賬號已過期,請重新登錄!");
	}
	//step4:判斷簽名是否有效
	String digest = null;
	try {
		digest = Md5Tool.getMD5Code(userId + timestamp + user.getOpenId()).toUpperCase();
	} catch (Exception e) {
		logger.error("checkoutAndCreateToken md5 fail:{}",e);
	}
	if (!sign.equals(digest)) {
		throw new ServiceException(ExceptionCode.SIGN_INVAILD, "簽名無效");
	}
	//step5:生成token和token的過期時間
	TokenRespVO newToken = createToken(userId, user);
	return newToken;
}

/**
* 返回沒有過期的token
 * @param token
 * @param ttl
 * @return
 */
private TokenRespVO setNotExpireToken(String token, Long ttl) {
	TokenRespVO resp = new TokenRespVO();
	resp.setTokenValue(token);
	long currentTime = System.currentTimeMillis();
	long expireTime = currentTime + ttl * 1000;
	resp.setExpireTime(expireTime);
	return resp;
}

/**
* 創建token
 * @param userId
 * @param user
 * @return
 */
public TokenRespVO createToken(String userId, User user) {
	//step5.1:判斷數據庫中保存的token是否過期
	String token = user.getToken();
	if (token != null) {
		long ttl = redisDao.ttl(RedisKeyPrefix.TOKEN + token);
		if (ttl > Constants.REPEATED_REQUEST_TIME) {
			TokenRespVO resp = setNotExpireToken(token, ttl);
			return resp;
		}
	}
	//step5.2:生成token
	TokenRespVO resp = new TokenRespVO();
	String token = RandomUtil.generateLowerString(16);
	redisDao.setValEx(RedisKeyPrefix.TOKEN + token, userId, Constants.TOKEN_EXPIRE_TIME);
	resp.setTokenValue(token);
	long currentTime = System.currentTimeMillis();
	long expireTime = currentTime + Constants.TOKEN_EXPIRE_TIME * 1000;
	resp.setExpireTime(expireTime);
	//step5.3:更新數據庫中的token
	user.setToken(token);
	user.setUpdateTime(LocalDateTime.now());
	userDao.updateUser(user);
	return resp;
}

/**
 * 返回token信息的VO
 * @author mike_z
 *
 */
public class TokenRespVO {
	
	/**
	 * token的值
	 */
	private String tokenValue;
	
	/**
	 * token過期時間,單位爲毫秒
	 */
	private long expireTime;

	public String getTokenValue() {
		return tokenValue;
	}

	public void setTokenValue(String tokenValue) {
		this.tokenValue = tokenValue;
	}

	public long getExpireTime() {
		return expireTime;
	}

	public void setExpireTime(long expireTime) {
		this.expireTime = expireTime;
	}

	@Override
	public String toString() {
		return "TokenRespVO [tokenValue=" + tokenValue + ", expireTime=" + expireTime + "]";
	}
}

二、token的攔截

/**
 * 認證的攔截器
 *
 */
@Component
public class AuthInterceptor implements HandlerInterceptor{
	
	@Autowired
	private RedisDao redisDao;
	
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		String token = request.getHeader(Constants.REQUEST_HEADER);
		if (StringUtils.isNoneBlank(wmkjToken)) {
			String userId = redisDao.getVal(RedisKeyPrefix.TOKEN + token);
			if (userId != null) {
				request.setAttribute("userId", userId);
				return true;
			}
		}
		//響應Json數據
		responseJson(response);
		return false;
	}

	/**
	 * 響應Json數據
	 * @param response
	 * @throws IOException
	 */
	private void responseJson(HttpServletResponse response) throws IOException {
		response.setCharacterEncoding("UTF-8");  
		response.setContentType("application/json; charset=utf-8");
		PrintWriter out = response.getWriter();
		out.append(JsonUtil.toJSon(ResData.fail(ExceptionCode.INVALID_TOKEN, "無效的token")));
	}
	
	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
	}
}

三、token在前端和後端的使用

前端每次請求的時候把token放在請求頭中,後端通過攔截獲取請求頭裏的token,然後查詢redis,獲取token對應的值,如果爲null,則token已經過期了,返回token過期的code給到前端。前端通過判斷返回值的code爲token過期,則重新調獲取token的接口,將返回的token寫入緩存,然後刷新當前頁面。後端拿到token則鑑權通過。

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