Java基于JWT的token认证

一、背景引入由于Http协议本身是无状态的,那么服务器是怎么识别两次请求是不是来自同一个客户端呢,传统用户识别是基于seeion和cookie实现的 。大致流程如下:

Java基于JWT的token认证

文章插图
 
  1. 用户向服务器发送用户名和密码请求用户进行校验,校验通过后创建session绘画,并将用户相关信息保存到session中服务器将sessionId回写到用户浏览器cookie中用户以后的请求,都会鞋带cookie发送到服务器服务器得到cookie中的sessionId,从session集合中找到该用户的session回话,识别用户
这种模式有很多缺点,对于分布式架构的支持以及扩展性不是很好 。而且session是保存在内存中,单台服务器部署如果登陆用户过多占用服务器资源也多,做集群必须得实现session共享的话,集群数量又不易太多,否则服务器之间频繁同步session也会非常耗性能 。当然也可以引入持久层,将session保存在数据库或者redis中,保存数据库的话效率不高,存redis效率高,但是对redis依赖太重,如果redis挂了,影响整个应用 。还有一种办法就是不存服务器,而是把用户标识数据存在浏览器,浏览器每次请求都携带该数据,服务器做校验,这也是JWT的思想 。
二、JWT介绍2.1 概念介绍
Json Web Token(JWT)是目前比较流行的跨域认证解决方案,是一种基于JSON的开发标准,由于数据是可以经过签名加密的,比较安全可靠,一般用于前端和服务器之间传递信息,也可以用在移动端和后台传递认证信息 。
2.2 组成结构
JWT就是一段字符串,格式如下:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIn0.qfd0GelhE1aGr15LrnYlIZ_3UToaOM5HeMcXrmDG由于三部分组成,之间用"."接 。第一部分是头信息Header,中间部分是载荷Payload,最后部分是签名信息Signature 。
头信息Header:描述JWT基本信息,typ表示采用JWT令牌,alg(algorithm)表示采用什么算法进行签名,常见算法有HmacSHA256(HS256)、HmacSHA384(HS384)、HmacSHA512(HS512)、SHA256withECDSA(ES256)、SHA256withRSA(RS256)、SHA512withRSA(RS512)等 。如果采用HS256则头信息结构为:
{ "typ": "JWT", "alg": "HS256}载荷Payload:载荷(也可以叫载体)是具体的传输内容,包括一些标准属性,iss: 该JWT的签发者,exp: 过期时间戳,iat: 签发时间戳,jti: JWTID等等 。也可以添加其他需要传递的内容数据 。结构为:
{ "iss": "kkk", "iat": 1548818203, "exp": 1548818212, "sub": "test.com}签名Signature:对头信息和载荷进行签名,保证传输过程中信息不被篡改,比如:将头信息和载荷分别进行base64加密得到字符串a和b,将字符串a和b以点相连并签名得到字符串c,将字符串a、b、c以点相连得到最终token 。
2.3 验证流程
使用JWT的验证流程为:
  1. 用户提交用户名,密码到服务器后台后台验证通过,服务器端生成Token字符串,返回到客户端客户端保存Token,下一次请求资源时,附带上Token信息服务器端验证Token是否由服务器签发的(一般在拦截器中验证),若Token验证通过,则返回需要的资源
验证流程和基于session大体相同,只不过不是基于session,而是采用拦截器在代码中实验验证,返回给客户端的也不是sessionid,而是经过一定算法得出来的token字符串 。
2.4 源码分析
JAVA中有封装好的开源哭JWT可以直接使用,下面就分析下关键代码验证以下内容 。
Header头信息结构分析关键源码如下:
//token生成方法public static void main(String[] args) { String token= JWT.create().withAudience("audience") .withIssuedAt(new Date()) .withSubject("subject") .withExpiresAt(new Date()).withJWTId("jtiid") .sign(Algorithm.HMAC256(user.getPassword()));}public abstract class Algorithm { private final String name; private final String description; //...其他方法省略... public static Algorithm HMAC256(String secret) throws IllegalArgumentException { return new HMACAlgorithm("HS256", "HmacSHA256", secret); } //...其他方法省略...}class HMACAlgorithm extends Algorithm { private final CryptoHelper crypto; private final byte[] secret; //...其他方法省略...HMACAlgorithm(String id, String algorithm, byte[] secretBytes)throws IllegalArgumentException {this(new CryptoHelper(), id, algorithm, secretBytes);}//...其他方法省略..}public String sign(Algorithm algorithm) throws IllegalArgumentException,JWTCreationException { if (algorithm == null) { throw new IllegalArgumentException("The Algorithm cannot be null.");} else { this.headerClaims.put("alg", algorithm.getName()); this.headerClaims.put("typ", "JWT"); String signingKeyId = algorithm.getSigningKeyId(); if (signingKeyId != null) { this.withKeyId(signingKeyId);}public final class JWTCreator { private final Algorithm algorithm; private final String headerJson; private final String payloadJson; private JWTCreator(Algorithm algorithm, Map<String, Object> headerClaims, Map<String, Object> payloadClaims) throws JWTCreationException { this.algorithm = algorithm; try { ObjectMApper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(ClaimsHolder.class, new PayloadSerializer()); mapper.registerModule(module); mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); this.headerJson = mapper.writeValueAsString(headerClaims); this.payloadJson = mapper.writeValueAsString(new ClaimsHolder(payloadClaims)); } catch (JsonProcessingException var6) { throw new JWTCreationException( "Some of the Claims couldn't be converted to a valid JSON format.", var6); }}//...其他方法省略...


推荐阅读