JSON Web Tokens(JWT)

现在API越来越流行,如何安全保护这些API? JSON Web Tokens(JWT)能提供基于JSON格式的安全认证。它有以下特点:

  • JWT是跨不同语言的,JWT可以在 .NET, Python, Node.js, Java, PHP, Ruby, Go, JavaScript和Haskell中使用
  • JWT是自我包涵的,它们包含了必要的所有信息,这就意味着JWT能够传递关于它自己的基本信息,比如用户信息和签名等。
  • JWT传递是容易的,因为JWT是自我包涵,它们能被完美用在HTTP头部中,当需要授权API时,你只要通过URL一起传送它既可。

JWT易于辨识,是三段由小数点组成的字符串:

aaaaaaaaaa.bbbbbbbbbbb.cccccccccccc

这三部分含义分别是header,payload, signature

Header

头部包含了两个方面:类型和使用的哈希算法(如HMAC SHA256):

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

对这个JSON字符进行base64encode编码,我们就有了首个JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

JWT的第二部分是payload,也称为 JWT Claims,这里放置的是我们需要传输的信息,有多个项目如注册的claim名称,公共claim名称和私有claim名称。

注册claim名称有下面几个部分:

  • iss: token的发行者
  • sub: token的题目
  • aud: token的客户
  • exp: 经常使用的,以数字时间定义失效期,也就是当前时间以后的某个时间本token失效。
  • nbf: 定义在此时间之前,JWT不会接受处理。开始生效时间
  • iat: JWT发布时间,能用于决定JWT年龄
  • jti: JWT唯一标识. 能用于防止 JWT重复使用,一次只用一个token;如果签发的时候这个claim的值是“1”,验证的时候如果这个claim的值不是“1”就属于验证失败

公共claim名称用于定义我们自己创造的信息,比如用户信息和其他重要信息。

私有claim名称用于发布者和消费者都同意以私有的方式使用claim名称。

下面是JWT的一个案例:

{
"iss": "scotch.io",
"exp": 1300819380,
"name": "Chris Sevilleja",
"admin": true
}

签名

JWT第三部分最后是签名,签名由以下组件组成:

  • header
  • payload
  • 密钥

下面是我们如何得到JWT的第三部分:

var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); HMACSHA256(encodedString, ‘secret‘);

这里的secret是被服务器签名,我们服务器能够验证存在的token并签名新的token。

TWT支持的算法有:

============================================================================================================

以上是官网的理论部分,下面会有提供一些实例:

首先 导入 依赖:

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.2.0</version>
</dependency>

1, 指定加密算法:

//HMAC
Algorithm algorithmHS = Algorithm.HMAC256("secret");
-------------------------------------------------------------------------
//RSA

Map<String,Object> keys=RSAUtils.getKeys();
RSAPublicKey publicKey = (RSAPublicKey)keys.get("public"); //Get the key instanceRSAPrivateKey privateKey = (RSAPrivateKey)keys.get("private");//Get the key instance
Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey);

2 , 生成token

用HS256生成token

try {
    Algorithm algorithm = Algorithm.HMAC256("secret");
    String token = JWT.create()
        .withIssuer("auth0")
        .sign(algorithm);
} catch (UnsupportedEncodingException exception){
    //UTF-8 encoding not supported
} catch (JWTCreationException exception){
    //Invalid Signing configuration / Couldn‘t convert Claims.
}

用RS256生成token

 Map<String,Object> keys=RSAUtils.getKeys();
        RSAPublicKey publicKey = (RSAPublicKey)keys.get("public"); //Get the key instance
        RSAPrivateKey privateKey = (RSAPrivateKey)keys.get("private");//Get the key instance
try {
    Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
    String token = JWT.create()
        .withIssuer("auth0")
        .sign(algorithm);
} catch (JWTCreationException exception){
    //Invalid Signing configuration / Couldn‘t convert Claims.
}

3, 验证token

1)普通验证

用HS256验证token

String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
try {
    Algorithm algorithm = Algorithm.HMAC256("secret");
    JWTVerifier verifier = JWT.require(algorithm)
        .withIssuer("auth0")
        .build(); //Reusable verifier instance
    DecodedJWT jwt = verifier.verify(token);
} catch (UnsupportedEncodingException exception){
    //UTF-8 encoding not supported
} catch (JWTVerificationException exception){
    //Invalid signature/claims
}

用RS256验证token

String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instance
try {
    Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
    JWTVerifier verifier = JWT.require(algorithm)
        .withIssuer("auth0")
        .build(); //Reusable verifier instance
    DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException exception){
    //Invalid signature/claims
}

2)在payLoad 是可以自定义数据,用于验证,包括时间等。

在生成token的时候指定数据:

@Test
   public void gen1() throws IOException {
        String token ="";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //日期转字符串
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.SECOND,30 ); //特定时间的年后
            Date date = calendar.getTime();
        try {
           Algorithm algorithm = Algorithm.HMAC256("mysecrite");
            token = JWT.create()
                   .withIssuer("auth0")
                    .withSubject("xiaoming")
                    .withClaim("name", 123)
                    .withArrayClaim("array", new Integer[]{1, 2, 3})
                    .withExpiresAt(date)
                   .sign(algorithm);
           System.out.println("loglogagel:"+token);
       } catch (UnsupportedEncodingException exception){
           //UTF-8 encoding not supported
       } catch (JWTCreationException exception){
           //Invalid Signing configuration / Couldn‘t convert Claims.
       }

   }

验证token是否过期,是否有制定的

@Test
  public void gen3(){
      String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCIsImV4cCI6MTQ5NzY4NTQwOX0.DHY-90JAA63_TvI-gRZ2oHCIItMajb45zB1tdCHQ_NQ";
      try {
          Algorithm algorithm = Algorithm.HMAC256("mysecrite");

          JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(algorithm)
                  .withIssuer("auth0")
                  .withSubject("xiaomong");
          Clock clock = new Clock() {
              @Override
              public Date getToday() {
                  return new Date();
              }
          };//Must implement Clock interface
          JWTVerifier verifier = verification.build(clock);
          DecodedJWT jwt = verifier.verify(token);
          System.out.println(jwt.getAlgorithm());
          System.out.println(jwt.getType());
          System.out.println(jwt.getIssuer());
          System.out.println(jwt.getExpiresAt());
      } catch (UnsupportedEncodingException exception){
          //UTF-8 encoding not supported
          exception.printStackTrace();
      } catch (JWTVerificationException exception){
          //Invalid signature/claims
          exception.printStackTrace();
      }
}

如果 subject验证的不一致,就会报如下错误:

如果时间超过 30 秒,会报如下错误:

对验证的方法稍加修改:

 @Test
  public void gen3(){
      String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4aWFvbWluZyIsImFycmF5IjpbMSwyLDNdLCJpc3MiOiJhdXRoMCIsIm5hbWUiOiJJYW0gcmlnaHQgZnJvbSBjbGFpbSIsImV4cCI6MTQ5NzY4OTQ4NX0.6lsXISVAgi8B2wAvaZq4tj-h9Pgd6GGaOYZLz_gPFMU";
      try {
          Algorithm algorithm = Algorithm.HMAC256("mysecrite");

          JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(algorithm)
                  .withIssuer("auth0")
                  .withSubject("xiaoming");
          Clock clock = new Clock() {
              @Override
              public Date getToday() {
                  return new Date();
              }
          };//Must implement Clock interface
          JWTVerifier verifier = verification.build(clock);
          DecodedJWT jwt = verifier.verify(token);
          Map<String, Claim> claims = jwt.getClaims();    //Key is the Claim name
          Claim claim = claims.get("name");
          System.out.println(claim.asString());    //打印出claim的值
          System.out.println(jwt.getAlgorithm());
          System.out.println(jwt.getType());
          System.out.println(jwt.getIssuer());
          System.out.println(jwt.getExpiresAt());
      } catch (UnsupportedEncodingException exception){
          //UTF-8 encoding not supported
          exception.printStackTrace();
      } catch (JWTVerificationException exception){
          //Invalid signature/claims
          exception.printStackTrace();
      }

验证后的最后结果:

4,claim的添加,获取

1) 内置的payload主要有以下几个,如果没有就返回null

Issuer ("iss") :发布者

String issuer = jwt.getIssuer();

Subject ("sub")

String subject = jwt.getSubject();

Audience ("aud")

List<String> audience = jwt.getAudience();

Expiration Time ("exp")

Date expiresAt = jwt.getExpiresAt();

Not Before ("nbf")

Date notBefore = jwt.getNotBefore();

Issued At ("iat")

Date issuedAt = jwt.getIssuedAt();

JWT ID ("jti")

String id = jwt.getId();

2)定义私有的claim

添加:

String token = JWT.create()
        .withClaim("name", 123)
        .withArrayClaim("array", new Integer[]{1, 2, 3})
        .sign(algorithm);

获取:

JWTVerifier verifier = JWT.require(algorithm)
    .withClaim("name", 123)
    .withArrayClaim("array", 1, 2, 3)
    .build();
DecodedJWT jwt = verifier.verify("my.jwt.token");

目前,官方支持claim的类型的有:Boolean, Integer, Double, String, Date , String[] 和 Integer.

5,  Header Claims

1)header claims 是定义header部分的内容,基本都是默认定义,不需要自己去设置的,内置的有:

Algorithm ("alg")

String algorithm = jwt.getAlgorithm();

Type ("typ")

String type = jwt.getType();

Content Type ("cty")

String contentType = jwt.getContentType();

Key Id ("kid")

String keyId = jwt.getKeyId();

2)添加:

Map<String, Object> headerClaims = new HashMap();
headerClaims.put("owner", "auth0");
String token = JWT.create()
        .withHeader(headerClaims)
        .sign(algorithm);

3)获取:

Claim claim = jwt.getHeaderClaim("owner");

总结: 看了其他人的一些博客,发现他们的api都是相对老一点的版本,生成token是一步一步来,新的确实简单方便很多。分享就这里,欢迎交流。

参考地址:https://github.com/auth0/java-jwt

时间: 2024-10-10 20:02:03

JSON Web Tokens(JWT)的相关文章

Spring Boot集成JSON Web Token(JWT)

一:认证 在了解JWT之前先来回顾一下传统session认证和基于token认证. 1.1 传统session认证 http协议是一种无状态协议,即浏览器发送请求到服务器,服务器是不知道这个请求是哪个用户发来的.为了让服务器知道请求是哪个用户发来的,需要让用户提供用户名和密码来进行认证.当浏览器第一次访问服务器(假设是登录接口),服务器验证用户名和密码之后,服务器会生成一个sessionid(只有第一次会生成,其它会使用同一个sessionid),并将该session和用户信息关联起来,然后将s

JSON WEB TOKEN(JWT)的分析

JSON WEB TOKEN(JWT)的分析 一般情况下,客户的会话数据会存在文件中,或者引入redis来存储,实现session的管理,但是这样操作会存在一些问题,使用文件来存储的时候,在多台机器上,比较难实现共享,使用redis来存储的时候,则需要引入多一个集群,这样会增加管理的工作量,也不方便.有一个直观的办法,就是将session数据,存储在客户端中,使用签名校验数据是否有篡改,客户请求的时候,把session数据带上,获取里面的数据,通过校验,然后进行身份认证. 数据存储在客户端中,会

JSON Web令牌(JWT)

JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. 1.跨域身份验证 Internet服务无法与用户身份验证分开.一般过程如下. 1.用户向服务器发送用户名和密码. 2.验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中. 3.服务器向用户返回session_id,session信息都会写入到用户的Cookie. 4.用户的每个后续请求都将通过在Cookie中取出session_id传给服务器. 5.服务器收到session_id并对比之前保存的数据,确认

Json Web Token(JWT)

Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景.JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密. 组成 由句号分隔的三段base64串b1.b2.b3,如:eyJ0eXAiOiJKV1QiL

JSON Web Tokens的实现原理

前言 最近在做一个python项目的改造,将python项目重构为java项目,过程中遇到了这个知识点,觉得这个蛮实用的,所以下班后回来趁热打铁写下这篇总结,希望后面的人能够有所借鉴,少走弯路. 一.优势简介 JSON Web Tokens简称jwt,是rest接口的一种安全策略.本身有很多的优势: 解决跨域问题:这种基于Token的访问策略可以克服cookies的跨域问题. 服务端无状态可以横向扩展,Token可完成认证,无需存储Session. 系统解耦,Token携带所有的用户信息,无需绑

JWT(JSON Web Tokens)

1.JWT即JSON Web Tokens,他可以用来安全的传递信息,因为这些信息是经过数字签名的 2.JWT可以使用一种加密算法比如HMAC 算法,也可以使用公钥/私钥的非对称算法 3.因为JWT签名后的信息够短,可以放在url里.request body里.http header里,传输够快. 4.荷载信息里包含所有你想要的,避免不止一次的去查询数据库 5.JWT的使用场景主要包括: 1) 认证,这是比较常见的使用场景,只要用户登录过一次系统,之后的请求都会包含签名出来的token,通过to

我这么玩Web Api(二):数据验证,全局数据验证与单元测试

目录 一.模型状态 - ModelState 二.数据注解 - Data Annotations 三.自定义数据注解 四.全局数据验证 五.单元测试   一.模型状态 - ModelState 我理解的ModelState是微软在ASP.NET MVC中提出的一种新机制,它主要实现以下几个功能: 1. 保存客户端传过来的数据,如果验证不通过,把数据返回到客户端,这样可以保存用户输入,不需要重新输入. 2. 验证数据,以及保存数据对应的错误信息. 3. 微软的一种DRY(Don't Repeat

JSON WEB TOKENS

用JWT来保护我们的ASP.NET Core Web API 在上一篇博客中,自己动手写了一个Middleware来处理API的授权验证,现在就采用另外一种方式来处理这个授权验证的问题,毕竟现在也 有不少开源的东西可以用,今天用的是JWT. 什么是JWT呢?JWT的全称是JSON WEB TOKENS,是一种自包含令牌格式.官方网址:https://jwt.io/,或多或少应该都有听过这个. 先来看看下面的两个图: 站点是通过RPC的方式来访问api取得资源的,当站点是直接访问api,没有拿到有

[Node.js] Creating JWTs (JSON Web Tokens) in Node

In this lesson we will look at all of the pieces that combine together to create a JWT (j AWT) or JSON Web Token. You will use node to create a JWT, and then verify it in the JWT debugger. What is the JSON Web Token structure? JSON Web Tokens consist