如何利用jwt来保护你的接口服务

目录
  • 前言
  • 一、JWT是什么
    • Headers
    • Playload
    • Signature
  • 二、JWT生成和解析token
  • 三、token续签问题
  • 总结

前言

以前写过一篇关于接口服务规范的文章,原文在此,里面关于安全性问题重点讲述了通过appid,appkey,timestamp,nonce以及sign来获取token,使用token来保障接口服务的安全。今天我们来讲述一种更加便捷的方式,使用jwt来生成token。

一、JWT是什么

JSON Web Token(JWT) 定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT可以设置有效期。

JWT是一个很长的字符串,包含了Header,Playload和Signature三部分内容,中间用.进行分隔。

Headers

Headers部分描述的是JWT的基本信息,一般会包含签名算法和令牌类型,数据如下:

{
    "alg": "RS256",
    "typ": "JWT"
}

Playload

Playload就是存放有效信息的地方,JWT规定了以下7个字段,建议但不强制使用:

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token

除此之外,我们还可以自定义内容

{
    "name":"Java旅途",
    "age":18
}

Signature

Signature是将JWT的前面两部分进行加密后的字符串,将Headers和Playload进行base64编码后使用Headers中规定的加密算法和密钥进行加密,得到JWT的第三部分。

二、JWT生成和解析token

在应用服务中引入JWT的依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

根据JWT的定义生成一个使用RSA算法加密的,有效期为30分钟的token

public static String createToken(User user) throws Exception{

    return Jwts.builder()
        .claim("name",user.getName())
        .claim("age",user.getAge())
        // rsa加密
        .signWith(SignatureAlgorithm.RS256, RsaUtil.getPrivateKey(PRIVATE_KEY))
        // 有效期30分钟
        .setExpiration(DateTime.now().plusSeconds(30 * 60).toDate())
        .compact();
}

登录接口验证通过后,调用JWT生成带有用户标识的token响应给用户,在接下来的请求中,头部携带token进行验签,验签通过后,正常访问应用服务。

public static Claims parseToken(String token) throws Exception{
    return Jwts
        .parser()
        .setSigningKey(RsaUtil.getPublicKey(PUBLIC_KEY))
        .parseClaimsJws(token)
        .getBody();
}

三、token续签问题

上面讲述了关于JWT验证的过程,现在我们考虑这样一个问题,客户端携带token访问下单接口,token验签通过,客户端下单成功,返回下单结果,然后客户端带着token调用支付接口进行支付,验签的时候发现token失效了,这时候应该怎么办?只能告诉用户token失效,然后让用户重新登录获取token?这种体验是非常不好的,oauth2在这方面做的比较好,除了签发token,还会签发refresh_token,当token过期后,会去调用refresh_token重新获取token,如果refresh_token也过期了,那么再提示用户去登录。现在我们模拟oauth2的实现方式来完成JWT的refresh_token。

思路大概就是用户登录成功后,签发token的同时,生成一个加密串作为refresh_token,refresh_token存放在redis中,设置合理的过期时间(一般会将refresh_token的过期时间设置的比较久一点)。然后将token和refresh_token响应给客户端。伪代码如下:

@PostMapping("getToken")
public ResultBean getToken(@RequestBody LoingUser user){

    ResultBean resultBean = new ResultBean();
    // 用户信息校验失败,响应错误
    if(!user){
        resultBean.fillCode(401,"账户密码不正确");
        return resultBean;
    }
    String token = null;
    String refresh_token = null;
    try {
        // jwt 生成的token
        token = JwtUtil.createToken(user);
        // 刷新token
        refresh_token = Md5Utils.hash(System.currentTimeMillis()+"");
        // refresh_token过期时间为24小时
        redisUtils.set("refresh_token:"+refresh_token,token,30*24*60*60);
    } catch (Exception e) {
        e.printStackTrace();
    }

    Map<String,Object> map = new HashMap<>();
    map.put("access_token",token);
    map.put("refresh_token",refresh_token);
    map.put("expires_in",2*60*60);
    resultBean.fillInfo(map);
    return resultBean;
}

客户端调用接口时,在请求头中携带token,在拦截器中拦截请求,验证token的有效性,如果验证token失败,则去redis中判断是否是refresh_token的请求,如果refresh_token验证也失败,则给客户端响应鉴权异常,提示客户端重新登录,伪代码如下:

HttpHeaders headers = request.getHeaders();
// 请求头中获取令牌
String token = headers.getFirst("Authorization");
// 判断请求头中是否有令牌
if (StringUtils.isEmpty(token)) {
    resultBean.fillCode(401,"鉴权失败,请携带有效token");
    return resultBean;
}
if(!token.contains("Bearer")){
    resultBean.fillCode(401,"鉴权失败,请携带有效token");
    return resultBean;
}

token = token.replace("Bearer ","");
// 如果请求头中有令牌则解析令牌
try {
    Claims claims = TokenUtil.parseToken(token).getBody();
} catch (Exception e) {
    e.printStackTrace();
    String refreshToken = redisUtils.get("refresh_token:" + token)+"";
    if(StringUtils.isBlank(refreshToken) || "null".equals(refreshToken)){
        resultBean.fillCode(403,"refresh_token已过期,请重新获取token");
        return resultbean;
    }
}

refresh_token来换取token的伪代码如下:

@PostMapping("refreshToken")
public Result refreshToken(String token){

    ResultBean resultBean = new ResultBean();
    String refreshToken = redisUtils.get(TokenConstants.REFRESHTOKEN + token)+"";
    String access_token = null;
    try {
        Claims claims = JwtUtil.parseToken(refreshToken);
        String username = claims.get("username")+"";
        String password = claims.get("password")+"";
        LoginUser loginUser = new LoginUser();
        loginUser.setUsername(username);
        loginUser.setPassword(password);
        access_token = JwtUtil.createToken(loginUser);
    } catch (Exception e) {
        e.printStackTrace();
    }
    Map<String,Object> map = new HashMap<>();
    map.put("access_token",access_token);
    map.put("refresh_token",token);
    map.put("expires_in",30*60);
    resultBean.fillInfo(map);
    return resultBean;
}

通过上面的分析,我们简单的实现了token的签发,验签以及续签问题,JWT作为一个轻量级的鉴权框架,使用起来非常方便,但是也会存在一些问题,

  • JWT的Playload部分只是经过base64编码,这样我们的信息其实就完全暴露了,一般不要将敏感信息存放在JWT中。
  • JWT生成的token比较长,每次在请求头中携带token,导致请求偷会比较大,有一定的性能问题。
  • JWT生成后,服务端无法废弃,只能等待JWT主动过期。

下面这段是我网上看到的一段关于JWT比较适用的场景:

  • 有效期短
  • 只希望被使用一次

比如,用户注册后发一封邮件让其激活账户,通常邮件中需要有一个链接,这个链接需要具备以下的特性:能够标识用户,该链接具有时效性(通常只允许几小时之内激活),不能被篡改以激活其他可能的账户,一次性的。这种场景就适合使用JWT。

总结

到此这篇关于如何利用jwt来保护你的接口服务的文章就介绍到这了,更多相关jwt保护接口服务内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Spring Boot实战之Filter实现使用JWT进行接口认证

    本文介绍了spring Boot实战之Filter实现使用JWT进行接口认证,分享给大家 jwt(json web token) 用户发送按照约定,向服务端发送 Header.Payload 和 Signature,并包含认证信息(密码),验证通过后服务端返回一个token,之后用户使用该token作为登录凭证,适合于移动端和api jwt使用流程 本文示例接上面几篇文章中的代码进行编写,请阅读本文的同时可以参考前面几篇文章 1.添加依赖库jjwt,本文中构造jwt及解析jwt都使用了jjwt库

  • SpringBoot集成Spring security JWT实现接口权限认证

    1.添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjw

  • Java中使用JWT生成Token进行接口鉴权实现方法

    先介绍下利用JWT进行鉴权的思路: 1.用户发起登录请求. 2.服务端创建一个加密后的JWT信息,作为Token返回. 3.在后续请求中JWT信息作为请求头,发给服务端. 4.服务端拿到JWT之后进行解密,正确解密表示此次请求合法,验证通过:解密失败说明Token无效或者已过期. 流程图如下: 一.用户发起登录请求 二.服务端创建一个加密后的JWT信息,作为Token返回 1.用户登录之后把生成的Token返回给前端 @Authorization @ResponseBody @GetMappin

  • Asp.Net Core基于JWT认证的数据接口网关实例代码

    前言 近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对前后端分离的数据服务支持,于是想到我一直做.Net开发,问我是否对.Net Core有所了解?能不能做个简单Demo出来看看?我说,分道扬镳之后我不是调用别人的接口就是提供接口给别人调用,于是便有了以下示例代码. 示例要求能演示获取Token及如何使用该Token访问数据资源,在Demo中实现

  • 如何利用jwt来保护你的接口服务

    目录 前言 一.JWT是什么 Headers Playload Signature 二.JWT生成和解析token 三.token续签问题 总结 前言 以前写过一篇关于接口服务规范的文章,原文在此,里面关于安全性问题重点讲述了通过appid,appkey,timestamp,nonce以及sign来获取token,使用token来保障接口服务的安全.今天我们来讲述一种更加便捷的方式,使用jwt来生成token. 一.JWT是什么 JSON Web Token(JWT) 定义了一种紧凑且自包含的方

  • 利用JWT如何实现对API的授权访问详解

    什么是JWT JWT(JSON Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各个系统之间用JSON作为对象安全地传输信息,并且可以保证所传输的信息不会被篡改. 「JWT」由三部分构成 信息头:指定了使用的签名算法 声明部分:其中也可以包含超时时间 基于指定的算法生成的签名 通过这三部分信息,API 服务端可以根据「JWT」信息头和声明部分的信息重新生成签名.之所以可以这样做,是因为生成签名需要的秘钥存放在服务器端. jwtauth.New("HS2

  • 教你利用springboot集成swagger并生成接口文档

    效果图 实现步骤 1.maven中引入jar包,不同版本的swagger可能页面效果不一样. <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.1</version> </dependency> <dependency> <groupId&g

  • Spring Boot如何利用拦截器加缓存完成接口防刷操作

    目录 为什么需要接口防刷 技术解析 主要代码 测试结果 总结 为什么需要接口防刷 为了减缓服务器压力,将服务器资源留待给有价值的请求,防止恶意访问,一般的程序都会有接口防刷设置,接下来介绍一种简单灵活的接口防刷操作 技术解析 主要采用的技术还是拦截+缓存,我们可以通过自定义注解,将需要防刷的接口给标记出来管理,利用缓存统计指定时间区间里,具体的某个ip访问某个接口的频率,如果超过某个阈值,就让他进一会儿小黑屋,到期自动解放 主要代码 前置环境搭建,Spring Boot项目,引入Web和Redi

  • 基于Koa2写个脚手架模拟接口服务的方法

    ap-cli 前端模拟接口服务脚手架 项目地址 依据接口定义,帮助你快速的完成接口模拟工作. 主要功能: [x] 通过json.js.swagger文件模拟接口数据 [x] 通过正则匹配指定需要mock的文件 [x] 生成简单markdown接口文档 开始 下载ap-cli $ npm install ap-cli -g 创建data.json [ { "url": "/api/get/index", "method": "get&qu

  • python如何构建mock接口服务

    引言 Mock 即模拟,就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法,其最大的优势就是降级前后端耦合度,使前端工程师可以不依赖后端返回数据,先开发前端样式以及逻辑处理 简单来说: Mock是用了解决依赖问题的,将复杂的/不稳定的/还未建立的依赖对象用一个简单的假对象来代替. Mock Server 即Mock接口服务器,可以通过配置快速Mock出新的接口 Mock Server的使用范围: 前后端分离项目 所测接口依赖第三方系统(还未具备)

  • springcloud feign 接口指定接口服务ip方式

    目录 feign接口指定接口服务ip 场景 调用Feign接口时指定ip 只指定服务名 指定ip feign接口指定接口服务ip 场景 现在有2个服务,在eureka注册的服务名称一样,但是对外的接口不一样.其中有一方不允许合并代码,只能把另一个调用指定一下具体的服务地址 @FeignClient(name = "服务名称",url = "${url}",fallback = ServiceHystrix.class) public interface Servic

  • vue中配置后端接口服务信息详解

    目录 vue配置后端接口服务信息 最重要一点 以此接口为例 vue与后端接口是如何进行接口请求的原理 最近遇到一个很奇怪的问题 附上如下图示 vue配置后端接口服务信息 最重要一点 配置完信息后,重新启动服务 npm run dev 需要在config下的index.js中 找到 module.exports = {   dev: {    } } 以此接口为例 dev: {     // Paths     assetsSubDirectory: 'static',     assetsPub

  • springcloud feign 接口指定接口服务ip方式

    目录 feign接口指定接口服务ip 场景 调用feign接口时指定ip 只指定服务名 指定ip feign接口指定接口服务ip 场景 现在有2个服务,在eureka注册的服务名称一样,但是对外的接口不一样.其中有一方不允许合并代码,只能把另一个调用指定一下具体的服务地址 @FeignClient(name = "服务名称",url = "${url}",fallback = ServiceHystrix.class) public interface Servic

  • 利用Python如何实现数据驱动的接口自动化测试

    前言 大家在接口测试的过程中,很多时候会用到对CSV的读取操作,本文主要说明Python3对CSV的写入和读取.下面话不多说了,来一起看看详细的介绍吧. 1.需求 某API,GET方法,token,mobile,email三个参数 token为必填项 mobile,email 必填其中1项 mobile为手机号,email为email格式 2.方案 针对上面的API,在做接口测试时,需要的测试用例动辄会多达10+, 这个时候采用数据驱动的方式将共性的内容写入配置文件或许会更合适. 这里考虑把AP

随机推荐