现在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 instance
RSAPrivateKey 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是一步一步来,新的确实简单方便很多。分享就这里,欢迎交流。

补充参考链接:

web 中使用jwt:    https://github.com/jwtk/jjwt

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

JSON Web Tokens(JWT)的更多相关文章

  1. Spring Boot集成JSON Web Token(JWT)

    一:认证 在了解JWT之前先来回顾一下传统session认证和基于token认证. 1.1 传统session认证 http协议是一种无状态协议,即浏览器发送请求到服务器,服务器是不知道这个请求是哪个 ...

  2. 10分钟了解JSON Web令牌(JWT)

    JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案.虫虫今天给大家介绍JWT的原理和用法. 1.跨域身份验证 Internet服务无法与用户身份验证分开.一般过程如下. 1.用户 ...

  3. JSON WEB TOKEN(JWT)的分析

    JSON WEB TOKEN(JWT)的分析 一般情况下,客户的会话数据会存在文件中,或者引入redis来存储,实现session的管理,但是这样操作会存在一些问题,使用文件来存储的时候,在多台机器上 ...

  4. JSON Web Token(JWT)使用步骤说明

    在JSON Web Token(JWT)原理和用法介绍中,我们了解了JSON Web Token的原理和用法的基本介绍.本文我们着重讲一下其使用的步骤: 一.JWT基本使用 Gradle下依赖 : c ...

  5. JSON Web Token(JWT)原理和用法介绍

    JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案.今天给大家介绍一下JWT的原理和用法. 官网地址:https://jwt.io/ 一.跨域身份验证 Internet服务无法与 ...

  6. JSON Web Token(JWT)机制

    JSON Web Token(JWT)机制  JWT是一种紧凑且自包含的,用于在多方传递JSON对象的技术.传递的数据可以使用数字签名增加其安全行.可以使用HMAC加密算法或RSA公钥/私钥加密方式. ...

  7. 了解JSON Web令牌(JWT)

    JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案.今天给大家介绍JWT的原理和用法. 1.跨域身份验证 Internet服务无法与用户身份验证分开.一般过程如下. 1.用户向服 ...

  8. JSON Web令牌(JWT)介绍与使用

    手机端接口开发会遇到一个问题是,接口登录后需要返回一个Token.token首先有一点必须唯一,每次请求都需要把token给带上.基于必须唯一的特性,很多朋友在开发是都选择了uuid.是不是token ...

  9. JSON Web Token(JWT)的详解

    1.传统身份验证和JWT的身份验证 传统身份验证: HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用.这里我们把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过下回这个客户 ...

随机推荐

  1. java类加载小记

    java类只有当创建实体或被调用时才会加载,加载时按 编码顺序 先加载static后加载普通的.static模块和static变量都是同一等级的,谁写前面就先加载谁. 在调用某个静态类的方法时,会按编 ...

  2. Mybatis了解(配置)

    Mybatis是一个基于jdbc映射框架.它跟hibernate一样都是对数据库进行操作的.Mybatis 它是通过配置xml或者是注解来进行映射的配置,最后实现操作接口与pojo来操作数据库. 因此 ...

  3. Pyquery API中文版

    Pyquery的用法与jQuery相同,可以直接参考jQuery API学习.

  4. 容器平台选型的十大模式:Docker、DC/OS、K8S 谁与当先?

    作者:刘超   来自:网易云 基础服务 无论是在社区,还是在同客户交流的过程中,总会被问到到底什么时候该用 Docker?什么时候用虚拟机?如果使用容器,应该使用哪个容器平台? 显而易见,我不会直接给 ...

  5. ubuntu下MySQL修改root密码的多种方法,phpmyadmin空密码无法登陆的解决方法

    phpmyadmin是默认不允许使用空密码的,所以若是在安装时没有设置密码,在登陆phpmyadmin时是个很头疼的问题 方法1是修改phpmyadmin的配置文件,这里不做推荐.. 方法2: php ...

  6. Jvm加载jar包的顺序

    使用-XX:+TraceClassPaths或者在服务器上执行jinfo时,都能得到classpath包含的jar包,例如: java.class.path = local/aaa/lib/sprin ...

  7. 【转】嵌入式C语言调试开关

    在调试程序时,经常会用到assert和printf之类的函数,我最近做的这个工程里就有几百个assert,在你自认为程序已经没有bug的时候,就要除去这些调试代码,应为系统在正常运行时这些用于调试的信 ...

  8. (转)Nginx与tomcat组合的简单使用

    原文出自:http://www.cnblogs.com/naaoveGIS/ 1.背景 项目中瓦片资源越来越多,如果提高瓦片的访问效率是一个需要解决的问题.这里,我们考虑使用Nginx来代理静态资源进 ...

  9. Fork/Join-Java并行计算框架

    Java在JDK7之后加入了并行计算的框架Fork/Join,可以解决我们系统中大数据计算的性能问题.Fork/Join采用的是分治法,Fork是将一个大任务拆分成若干个子任务,子任务分别去计算,而J ...

  10. .NET程序集引用COM组件MSScriptControl所遇到的问题

    问题描述:为了在C#中执行js脚本,在一个目标平台编译为Any Cpu的.NET程序集中引用了MSScriptControl组件,在winform程序中,调用这个程序集中的执行js的方法,没有任何问题 ...