最近一直在思考如何重构现有的微服务架构安全认证的解决方案,于是与Baron同学针对JWT在不同架构演变的基础上进行了一次深入讨论,熟悉微服务的朋友都知道相比较传统项目架构JWT技术标准对扩展是非常有利,但是如果JWT使用不当的话对项目安全来说无疑是致命的,所以在使用JWT时要注意安全性,本文结合自己的实践以及整理JWT相关资料后,写下这篇文章给大家分享一下,希望对大家有所帮助,欢迎评论交流~
JWT介绍
JWT使用场景
JWT的结构体
数据生成格式
JWT流程图
SpringCloud下如何使用JWT?
JWT 使用 pom文件中引入依赖
JWT令牌生成验证实例
关于JWT有效期与安全性
Json web token (JWT) 是目前最流行的跨域认证解决方案,JWT是一个开放标准 (RFC 7519),它定义了一种紧凑且独立的方式,可以在客户端与服务器之间作为JSON对象安全地传输信息。
在微服务架构下的服务基本都是无状态的,传统的使用session的方式不再适用,如果使用的话需要做同步session机制,所以产生了一些技术来对微服务架构进行保护,例如常用的鉴权框架Spring Security OAuth2和用Jwt来进行保护,相对于框架而言,jwt较轻,且可以自包含一些用户信息和设置过期时间,省去了Spring Security OAuth2繁琐的步骤
JWT由三部分组成,分别是Header、Payload、Signature中间以.隔开
Header:头部,通常头部有两部分信息
Payload:载荷,就是有效数据,一般包含下面信息
Signature:签名,是整个数据的认证信息。一般根据前两步的base64编码后的数据,再加上服务的的密钥secret(存在服务器不能泄露),通过加密算法生成。用于验证整个数据完整和可靠性
osnzheciOiJIUzI1NiIsInR5cCI6Ifaqgag.tqgbeznOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaGdas35wiaWF0IjoxNTE2MjM5MDIyfe.vhsgKxwRJSMeKKF2QT4fwpMeJf36POk6yJVmnbw5dwqaf
用户在提交登录信息后,服务器校验数据后将通过密钥的方式来生成一个字符串token返回给客户端,客户端在之后的请求会把token放在header里,在请求到达服务器后,服务器会检验和解密token,如果token被篡改或者失效将会拒绝请求,如果有效则服务器可以获得用户的相关信息并执行请求内容,最后将结果返回
在微服务架构下,通常有单独一个服务Auth去管理相关认证,为了安全不会直接让用户访问某个服务,会开放一个入口服务作为网关gateway,只允许外网网关,所有请求首先访问gateway,由gateway将请求路由到各个服务,那么我们通常的做法在网关里进行请求拦截,来保证项目安全性,下图是JWT在微服务中流程图(图中采用非对称加密算法,利用私钥在auth加密,公钥在网关gateway中解密,由此来减轻auth压力,可根据场景灵活运用JWT)
com.auth0
java-jwt
3.3.0
package com.giantfind.common.util;
import com.giantfind.common.component.IDGenerator;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.lang.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
/**
* @ProjectName: auth-service-api
* @Package: com.giantfind.common.util
* @ClassName: JwtUtils
* @Author: liuyaolong
* @Description: JWT 工具类
* @Version: 1.0
*/
public class JwtUtils {
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
private static final String SECRET = "7ejklf96z0ebhsnefkfoxuiciybv";
/**
* 生成token
* @param issuer 签发者
* @param subject JWT所面向的用户,可选
* @param ttlMillis 过期时间,可选
* @param audience 接收该JWT的一方,可选
* @param claimHashtable 自定义荷载
* @return
*/
public static String createToken(String issuer,String subject,long ttlMillis, String audience,Hashtable claimHashtable){
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
JwtBuilder jwtBuilder = Jwts.builder()
.setHeaderParam("alg", "HS256")
.setId(String.valueOf(new IDGenerator().getGlobalId()))
.setSubject(subject)
.setIssuedAt(now)
.setIssuer(issuer)
.setAudience(audience)
.signWith(signatureAlgorithm,signingKey);
if (!Collections.isEmpty(claimHashtable)) {
//自定义业务荷载
for(Map.Entry entry: claimHashtable.entrySet()){
jwtBuilder.claim(entry.getKey(),entry.getValue());
}
}
//设置Token的过期时间
if(ttlMillis >= 0){
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
jwtBuilder.setExpiration(exp);
}
return jwtBuilder.compact();
}
/**
* 私钥解密token信息
* @param token
* @return
*/
public static Claims getClaims(String token) {
try {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(SECRET))
.parseClaimsJws(token).getBody();
return claims;
} catch (Exception e) {
logger.info("Token令牌已过期或不存在:{}",token);
return null;
}
}
public static void main(String[] args) {
//自定义参数荷载
Hashtable claimHashtable = new Hashtable<>();
claimHashtable.put("userName","admin");
claimHashtable.put("nickName","devin");
String token = createToken("devin","wechat",10000000L,"all",claimHashtable);
System.out.println(token);
System.out.println(getClaims(token).get("userName"));
}
}
JWT如果使用不当,服务器如同裸奔~~~
假如黑客监控电脑,抓包获取到token,伪造http 请求,对服务器是非常不安全的,常见的问题如下
- 客户端修改token中body的信息进行操作(修改了之后签名信息就不正确,然后就无法验证签名,说明数据被修改)
- 客户端伪造用户token进行访问进行操作(无法使用服务器的签名,所以在保证密钥不被泄露的情况下,不会被渗透)
- 客户端截取token,模仿真实用户进行操作(解决办法:对敏感api接口,采用https,https是在http超文本传输协议加入SSL层,它在网络间通信是加密的,所以需要加密证书。或者在代码层面进行优化做安全检测:比如根据ip地址,设备码,一次性token机制,token时效期等措施来解决项目安全性问题,或者其他的解决方案评论交流)
以上就是本文的全部内容,希望对大家的学习有所帮助,欢迎评论交流。能get到知识点不要忘了关注点赞~~~