admin管理员组文章数量:1566353
使用场景
现在很多基于 restful 的 api 接口都有个登录的设计,也就是在发起正式的请求之前先通过一个登录的请求接口,申请一个叫做 token 的东西。申请成功后,后面其他的支付请求都要带上这个token,服务端通过这个 token 验证请求的合法性。这个 token 通常都有一个有效期,一般就是几个小时。
比如我之前接入过一个支付宝和微信支付的通道,他们提供的 api 就要求先登录获取 token然后才能使用支付的 api 接口。
在比如微信的公众平台接口,关键的接口在使用之前都要带access token。access_token 是公众号的全局唯一票据,有效期为7200秒,重复获取将导致上次获取的 access_token 失效。
接口调用请求说明
http请求方式: GET
https://api.weixin.qq/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
参数说明
参数 是否必须 说明
grant_type 是 获取access_token填写client_credential
appid 是 第三方用户唯一凭证
secret 是 第三方用户唯一凭证密钥,既appsecret
返回说明
正常情况下,微信会返回下述 JSON 数据包给公众号:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
参数 说明
access_token 获取到的凭证
expires_in 凭证有效时间,单位:秒
错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
{"errcode":40013,"errmsg":"invalid appid"}
什么是JWT(json web token)
首先说它是一种规范。目的是在客户端和服务端之间定义一种鉴权行为从而保证数据传递的安全性。
JWT 标准的 Token 有三个部分:
- header
- payload
- signature
中间用点分隔开,并且都会使用 Base64 编码,所以真正的 Token 看起来像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
关于三个部分具体都是些什么东西,大家自行搜索即可,不是我这篇文章的重点。
我们说 restful API 中使用的 token 鉴权机制,大部分都是遵循 JWT 规范的,也就是底层都是对 JWT 的具体实现。
一个java实现实例
说了这么多,该上实例了。这个实例会用到redis,spring等技术。
这个实例原出处:
RESTful登录设计(基于Spring及Redis的Token鉴权)
代码很简单不过多解释了。我这里只说下如何运行看看测试效果。
根据本地实际情况修改mysql和redis配置
这个工程其实是个基于spring boot的项目,Spring boot 的默认配置文件是 resources 下的 application.properties。所以我们主要是修改它。
spring.datasource.username就是表示数据库用户名,你不能随便改,框架里就用它作为查抄依据。
同理spring.redis.*也是redis相关的配置。
上面几个地方要根据你本地实际情况修改。修改完之后,在你的mysql中新建一个名为demo的数据库(如果原来没有的话)。然后执行工程中init.sql中的语句,这是为了初始化表。
运行
首先执行右键项目目录,run as -> maven install,会看到类似下面的输出:
然后再执行 run as -> java application, 然后选择程序的入口:
同样看下console有没有错误,如果报错通常是上面的配置不对,仔细检查下。
打开浏览器,输入http://localhost:8080,显示如下:
表明运行成功。
测试下登录,
登录成功,并且也成功的创建了token。
退出登录,在 authorization 中填写用 userId 和 token 以"_"拼接得到的字符串。
有人会问为什么authorization需要这样的格式才能退出登录成功。下面的代码可以说明问题,
...
@RequestMapping(method = RequestMethod.DELETE)
@Authorization
public ResponseEntity logout(@CurrentUser User user) {
tokenManager.deleteToken(user.getId());
return new ResponseEntity<>(ResultModel.ok(), HttpStatus.OK);
}
@Authorization用于表示该操作需要登录后才能进行,否则会返回401错误。如下:
...
//验证token
TokenModel model = manager.getToken(authorization);
if (manager.checkToken(model)) {
//如果token验证成功,将token对应的用户id存在request中,便于之后注入
request.setAttribute(Constants.CURRENT_USER_ID, model.getUserId());
return true;
}
//如果验证token失败,并且方法注明了Authorization,返回401错误
if (method.getAnnotation(Authorization.class) != null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
为了验证redis的缓存有效期,我把代码做了一点修改,过期时间设置成1分钟便于测试。
/**
* token有效期(分钟)
*/
public static final int TOKEN_EXPIRES_MINS = 1;
public TokenModel createToken(long userId) {
//使用uuid作为源token
String token = UUID.randomUUID().toString().replace("-", "");
TokenModel model = new TokenModel(userId, token);
//存储到redis并设置过期时间
// redis.boundValueOps(userId).set(token, Constants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS);
redis.boundValueOps(userId).set(token, Constants.TOKEN_EXPIRES_MINS, TimeUnit.MINUTES);
return model;
}
然后我先登录,等超过一分钟再退出登录,确认会返回401错误。
欢迎大家关注我的公众号
版权声明:本文标题:RESTful登录(基于token鉴权)的设计实例 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1725632935a1033925.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论