小程序后端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则鉴权通过。

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