Web前端
- 每次发送AJAX web请求(包括POST,GET)前,都要产生一个随机token。
- 将token以'HALO_TOKEN'标签名,放入每次AJAX请求的Header中,作为http报文一部分发送至服务端。
- 对服务端响应的特殊错误码(0008)做相应CSRF风险提示。
token创建
-
读取SEED值。SEED可以用sessionId,也可以用固定的值。SEED建议24位,不够用0后补位。
-
每次发送服务端请求前,基于SEED和当前客户端系统时间毫秒数,产生一段随机可逆的明文。
- 随机选取SEED文本长度内的5个数字。
- 将每个数字对应SEED中的字符内容连同数字组成一个键值对。键值对用':'分隔。
- 将所有键值对用','串联起来。形成长串文本。
- 获取当前客户端系统时间毫秒数,用'_'将其添加到文本末尾,最终形成明文。
-
用3DES算法对步骤2中产生的明文,以SEED为密钥加密,形成密文。
-
用Base64对密文做一次编码操作,形成token。
SEED获取与设置
前端应用开发人员可以通过两种方式获取一个有效的SEED:
sessionID作为SEED
- 用户登录成功后,前端程序通过访问"/web/sessionid",从响应报文的data字段中获取当前用户sessionId。
- 注意:将sessionId放入浏览器的localStorage中。
localStorage.setItem("_HALOSESSIONID", sessionId);
固定SEED设置
- 用户首次访问网站后,由JS将与服务端开发人员约定好的固定SEED值放入localStorage中。
注意:固定SEED值需要与服务器端配合。两方的配置内容要统一。
服务端
- 拦截所有SpringMVC的Request请求。
- 将token值从header的'HALO_TOKEN'中获取到,如果获取不到则直接抛出(0008)错误。
- 对token内容进行解密验证。
- 根据解密验证的情况,执行对应的业务逻辑或直接返回(0008)错误。
token解密与验证
- 获取当前请求的SEED。
- 对token做Base64做解码操作,还原密文。
- 采用3DES算法,以SEED为密钥对密文进行解密。
- 对解密后的token内容进行验证
- 对解密后内容的以'_'为分割,对前半段的Hash内容进行验证。要求必须能够与SEED的字符分布情况一致。
- 对'_'分割的后半段内容,与服务器当前时间毫秒数做差值,要求毫秒数差值不能大于N*60000。N代表分钟数。
SEED读取策略
后端框架会分别尝试两种方式获取SEED值。第一种不成功,就会尝试第二种获取方式,如果两种都无法获取有效SEED,则报错。
- 先尝试访问配置项web.csrf.token.seed,如果读取到非空值,则将其作为SEED。
- 如果第一种方式失败,则直接获取当前请求对应的sessionId,将其作为SEED。