Spring Gateway使用JWT实现统一身份认证

这篇具有很好参考价值的文章主要介绍了Spring Gateway使用JWT实现统一身份认证。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在开发集群式或分布式服务时,鉴权是最重要的一步,为了方便对请求统一鉴权,一般都是会放在网关中进行处理。目前非常流行的一种方案是使用JWT,详细的使用说明,可以找相关的资料查阅,这里先不进行深入的引用了。主要使用它下面的特性:

  • 它的数据使用JSON格式封装。所以JWT是可以在不同的开发语音中传递。
  • 在payload可以加载部分业务数据,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 减少了内存占用,也不需要落地存储,提升了检查效率。
  • JWT 使用的密钥都是在服务器端,不会暴露到客户端,所以是安全的。

具体的流程如下:

  1. 用户先访问登陆授权服务器,授权验证通过之后,返回给客户端授权服务器生成的JWT Token字符串
  2. 客户端再访问后面的接口时,将授权服务器返回的JWT Token添加到header中
  3. 服务器网关收到客户端请求时,检测JWT Token是否合法,如果不合法,拒绝访问,返回错误。

jwt 统一认证,集群式游戏服务器开发记录,Spring Boot & Spring Cloud,游戏服务器,JWT 检测,Spring gateway
需要处理的另一个问题是JWT Token 失败的问题,比如用户修改了密码,原来的JWT Token就不能再被使用了,一般是做法是添加JWT Token的黑名单,直到JWT Token失败。毕竟触发某些事件让JWT Token失效还是低概率事件。
做法如下:

  • 当JWT Token失效事件发生时,将原来的JWT TOKEN 加入的黑名单中,黑名单,可以存到Redis或数据库中。
  • 为了提升处理效率,网关服务定时从授权服务刷新黑名单到网关服务内存中,这样检测JWT Token是否在黑名单中效率比较高
  • 在黑名单中的JWT Token 过期后,自动从黑名单中删除,防止黑名单数量堆积。
  • 为了防止用户JWT Token扩展,用户登陆之后检测,如果已存在JWT Token 且过期时间大于1天,就返回旧的JWT Token,否则自动延期,返回新的JWT Token

实现方式

  • 在项目pom.xml中添加依赖
 		<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

  • 创建JWT Token的管理类
package com.xinyue.game.jwt;

import java.time.Duration;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.alibaba.fastjson.JSON;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * @author 王广帅
 * @since 2023/5/23 22:27
 **/
public class GameJwtService {

    private final static String JWT_SUBJECT = "game_token";
    private final static String TOKEN_EXTRA_KEY = "token_extra_key";

    /**
     * 创建一个Jwt token
     *
     * @param data 需要携带的业务数据,这里不要放置敏感信息,因为它是明文传输的
     * @param key  HS512的签名密钥
     * @return
     */
    public String createJwtToken(Object data, Duration expire, byte[] key) {
        Date expireDate = new Date(System.currentTimeMillis() + expire.toMillis());
        JwtBuilder jwtBuilder = Jwts.builder().setSubject(JWT_SUBJECT);
        if (data != null) {
            Map<String, Object> claims = new HashMap<>();
            claims.put(TOKEN_EXTRA_KEY, JSON.toJSONString(data));
            jwtBuilder.addClaims(claims);
        }
        String token = jwtBuilder.setExpiration(expireDate).compressWith(CompressionCodecs.DEFLATE).signWith(SignatureAlgorithm.HS512, key).compact();
        return token;
    }

    /**
     * 检查 jwt token并获取token携带的业务数据
     *
     * @param token
     * @param key
     * @return
     * @throws JwtTokenExpiredException
     * @throws JwtTokenErrorException
     */
    public <T> T checkTokenAndGet(String token, byte[] key, Class<T> clazz) throws JwtTokenExpiredException, JwtTokenErrorException {
        try {
            Jws<Claims> headerClaimsJwt = Jwts.parser().requireSubject(JWT_SUBJECT).setSigningKey(key).parseClaimsJws(token);
            Claims body = headerClaimsJwt.getBody();
            String value = (String) body.get(TOKEN_EXTRA_KEY);
            return JSON.parseObject(value, clazz);
        } catch (ExpiredJwtException e) {
            throw new JwtTokenExpiredException("token 已过期");
        } catch (Throwable e) {
            throw new JwtTokenErrorException("token不合法");
        }
    }
}

  • 在Spring Cloud Gateway中添加全局过滤器
    使用全局过滤器,我们检测所有的请求是否合法,这里需要一个配置,因为有些请求是不需要检测token的,比如登陆和注册等,
package com.xinyue.game.web.gateway.access;

import java.nio.charset.StandardCharsets;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSONObject;
import com.xinyue.game.jwt.GameJwtService;
import com.xinyue.game.jwt.GameUserToken;
import com.xinyue.game.jwt.JwtTokenErrorException;
import com.xinyue.game.jwt.JwtTokenExpiredException;
import com.xinyue.game.web.gateway.common.XinYueWebGatewaySystemConfig;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.ByteBufFlux;

/**
 * 访问授权过滤器,如果访问的地址,不在忽略名单内,则必须经过授权检测才可以访问
 *
 * @author 王广帅
 * @since 2023/5/23 21:12
 **/
@Service
public class AccessAuthVerifyFilter implements GlobalFilter {

    private Logger logger = LoggerFactory.getLogger(AccessAuthVerifyFilter.class);
    @Autowired
    private XinYueWebGatewaySystemConfig webGatewaySystemConfig;


    private GameJwtService gameJwtService = new GameJwtService();


    private boolean isIgnoreCheckUri(String uri) {
        return webGatewaySystemConfig.getUriAuthIgnoreList().contains(uri);
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String userToken = exchange.getRequest().getHeaders().getFirst("Authorization");
        // 获取请求的路径
        String uri = exchange.getRequest().getPath().value();
        logger.debug("收到请求:{}", uri);
        if (ObjectUtils.isEmpty(userToken)) {
            if (isIgnoreCheckUri(uri)) {
                // 如果是不需要检测的请求,直接返回成功
                return chain.filter(exchange);
            }else {
                return this.responseError(exchange, 5001, "未登陆成功,请重新登陆之后再重试");
            }
        } else {
            byte[] key = webGatewaySystemConfig.getTokenAesKey().getBytes(StandardCharsets.UTF_8);
            try {
                GameUserToken gameRoleToken = gameJwtService.checkTokenAndGet(userToken, key, GameUserToken.class);
                if (gameRoleToken == null || gameRoleToken.getUserId() == null) {
                    return this.responseError(exchange, 5001, "登陆数据不正确,请重新登陆");
                }
            } catch (JwtTokenExpiredException e) {
                return this.responseError(exchange, 5002, "登陆已过期,请重新登陆");
            } catch (JwtTokenErrorException e) {
                return this.responseError(exchange, 5003, "非法登陆,请重新登陆");
            }
        }
        // 如果没有异常,继续往下传递
        return chain.filter(exchange);
    }

    /**
     * 统一响应错误提示
     *
     * @param exchange
     * @param code
     * @param msg
     * @return
     */
    private Mono<Void> responseError(ServerWebExchange exchange, int code, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        JSONObject data = new JSONObject();
        data.put("code", code);
        data.put("msg", msg);
        byte[] dataBytes = data.toJSONString().getBytes(StandardCharsets.UTF_8);
        Mono<Void> ret = response.writeAndFlushWith(Flux.just(ByteBufFlux.just(response.bufferFactory().wrap(dataBytes))));
        return ret;
    }
}

实现源码地址:https://gitee.com/wgslucky/xinyue-game-frame文章来源地址https://www.toymoban.com/news/detail-659582.html

到了这里,关于Spring Gateway使用JWT实现统一身份认证的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【安全】探索统一身份认证:OAuth 2.0的介绍、原理和实现方法

    在现代互联网应用中,用户需要在多个应用程序之间共享身份验证和授权信息。OAuth 2.0作为一种流行的统一身份认证解决方案,通过简化和安全地授权第三方应用程序访问用户资源,为用户提供了更好的体验。本文将深入介绍OAuth 2.0的概念、原理和实现方法,帮助读者更好地

    2024年02月11日
    浏览(61)
  • 【Node.js】身份认证,Cookie和Session的认证机制,express中使用session认证和JWT认证

    Web开发模式 基于服务器渲染的传统web开发模式 服务端渲染的概念,服务器发送给客户端的HTNL页面,是在服务器通过字符串的拼接,动态生成的,因此,客户端不需要Ajax这样的技术额外请求页面的数据 优点: 前端耗时少,有利于seo(就是爬虫更容易爬取获得信息,更有利于

    2023年04月11日
    浏览(46)
  • Spring Gateway + Oauth2 + Jwt网关统一鉴权

    之前文章里说过,分布式系统的鉴权有两种方式,一是在网关进行统一的鉴权操作,二是在各个微服务里单独鉴权。 第二种方式比较常见,代码网上也是很多。今天主要是说第一种方式。 重要前提:需要收集各个接口的uri路径和所需权限列表的对应关系,并存入缓存。 服务

    2024年02月03日
    浏览(45)
  • API身份认证JWT

    是一种身份认证的开放标准(RFC 7519),可以在网络应用间传输信息作为Json对象。由三部分组成:头部(Header)、载荷(payload)和签名(Signature). 头部(Header) 两部分组成,令牌类型和所使用的的签名算法   载荷(payload) 包含要传输的信息,包括用户的身份信息、权限等。载

    2024年02月13日
    浏览(37)
  • Day17-Node后端身份认证-JWT

    1 MD5加密 创建MD5.js

    2024年02月12日
    浏览(37)
  • Spring Security 如何实现身份认证和授权?

    Spring Security 是一个开源的安全框架,提供了基于权限的访问控制、身份认证、安全性事件发布等功能。在 Spring Boot 应用中使用 Spring Security 可以非常方便地实现用户身份认证和授权。 Spring Security 实现身份认证的主要方式是使用认证过滤器链,该过滤器链包含多个过滤器,用

    2024年02月06日
    浏览(49)
  • 智慧校园统一身份认证平台建设

    随着应用建设的逐步深入,已经建成的和将要建成的各种校园应用系统存在不同的身份认证方式,用户必须记忆不同的密码和身份。因此,要建设以目录服务和认证服务为基础的统一用户管理、授权管理和身份认证体系,将组织信息、用户信息统一存储,进行分级授权和集中

    2024年01月20日
    浏览(38)
  • 指纹统一身份认证系统功能特点

     实现用户单点登录 对于 B/S 结构应用系统,用户只需通过浏览器界面登录一次,即可通过统一身份认证系统 访问后台的多个用户权限内的 Web 应用系统,无需逐一输入用户名、密码登录。对于 C/S 结构应用系统,通过 Active 控件或客户端 Plugin 来实现对 C/S 系统客户端的单点

    2024年02月11日
    浏览(42)
  • 指纹统一身份认证系统系统安全设计

    2.10.1 身份认证 系统是为广大工作人员提供服务的,为了区分各个用户以及不同级别的用户,需要对他们的 身份和操作的合法性进行检查。体系应该规定实现身份认证与权限检查的方式、方法以及对 这些用户的管理要求。 2.10.2 权限管理 权限管理系统是保证业务系统安全的一

    2024年02月10日
    浏览(50)
  • 钉钉统一身份认证对接前后端代码

    钉钉统一身份认证平台提供了多种对接方式,包括OAuth2.0、SAML、CAS等,其中OAuth2.0是最常用的一种方式。OAuth2.0的对接流程如下: 1.开发人员在钉钉开放平台上创建应用,并获取应用的AppID和AppSecret。 2.在应用的授权回调页面中,添加钉钉授权登录按钮,并将其链接到钉钉授权

    2024年02月02日
    浏览(78)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包