什么是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是怎样工作的

  1. 应用程序或客户端向授权服务器请求授权。这里的授权服务器可以是单独的一个应用,也可以和API集成在同一个应用里。
  2. 授权服务器向应用程序返回一个JWT。
  3. 应用程序将JWT放入到请求里(通常放在HTTPAuthorization头里)
  4. 服务端接收到请求后,验证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里的信息。如上所示。

尾注

使用JWT来实现对API的授权访问的更多相关文章

  1. Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问

    本篇内容还是建立在上一篇Java Web学习系列——Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Jar包 这 ...

  2. 【转+自己研究】新姿势之Docker Remote API未授权访问漏洞分析和利用

    0x00 概述 最近提交了一些关于 docker remote api 未授权访问导致代码泄露.获取服务器root权限的漏洞,造成的影响都比较严重,比如 新姿势之获取果壳全站代码和多台机器root权限 ...

  3. SILVERLIGHT实现对HTML DOM的访问

    实现对HTML DOM的访问.Silverlight 2在命名空间System.Windows.Browser下内置了很多对于HTML DOM访问和操作的支持,我们最常用的一个对象是HtmlEleme ...

  4. [Shell]Docker remote api未授权访问漏洞(Port=2375)

    0x01 简介 该未授权访问漏洞是因为docker remote api可以执行docker命令,从官方文档可以看出,该接口是目的是取代docker 命令界面,通过url操作docker. Docke ...

  5. 关于docker remote api未授权访问漏洞的学习与研究

    漏洞介绍: 该未授权访问漏洞是因为docker remote api可以执行docker命令,从官方文档可以看出,该接口是目的是取代docker 命令界面,通过url操作docker. docker ...

  6. [Kong] key-auth实现对API请求的密钥认证

    目录 1. 配置密钥验证插件 2. 确认插件配置正确 3. 创建cunsumer 4. 给cunsumer提供关键凭证 5. 验证 6. 小结 [前言]: 下面我们将配置key-auth插件以向服务添 ...

  7. 【OPCAutomation】 使用OPCAutomation实现对OPC数据的访问

    折腾了一段时间研究OPC,理清了下位机.OPCServer 和OPCClient的关系和通信模型,终于能够来写一篇相关的博客了. 我们使用西门子的 S7 200 SMART作为下位机端,通过3G路由器 ...

  8. 利用过滤器Filter和特性Attribute实现对Web API返回结果的封装和统一异常处理

    在我们开发Web API应用的时候,我们可以借鉴ABP框架的过滤器Filter和特性Attribute的应用,实现对Web API返回结果的封装和统一异常处理,本篇随笔介绍利用AuthorizeAtt ...

  9. 用JWT技术为SpringBoot的API增加授权保护(转),需要自己实现userdetailservice接口

    转自:https://blog.csdn.net/haiyan_qi/article/details/77373900 概述 示例 https://github.com/qihaiyan/jwt-bo ...

随机推荐

  1. WordCount作业修改

    WordCount作业修改 github地址 需求说明 基本需求 功能说明 PSP 代码实现 字符总数查询 单词数查询 行数查询 总结 一.需求说明 1.基本需求 WordCount的需求可以概括为: ...

  2. 测者的测试技术笔记:Screenplay 模式(Journey 模式)

    Screenplay模式 Junit的Screenplay 举例 Actor theReceptionist =newActor().with(WebBrowsing.ability()) theRe ...

  3. 一个解决过程:Servlet [某路径xxx] in web application [/项目xxx] threw load() exception和java.lang.ClassNotFoundException XXX

    Servlet [某路径xxx] in web application [/项目xxx] threw load() exception和java.lang.ClassNotFoundException ...

  4. java笔记----线程状态转换函数

    注意:stop().suspend()和 resume()方法现在已经不提倡使用,这些方法在虚拟机中可能引起“死锁”现象.suspend()和 resume()方法的替代方法是 wait()和 sle ...

  5. css属性总结

    前言 有些属性不是很常用,但是工作中遇到了,记录一下,方便学习. 1.text-indent text-indent 属性规定文本块中首行文本的缩进.主要实现就像word那种首行缩进的效果. 详解ht ...

  6. 洗礼灵魂,修炼python(68)--爬虫篇—番外篇之webbrowser模块

    题外话: 爬虫学到这里,我想你大部分的网站已经不再话下了对吧?有检测报文头的,我们可以伪造报文头为浏览器,有检测IP,我们可以用代理IP,有检测请求速度的,我们可以用time模块停顿一下,需要登录验证 ...

  7. SSIS使用事务回滚

    --创建表ttt Create table ttt ( ID INT PRIMARY KEY , NAME VARCHAR(50) ) --插入测试数据 INSERT INTO TTT VALUES ...

  8. MySQL 8.0.11安装配置

    官网地址:https://dev.mysql.com/downloads/mysql/ 我这里是RHEL6.5的系统,因此选择RedHat 6 x86,64bit操作系统---下载第一个RPM Bun ...

  9. lambda表达式2

    1.lambda表达式 Java8最值得学习的特性就是Lambda表达式和Stream API,如果有python或者javascript的语言基础,对理解Lambda表达式有很大帮助,因为Java正 ...

  10. Linux下键盘值 对应input_evnet的code值。

    最近做了一个linux下面的模拟鼠标和键盘的app,但不是很清楚字符对应的键值:查找内核源码,在kernel/include/uapi/linux/input.h文件中找到: 下面给出普通键盘上面对应 ...