下面是本人的一点对安全登陆的体会,如有错误,还请大佬斧正。
token的特点有两个 1.唯一性,可以标志一个用户。2.也可以按照一定规则生成,本身可携带一些有效信息。
为何不能用用户id代替token呢?因为同一用户每次登陆,生成的token可能不一致。
我们项目的token是用uuid生成的,将用户id作为key,token作为value,放入redis中。
通过redis管理,可以设置token的有效时间。
securityKey = UUIDUtils.genUUID();
redis.set(key,securityKey);
redis.expire(key, Integer.parseInt(securityKeyExpireTime));
后台通过aop 拦截每次请求,请求的路径设置为open与login,如果是open开头则直接放过,如果是login则需要token校验。
public void before(HttpServletRequest request, JSONObject jsonObject) throws LoginValidateException {
logger.info("[#requestUrl#]:{}\t[#requestJson#]:{}", request.getRequestURI(), StringUtil.formatJson(jsonObject.toString()));
String requestUri = request.getRequestURI();
String contextPath = request.getContextPath();
String url = requestUri.substring(contextPath.length());
if (url.startsWith("/open")) { //如果是不拦截的地址直接放行
return;
}
}
String securityKey = RedisUtil.getSecurityKey((String) jsonObject.get(BaseQUERYHead.MERCHANT_ID), (String) jsonObject.get(BaseQUERYHead.USER_ID));
因为login请求都会携带用户id,通过用户id在redis中查找token,如果查找不到或token不一致,则直接返回错误信息,不进入方法。token合法则重新刷新token的有效时间。
if (securityKey != null && securityKey.equals(jsonObject.get(BaseQUERYHead.SECURITY_KEY))) {
RedisUtil.flushSecurityKey((String) jsonObject.get(BaseQUERYHead.MERCHANT_ID), (String) jsonObject.get(BaseQUERYHead.USER_ID));
} else {
logger.warn("securityKey校验失败,userId={},requestId={}", jsonObject.get(BaseQUERYHead.USER_ID), jsonObject.get(BaseQUERYHead.REQUEST_ID));
baseDTO = BaseDTO.genErrBaseDTO(MemberError.E99_SECURITY_ERROR);
if (baseDTO.getRequestId() == null) {
baseDTO.setRequestId(String.valueOf(new Date().getTime()));
}
throw new LoginValidateException(JSONObject.fromObject(baseDTO).toString());
}