JAVA JWT GATEWAY

JWT–以算法校验替换token值的比较

jwt之前,我们做登录会在后端生成一个字符串令牌存储到 redis 中,然后下次客户端访问资源想要免登录就要带着后端返回的这个字符串和明文用户信息。请求到后端,后端会根据用户信息去redis里面取出对应的字符串,与前端带过来的值进行比较(就是字符串的比较)。比较值相同则认为该用户身份信息有效合法,接着后端会返回对应的请求信息给客户端。

这样做有什么问题?
通常来说,这样的设计可以把项目运行起来,但是相比较于下面的jwt认证方式,问题就暴露了出来。

如果用户量很大,假设某应用日均活跃量产生的token达到几个十几个G甚至更大,或者说redis宕机(即便不用redis也需要将token存到某个公共区,该公共区也会有宕机风险)。JWT的出现解决了以上的问题。先说一说jwt的组成部分:

在这里插入图片描述
上图源自jwt官方 jwt.io.由官方的网页使用者可以清晰地看到jwt地密文是 xxxxxxx.xxxxxxx.xxxxxxx的字符串组成,由 “.” 隔开成为三个部分,同样明文也是由三个部分组成,HEADER,PAYLOAD,VERIFY SIGNATURE组成。

一般来说对于使用者而言,HEADER里面存放jwt的一些说明,这部分默认给出类型:jwt,算法:HS256 。PAYLOAD存放用户的一些公开信息,用于保证用户信息的唯一性,就像人身份证号码一样的作用。这里面的键值对可以是默认的一些值,常用的例如“exp” (token过期时间),也可以是自定义的一些键值对。而类似于 redis 的token 字符串值比较这一行为的工作,jwt 通过 “签名”部分做算法计算来替代。简单来说就是通过某一种算法和一个服务端的密钥,将HEADER 和 PAYLOAD信息进行加密计算,得到的字符串作为 SIGNATURE。当下次客户端传入 token值,只需要再次对用户的明文信息和HEADER计算,将结果值和传入的SIGNATURE做比较,相同则认定识别成功。初次使用者需要理解一下 服务端密钥的作用,这个服务端密钥是不可以泄露的,可以说它就是一把解开jwt的一把钥匙。

JAVA 代码实现 jwt.io在线解析

项目里可能会用到以下的依赖

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.DecodingException;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
/**
 * JWT token manager.
 */

public class JwtTokenManager {
    private static final String AUTHORITIES_KEY = "auth";
    /**
     * secret key.
     */
    private String secretKey="SecretKey012345678901234567890123456789012345678901234567890123456789";
    /**
     * secret key byte array.
     */
    private byte[] secretKeyBytes;
    /**
     * Token validity time(seconds).
     */
    private long tokenValidityInSeconds=300;
    private JwtParser jwtParser;
    /**
     * init tokenValidityInSeconds and secretKey properties.
     */
    public String getSecretKey() {
        return secretKey;
    }
    /**
     * Create token.
     * @param userName auth info
     * @return token
     */
    public String createToken(String userName) {
        long now = System.currentTimeMillis();
        Date validity;
        validity = new Date(now + this.getTokenValidityInSeconds() * 1000L);
        Claims claims = Jwts.claims().setSubject(userName);
        return Jwts.builder().setClaims(claims).setExpiration(validity)
                .signWith(Keys.hmacShaKeyFor(this.getSecretKeyBytes()), SignatureAlgorithm.HS256).compact();
    }

    /**
     * validate token.
     * @param token token
     */
    public void validateToken(String token) {
        if (jwtParser == null) {
            jwtParser = Jwts.parserBuilder().setSigningKey(this.getSecretKeyBytes()).build();
        }
        jwtParser.parseClaimsJws(token);
    }

    public byte[] getSecretKeyBytes() {
        if (secretKeyBytes == null) {
            try {
                secretKeyBytes = Decoders.BASE64.decode(secretKey);
            } catch (DecodingException e) {
                secretKeyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
            }

        }
        return secretKeyBytes;
    }

    public long getTokenValidityInSeconds() {
        return tokenValidityInSeconds;
    }
}

注意到jwt.io上的VERIFY SIGNATURE 部分有一个勾选 secret base64 encoded ,意思是 密钥是经过Base64加密后的,需要先进行解密,代码部分需要改动的就是

        return Jwts.builder().setHeader(map).setClaims(claims).setExpiration(validity)
                .signWith(Keys.hmacShaKeyFor(Base64.decodeBase64(k)), SignatureAlgorithm.HS256).compact();

以上就是对于JWT的个人见解,有不对的欢迎指正。
附上 demo项目 内涵 上述代码片段
gitee项目demo

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的 Spring Cloud Gateway 基于JWT的验证代码案例: 1. 首先,确保你的项目中已经引入了相关的依赖,包括 Spring Cloud Gateway、Spring Security 和 JWT 相关的库。 2. 创建一个自定义的 GatewayFilter,用于验证JWT并将用户身份信息添加到请求头中: ```java import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class JwtAuthFilter implements GatewayFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 从请求头中获取 JWT String jwt = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION); // 验证 JWT,根据需要进行相应的验证逻辑 // 如果验证成功,将用户身份信息添加到请求头中 exchange.getRequest().mutate() .header("X-User-Id", "123") // 示例,替换为实际的用户身份信息 .build(); return chain.filter(exchange); } } ``` 3. 在配置类中注册自定义的 GatewayFilter: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class GatewayConfig { @Bean public JwtAuthFilter jwtAuthFilter() { return new JwtAuthFilter(); } } ``` 4. 配置 Spring Cloud Gateway 路由谓词和过滤器,指定需要进行JWT验证的路由: ```yaml spring: cloud: gateway: routes: - id: example_route uri: http://example.com predicates: - Path=/example/** filters: - JwtAuthFilter= # 指定使用 JwtAuthFilter 进行JWT验证 ``` 在这个示例中,我们创建了一个名为 `JwtAuthFilter` 的自定义过滤器,用于验证JWT并将用户身份信息添加到请求头中。然后在配置类中注册该过滤器,并在路由配置中指定需要使用该过滤器的路由。 请注意,这只是一个简单的示例,实际的JWT验证逻辑可能更复杂。你可以根据自己的需求进行相应的调整和扩展。希望对你有所帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值