使用JWT来实现对API的授权访问
什么是JWT
JWT(JSON Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各个系统之间用JSON作为对象安全地传输信息,并且可以保证所传输的信息不会被篡改。
JWT通常有两种应用场景:
- 授权。这是最常见的JWT使用场景。一旦用户登录,每个后续请求将包含一个JWT,作为该用户访问资源的令牌。
- 信息交换。可以利用JWT在各个系统之间安全地传输信息,JWT的特性使得接收方可以验证收到的内容是否被篡改。
本文讨论第一点,如何利用JWT来实现对API的授权访问。这样就只有经过授权的用户才可以调用API。
JWT的结构
JWT由三部分组成,用.
分割开。
Header
第一部分为Header
,通常由两部分组成:令牌的类型,即JWT,以及所使用的加密算法。
{
"alg": "HS256",
"typ": "JWT"
}
Base64
加密后,就变成了:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload
第二部分为Payload
,里面可以放置自定义的信息,以及过期时间、发行人等。
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Base64
加密后,就变成了:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Signature
第三部分为Signature
,计算此签名需要四部分信息:
Header
里的算法信息Header
Payload
- 一个自定义的秘钥
接受到JWT后,利用相同的信息再计算一次签名,然年与JWT中的签名对比,如果不相同则说明JWT中的内容被篡改。
解码后的JWT
将上面三部分都编码后再合在一起就得到了JWT。
需要注意的是,JWT的内容并不是加密的,只是简单的Base64
编码。也就是说,JWT一旦泄露,里面的信息可以被轻松获取,因此不应该用JWT保存任何敏感信息。
JWT是怎样工作的
- 应用程序或客户端向授权服务器请求授权。这里的授权服务器可以是单独的一个应用,也可以和API集成在同一个应用里。
- 授权服务器向应用程序返回一个JWT。
- 应用程序将JWT放入到请求里(通常放在
HTTP
的Authorization
头里) - 服务端接收到请求后,验证JWT并执行对应逻辑。
在JAVA里使用JWT
引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
这里使用了一个叫JJWT(Java JWT)的库。
JWT Service
生成JWT
public String generateToken(String payload) {
return Jwts.builder()
.setSubject(payload)
.setExpiration(new Date(System.currentTimeMillis() + 10000))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
- 这里设置过期时间为10秒,因此生成的JWT只在10秒内能通过验证。
- 需要提供一个自定义的秘钥。
解码JWT
public String parseToken(String jwt) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(jwt)
.getBody()
.getSubject();
}
- 解码时会检查JWT的签名,因此需要提供秘钥。
验证JWT
public boolean isTokenValid(String jwt) {
try {
parseToken(jwt);
} catch (Throwable e) {
return false;
}
return true;
}
JJWT
并没有提供判断JWT是否合法的方法,但是在解码非法JWT时会抛出异常,因此可以通过捕获异常的方式来判断是否合法。
注册/登录
@GetMapping("/registration")
public String register(@RequestParam String username, HttpServletResponse response) {
String jwt = jwtService.generateToken(username);
response.setHeader(JWT_HEADER_NAME, jwt);
return String.format("JWT for %s :\n%s", username, jwt);
}
- 需要为还没有获取到JWT的用户提供一个这样的注册或者登录入口,来获取JWT。
- 获取到响应里的JWT后,要在后续的请求里包含JWT,这里放在请求的
Authorization
头里。
验证JWT
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
String jwt = httpServletRequest.getHeader(JWT_HEADER_NAME);
if (WHITE_LIST.contains(httpServletRequest.getRequestURI())) {
chain.doFilter(request, response);
} else if (isTokenValid(jwt)) {
updateToken(httpServletResponse, jwt);
chain.doFilter(request, response);
} else {
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
private void updateToken(HttpServletResponse httpServletResponse, String jwt) {
String payload = jwtService.parseToken(jwt);
String newToken = jwtService.generateToken(payload);
httpServletResponse.setHeader(JWT_HEADER_NAME, newToken);
}
- 将验证操作放在
Filter
里,这样除了登录入口,其它的业务代码将感觉不到JWT的存在。 - 将登录入口放在
WHITE_LIST
里,跳过对这些入口的验证。 - 需要刷新JWT。如果JWT是合法的,那么应该用同样的
Payload
来生成一个新的JWT,这样新的JWT就会有新的过期时间,用此操作来刷新JWT,以防过期。 - 如果使用
Filter
,那么刷新的操作要在调用doFilter()
之前,因为调用之后就无法再修改response
了。
API
private final static String JWT_HEADER_NAME = "Authorization";
@GetMapping("/api")
public String testApi(HttpServletRequest request, HttpServletResponse response) {
String oldJwt = request.getHeader(JWT_HEADER_NAME);
String newJwt = response.getHeader(JWT_HEADER_NAME);
return String.format("Your old JWT is:\n%s \nYour new JWT is:\n%s\n", oldJwt, newJwt);
}
这时候API就处于JWT的保护下了。API可以完全不用感知到JWT的存在,同时也可以主动获取JWT并解码,以得到JWT里的信息。如上所示。
尾注
- 完整的DEMO可以在这里找到:https://github.com/Beginner258/jwt-demo
- 参考资料:https://jwt.io/
使用JWT来实现对API的授权访问的更多相关文章
- Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问
本篇内容还是建立在上一篇Java Web学习系列——Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Jar包 这 ...
- 【转+自己研究】新姿势之Docker Remote API未授权访问漏洞分析和利用
0x00 概述 最近提交了一些关于 docker remote api 未授权访问导致代码泄露.获取服务器root权限的漏洞,造成的影响都比较严重,比如 新姿势之获取果壳全站代码和多台机器root权限 ...
- SILVERLIGHT实现对HTML DOM的访问
实现对HTML DOM的访问.Silverlight 2在命名空间System.Windows.Browser下内置了很多对于HTML DOM访问和操作的支持,我们最常用的一个对象是HtmlEleme ...
- [Shell]Docker remote api未授权访问漏洞(Port=2375)
0x01 简介 该未授权访问漏洞是因为docker remote api可以执行docker命令,从官方文档可以看出,该接口是目的是取代docker 命令界面,通过url操作docker. Docke ...
- 关于docker remote api未授权访问漏洞的学习与研究
漏洞介绍: 该未授权访问漏洞是因为docker remote api可以执行docker命令,从官方文档可以看出,该接口是目的是取代docker 命令界面,通过url操作docker. docker ...
- [Kong] key-auth实现对API请求的密钥认证
目录 1. 配置密钥验证插件 2. 确认插件配置正确 3. 创建cunsumer 4. 给cunsumer提供关键凭证 5. 验证 6. 小结 [前言]: 下面我们将配置key-auth插件以向服务添 ...
- 【OPCAutomation】 使用OPCAutomation实现对OPC数据的访问
折腾了一段时间研究OPC,理清了下位机.OPCServer 和OPCClient的关系和通信模型,终于能够来写一篇相关的博客了. 我们使用西门子的 S7 200 SMART作为下位机端,通过3G路由器 ...
- 利用过滤器Filter和特性Attribute实现对Web API返回结果的封装和统一异常处理
在我们开发Web API应用的时候,我们可以借鉴ABP框架的过滤器Filter和特性Attribute的应用,实现对Web API返回结果的封装和统一异常处理,本篇随笔介绍利用AuthorizeAtt ...
- 用JWT技术为SpringBoot的API增加授权保护(转),需要自己实现userdetailservice接口
转自:https://blog.csdn.net/haiyan_qi/article/details/77373900 概述 示例 https://github.com/qihaiyan/jwt-bo ...
随机推荐
- (后端)sql手工注入语句&SQL手工注入大全(转)
转自脚本之家: 看看下面的1.判断是否有注入;and 1=1;and 1=2 2.初步判断是否是mssql;and user>0 3.判断数据库系统;and (select count(*) f ...
- 云ERP真的靠谱吗?
现在几乎每个IT系统或项目都要跟云挂上钩,跟数码产品必与“智能”扯上关系一样,否则在外行甚至同行眼里就是“矮小搓”.ERP领域也悄然刮起了云端化.国内ERP产品也借此机会想弯道超车,通过云化来抢夺被S ...
- Bresenham算法的实现思路
条件已知两个点的坐标p1(x0,y0),p2(x1,y1)要求画出这条直线 之后的e代表每次的误差积累,初始值为0,可以计算出斜率为k=dy/dx=(y0-y1)/(x0-x1) 1.x为阶跃步长(直 ...
- RAS非对称加密与数字证书数字签名
它用图片通俗易懂地解释了,"数字签名"(digital signature)和"数字证书"(digital certificate)到底是什么. 我对这些问题的 ...
- shell编程—变量(三)
在shell脚本中,变量分两种,系统变量和自定义变量. 系统默认变量是系统自带的一些变量,如path为路径变量 用户自定义变量为在编写吧脚本的时候自己定义的一些变量 变量名命名规则 首个字符必须为字母 ...
- Python:import 与__import__()
首先来说一下两者的区别: import指令做了两件事:1)搜索module,2)绑定到局部变量 内置函数__import__()只做了一件事:搜索module import指令执行过程中是调用__im ...
- window.location.href刷新页面
刷新当前页 window.location.href=window.location.href; 或者 window.location.href="当前URL",例如 window ...
- nmap脚本使用总结
0x00 前言: nmap的基本介绍和基本使用方法,在乌云知识库中已经有人提交过,讲的比较详细,在此文中就不再讲述. 具体链接:http://drops.wooyun.org/tips/2002 本文 ...
- python 黏包现象及其解决方案
一.数据缓冲区 缓冲区(buffer),它是内存空间的一部分.也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,显然缓冲区是具有一定大小的 ...
- 【Teradata System】How Teradata uses MPP Systems
内存分配 内存初始化时将分配给操作系统和Vprocs,内存不使用部分的90%做为FSG (File Segment Cache) ,由PDE对FSG进行管理. FSG Cache:缓存常驻内存的dat ...