NutzCN Logo
精华 AccessTokenFilter中的accesstoken?
发布于 3012天前 作者 qq_a930202f 3198 次浏览 复制 上一个帖子 下一个帖子
标签: accesstoken mvc
public View match(ActionContext ac) {
		if (yvrService == null)
			yvrService = ac.getIoc().get(YvrService.class);
		HttpServletRequest req = ac.getRequest();
		String at = req.getParameter("accesstoken");<--------1
		if (req.getHeader("Api-Version") != null) {
			log.debug("api version = " + req.getHeader("Api-Version"));
			String loginname = req.getHeader("Api-Loginname");
			String nonce = req.getHeader("Api-Nonce");
			String key = req.getHeader("Api-Key");
			String time = req.getHeader("Api-Time");
			if (Strings.isBlank(loginname) || Strings.isBlank(nonce) || Strings.isBlank(key) || Strings.isBlank(time)) {
				return BaseModule.HTTP_403;
			}
			if (!yvrService.checkNonce(nonce, time)){
				return BaseModule.HTTP_403;
			}
			at = yvrService.accessToken(loginname);<--------2
			if (Strings.isBlank(at)) {
				return BaseModule.HTTP_403;
			}
			String tmp = Strings.join(",", at, loginname, nonce, time);
			String _key = Lang.sha1(tmp);
			log.debug("tmp="+tmp);
			log.debug("_key=" + _key);
			log.debug(" key=" + key);
			if (!_key.equals(key)) {
				return BaseModule.HTTP_403;
			}
			log.debug("api access token check ok");
		} 
		else if (Strings.isBlank(at)) { // TODO 移除这种兼容性,改成必须用nonce加密
			return BaseModule.HTTP_403;
		}
		int uid = yvrService.getUserByAccessToken(at);
		if (uid < 1) {
			return BaseModule.HTTP_403;
		}
		SecurityUtils.getSubject().getSession().setAttribute("me", uid);
		return null;
	}

在1处得到的accesstoken没有判断是否合法就赋值覆盖了?不是很懂这块的流程逻辑。

18 回复

事实上有2个版本,用Api-Version区分:

第一种,老版本, 单纯用AccessToken识别
第二种,新版本,带Api-Version识别, 不直接发送accesstokn, 而是通过几个header一起算签名

@wendal 目前代码库里是2种版本并存吗?
第二种方式,不用accesstoken的话,如何防止伪造?

第二种的核心是

// loginname是客户端发送过来的
// at是服务器从本地数据库,根据loginname查询得到的access token
// nonce是客户端发送上来的随机字符串
// time是客户端发送上来的时间字符串
String tmp = Strings.join(",", at, loginname, nonce, time);
String _key = Lang.sha1(tmp); // 集合上述参数,得出签名(_key);
// key是客户端按相同算法得到的签名
// 服务器端计算的签名(_key) 与 客户端计算的签名(key)对比一下,即可反证出access token是否一致
if (!_key.equals(key)) {
    return BaseModule.HTTP_403;
}

注意, ac(access token)是没有直接发送上来的!!!

第二种更安全,因为不直接传送token,

@wendal 如果只传 accesstoken就是第一种方式 那如何验证 是通过redis验证吗

int uid = yvrService.getUserByAccessToken(at);

@tonyyule 第一种,根据access token查uid,能查到就是合法,不能查到就是非法.

@wendal 第一种token更新了以后 redis 怎么做继续保留之前的token吗

@tonyyule token更新是服务器端的行为,更新token的时候把老token删掉

@wendal
3楼的回复,其中这句:// loginname是客户端发送过来的
的意思是,客户端扫码时,还需输入登录名才可以正常登录吗?

@wendal
其中这句:// loginname是客户端发送过来的
的意思是,客户端扫码时,还需输入登录名才可以正常登录吗?

@qq_a930202f 不需要

扫码之后, 会发起一个请求,验证access token是否合法, 如果合法, 则返回一个json字符串, 里面包含了loginname

@wendal 你说的意思是第二种老版本吧,就是上传roken,那第二种新版本是不是要发送login name

@qq_a930202f 不是

无论新老版本,获取token后马上会访问一个入口方法,那个入口方法是不受上述ActionFilter控制的,它只有一个参数,accesstoken,返回值里面带loginname

@wendal
----------------------------------------------
事实上有2个版本,用Api-Version区分:

第一种,老版本, 单纯用AccessToken识别
第二种,新版本,带Api-Version识别, 不直接发送accesstokn, 而是通过几个header一起算签名

------------------------------------------------

注意, ac(access token)是没有直接发送上来的!!!
-----------------------------------------------
你说的新版本的accesstokn在app客户端不是没发送吗,服务器端怎么会收到?(老版本发送access token的情况我理解)
有点晕,咱们说的是一回事吗。

第二种的核心是
// loginname是客户端发送过来的
// at是服务器从本地数据库,根据loginname查询得到的access token
// nonce是客户端发送上来的随机字符串

// time是客户端发送上来的时间字符串

按你说的这个,是不是客户端App需要发送 loginname,nonce,time,这个三个参数到服务器端,不需要发送access token

晕的原因是你没有全局去看

YvrApiModule里面有2种入口方法, 需要验证accesstoken和不需要验证accesstoken, 并非所有请求都会经过AccessTokenFilter. 这一点非常重要!!!

而, 不需要验证accesstoken的入口方法里面, 有一个特殊的方法, checkAccessToken, 该入口方法只有一个参数, accesstoken, 用于验证accesstoken的合法性. 注意,这个方法的accesstoken是直接发送过来的,不会经过AccessTokenFilter!! 如果是合法token,则返回loginname等信息

手机客户端->>网页: 扫描二维码,得到accesstoken
手机客户端->>服务器: 访问入口方法checkAccessToken
服务器->>客户端: 验证成功,返回loginname等信息的json文本

经过上述初始化请求之后, 手机客户端有 accesstoken 和 loginname了, 根据签名的公式,所有变量均有值

accesstoken : 扫码得到
loginname : 初始化请求后服务器告知
nonce : 随机字符串,即时生成
time : 系统时间

然后,就可以愉快地通信了

@wendal
明白了,多谢~
这么做的好处是不是只需要登陆的时候发送一次accesstoken,后面的通信都不需要在传输accesstoken。

@qq_a930202f 嗯嗯

添加回复
请先登陆
回到顶部