关于java:Java基于JWT的token认证

一、背景引入

因为Http协定自身是无状态的,那么服务器是怎么辨认两次申请是不是来自同一个客户端呢,传统用户辨认是基于seesion和cookie实现的。大抵流程如下:

  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);
 }
}
//...其余办法省略...

headerClaims是一个Map,包含两个属性typ和alg,typ值固定JWT,alg传过来的签名算法这里应用的

HmacSHA256简称HS256。typ和alg组成Header头信息。

Payload载荷构造剖析要害源码如下:

public abstract class JWT {
 public JWT() {
 }
 public static DecodedJWT decode(String token) throws JWTDecodeException {
 return new JWTDecoder(token);
 }
 public static Verification require(Algorithm algorithm) {
 return JWTVerifier.init(algorithm);
 }
 public static Builder create() {
 return JWTCreator.init();
 }
}
public static class Builder {
 private final Map<String, Object> payloadClaims = new HashMap();
 private Map<String, Object> headerClaims = new HashMap();
 Builder() {
 }
 public JWTCreator.Builder withHeader(Map<String, Object> headerClaims) {
 this.headerClaims = new HashMap(headerClaims);
 return this;
 }
 public JWTCreator.Builder withKeyId(String keyId) {
 this.headerClaims.put("kid", keyId);
 return this;
 }
 public JWTCreator.Builder withIssuer(String issuer) {
 this.addClaim("iss", issuer);//签发人
 return this;
 }
 public JWTCreator.Builder withSubject(String subject) {
 this.addClaim("sub", subject);//主题
 return this;
 }
 public JWTCreator.Builder withAudience(String... audience) {
 this.addClaim("aud", audience);//承受一方
 return this;
 }
 public JWTCreator.Builder withExpiresAt(Date expiresAt) {
 this.addClaim("exp", expiresAt);//过期工夫
 return this;
 }
 public JWTCreator.Builder withNotBefore(Date notBefore) {
 this.addClaim("nbf", notBefore);//失效工夫
 return this;
 }
 public JWTCreator.Builder withIssuedAt(Date issuedAt) {
 this.addClaim("iat", issuedAt);//签发工夫
 return this;
 }
 public JWTCreator.Builder withJWTId(String jwtId) {
 this.addClaim("jti", jwtId);//编号
 return this;
 }
 public JWTCreator.Builder withClaim(String name, Boolean value)
 throws IllegalArgumentException {
 this.assertNonNull(name);
 this.addClaim(name, value);
 return this;
 }
 public JWTCreator.Builder withClaim(String name, Integer value)
 throws IllegalArgumentException {
 this.assertNonNull(name);
 this.addClaim(name, value);
 return this;
 }
 //...其余办法省略...
}

Payload是一个json对象,寄存须要传递的数据,JTW默认规定了几个属性,如果须要增加其余属性能够调用其重载办法witchClaim()增加。

Signature签名局部源码如下:

private String sign() throws SignatureGenerationException {
 String header = Base64.encodeBase64URLSafeString(
 this.headerJson.getBytes(StandardCharsets.UTF_8));
 String payload = Base64.encodeBase64URLSafeString(
 this.payloadJson.getBytes(StandardCharsets.UTF_8));
 String content = String.format("%s.%s", header, payload);
 byte[] signatureBytes = this.algorithm.sign(
 content.getBytes(StandardCharsets.UTF_8));
 String signature = Base64.encodeBase64URLSafeString(signatureBytes);
 return String.format("%s.%s", content, signature);
}

从这里能够看出,所谓token就是别离对header和payload的json字符串做Base64加密失去a和b,并将后果拼接一起,在进行签名失去c,最终把a、b、c三局部内容以点拼接起来造成token,返回客户端保留,客户端当前每次申请都在header中退出token,服务器采纳拦截器形式获取header中的token做校验,辨认用户。

三、示例

3.1 数据筹备

创立用户表

3.2 搭建springboot工程

设置工程Group、Artifact、Version、Name等信息

Spring Boot的版本抉择2.0.8,抉择导入web的起步器

创立工程胜利之后,将各个包创立进去,工程目录构造如下:

3.3 引入pom依赖

3.4 编写application.yml配置文件

3.5 编写User实体类

Result类:用于对立返回音讯的封装

TokenUtil类,用于生成token

VerifyToken注解类:加到controller办法上示意该办法须要验证token。

3.6 编写mapper接口和service层

mapper类:

UserService接口:

UserServiceImpl实现类:

3.7 编写拦截器和全局异样处理器

AuthInterceptor拦截器类:用于token验证。

全局异样处理器GloabllExceptionHandler:用于异样的捕捉。

3.8 编写配置类及controller

拦截器配置类InterceptorConfig:配置拦挡所有申请

UserController类:

3.9 测试

测试1:应用postman发送get申请http://localhost:8088/user/getUser?id=1

测试2:发送post申请http://localhost:8088/user/login 明码成心输错

测试3:发送post申请http://localhost:8088/user/login 填正确的用户名明码

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理