JWT在微服务系统中的如何应用,如何保证安全性?

前言

       最近一直在思考如何重构现有的微服务架构安全认证的解决方案,于是与Baron同学针对JWT在不同架构演变的基础上进行了一次深入讨论,熟悉微服务的朋友都知道相比较传统项目架构JWT技术标准对扩展是非常有利,但是如果JWT使用不当的话对项目安全来说无疑是致命的,所以在使用JWT时要注意安全性,本文结合自己的实践以及整理JWT相关资料后,写下这篇文章给大家分享一下,希望对大家有所帮助,欢迎评论交流~

目录

JWT介绍

JWT使用场景

JWT的结构体

数据生成格式

JWT流程图

SpringCloud下如何使用JWT?

JWT 使用 pom文件中引入依赖

JWT令牌生成验证实例

关于JWT有效期与安全性


JWT介绍

        Json web token (JWT) 是目前最流行的跨域认证解决方案,JWT是一个开放标准 (RFC 7519),它定义了一种紧凑且独立的方式,可以在客户端与服务器之间作为JSON对象安全地传输信息。

JWT使用场景

        在微服务架构下的服务基本都是无状态的,传统的使用session的方式不再适用,如果使用的话需要做同步session机制,所以产生了一些技术来对微服务架构进行保护,例如常用的鉴权框架Spring Security OAuth2和用Jwt来进行保护,相对于框架而言,jwt较轻,且可以自包含一些用户信息和设置过期时间,省去了Spring Security OAuth2繁琐的步骤

JWT的结构体

JWT由三部分组成,分别是Header、Payload、Signature中间以.隔开

Header:头部,通常头部有两部分信息

  • 声明类型,这里是JWT
  • 加密算法

Payload:载荷,就是有效数据,一般包含下面信息

  • 用户身份信息(注意,这里客户端是可解密的,所以不要存放敏感信息)
  • 注册声明:如token的签发时间,过期时间,签发人等

Signature:签名,是整个数据的认证信息。一般根据前两步的base64编码后的数据,再加上服务的的密钥secret(存在服务器不能泄露),通过加密算法生成。用于验证整个数据完整和可靠性

数据生成格式

osnzheciOiJIUzI1NiIsInR5cCI6Ifaqgag.tqgbeznOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaGdas35wiaWF0IjoxNTE2MjM5MDIyfe.vhsgKxwRJSMeKKF2QT4fwpMeJf36POk6yJVmnbw5dwqaf

JWT流程图

        用户在提交登录信息后,服务器校验数据后将通过密钥的方式来生成一个字符串token返回给客户端,客户端在之后的请求会把token放在header里,在请求到达服务器后,服务器会检验和解密token,如果token被篡改或者失效将会拒绝请求,如果有效则服务器可以获得用户的相关信息并执行请求内容,最后将结果返回

SpringCloud下如何使用JWT?

        在微服务架构下,通常有单独一个服务Auth去管理相关认证,为了安全不会直接让用户访问某个服务,会开放一个入口服务作为网关gateway,只允许外网网关,所有请求首先访问gateway,由gateway将请求路由到各个服务,那么我们通常的做法在网关里进行请求拦截,来保证项目安全性,下图是JWT在微服务中流程图(图中采用非对称加密算法,利用私钥在auth加密,公钥在网关gateway中解密,由此来减轻auth压力,可根据场景灵活运用JWT)

JWT 使用 pom文件中引入依赖

<!-- JWT核心依赖 -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.3.0</version>
</dependency>
		

JWT令牌生成验证实例

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<String,Object> 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<String, Object> 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<String,Object> 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有效期与安全性

JWT如果使用不当,服务器如同裸奔~~~

假如黑客监控电脑,抓包获取到token,伪造http 请求,对服务器是非常不安全的,常见的问题如下

  • 客户端修改token中body的信息进行操作(修改了之后签名信息就不正确,然后就无法验证签名,说明数据被修改)
  • 客户端伪造用户token进行访问进行操作(无法使用服务器的签名,所以在保证密钥不被泄露的情况下,不会被渗透)
  • 客户端截取token,模仿真实用户进行操作(解决办法:对敏感api接口,采用https,https是在http超文本传输协议加入SSL层,它在网络间通信是加密的,所以需要加密证书。或者在代码层面进行优化做安全检测:比如根据ip地址,设备码,一次性token机制,token时效期等措施来解决项目安全性问题,或者其他的解决方案评论交流)

以上就是本文的全部内容,希望对大家的学习有所帮助,欢迎评论交流。能get到知识点不要忘了关注点赞~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

破茧重生ys

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值