gateway和jwt网关认证实现过程解析

所属分类: 软件编程 / java 阅读数: 62
收藏 0 赞 0 分享

这篇文章主要介绍了gateway和jwt网关认证实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

思路: 全局过滤器对所有的请求拦截(生成token有效期30分钟,放入redis设置有效期3天。3天之类可以通过刷新接口自动刷新,超过3天需要重新登录。)

前端在调用接口之前先判断token是否过期(3o分钟),过期则先调刷新接口,换取新token,

1引入相关jar

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.0</version>
</dependency>

2编写Jwt工具类(生成token + 解析token)

package spring.cloud.gateway.common;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.util.StringUtils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtUtil {
  public static final String SECRET = "qazwsx123444$#%#()*&& asdaswwi1235 ?;!@#kmmmpom in***xx**&";
  public static final String TOKEN_PREFIX = "Bearer";
  public static final String LOGIN_URL = "/token/userId/pwd";
  public static final String LOGOUT_URL = "/token/userId";
  public static final String HEADER_AUTH = "authorization";
  public static final String HEADER_USERID = "userid";
  //token超时时间
  public static final int TOKEN_EXPIRATION_MINUTE = 30;
  //token的redis超时时间
  public static final int TOKEN_REDIS_EXPIRATION_DAY = 7;

  
  public static String generateToken(String userId) {
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, TOKEN_EXPIRATION_MINUTE); //得到前一天
    Date date = calendar.getTime();
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    df.format(date);
    //todo 优化token的生层规则
    HashMap<String, Object> map = new HashMap<>();
    map.put(HEADER_USERID, userId);
    String jwt = Jwts.builder()
        .setSubject(HEADER_USERID).setClaims(map)
        .setExpiration(date)
        .signWith(SignatureAlgorithm.HS512, SECRET)
        .compact();
    return TOKEN_PREFIX + " " + jwt;
  }

  public static Map<String, String> validateToken(String token) {
    HashMap<String, String> tokenMap = new HashMap<String, String>();
    if (StringUtils.isEmpty(token)) {
      return tokenMap;
    }
    try {
      Map<String, Object> tokenBody = Jwts.parser()
          .setSigningKey(SECRET)
          .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
          .getBody();
      String userId = String.valueOf(tokenBody.get(HEADER_USERID));
      tokenMap.put(HEADER_USERID, userId);
    }catch (ExpiredJwtException e){
      e.printStackTrace();
    }
    return tokenMap;
  }

  /**
   * 移到jwtUtil中去
   *
   * @param token
   * @return
   */
  public static Map<String, String> validateTokenAndUser(String token, String userIdIn) {
    Map<String, String> tokenResultMap = new HashMap<>();
    if (StringUtils.isEmpty(token) || StringUtils.isEmpty(userIdIn)) {
      return tokenResultMap;
    }
    tokenResultMap = validateToken(token);
    if (StringUtils.isEmpty(token) || StringUtils.isEmpty(userIdIn)) {
      return tokenResultMap;
    }
    //判断传入的userid和token是否匹配
    String userIdOri = tokenResultMap.get(HEADER_USERID);
    if (!userIdIn.equals(userIdOri)) {
      return new HashMap<String,String>();
    }
    return tokenResultMap;
  }

}

3编写过滤器类

package spring.cloud.gateway.filter;

import java.net.URI;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.PathContainer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import spring.cloud.gateway.common.JwtUtil;
import spring.cloud.gateway.exception.PermissionException;

/**
 * 参数参考 https://blog.csdn.net/tianyaleixiaowu/article/details/83375246
 * response参考 https://bbs.csdn.net/topics/392412604?list=11074255
 */
@Component
public class AuthFilter implements GlobalFilter {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    ServerHttpRequest request = exchange.getRequest();
    HttpHeaders header = request.getHeaders();
    HttpMethod method = request.getMethod();
    String token = header.getFirst(JwtUtil.HEADER_AUTH);
    String userId = header.getFirst(JwtUtil.HEADER_USERID);
    PathContainer pathContainer = request.getPath().pathWithinApplication();
    String path = pathContainer.value();

    //2- 处理登录请求
    if (StringUtils.isBlank(token)) {
      //是登录接口则放行,否则返回异常
      if (path.contains(JwtUtil.LOGIN_URL) && HttpMethod.POST.equals(method)) {
        throw new PermissionException("please login");
      }
      return chain.filter(exchange);
    }

    //3- 处理刷新token请求
    if (path.indexOf("refresh") >= 0) {
      //放行去掉刷新接口(在刷新前校验userId和token是否匹配)
      return chain.filter(exchange);
    }

    //4- 处理刷新token请求
    if (path.contains(JwtUtil.LOGOUT_URL) && HttpMethod.DELETE.equals(method)) {
      //放行去掉登出接口(在刷新前校验userId和token是否匹配)
      return chain.filter(exchange);
    }

    //5- 携带token请求其他业务接口
    Map<String, String> validateResultMap = JwtUtil.validateTokenAndUser(token, userId);
    if (validateResultMap == null || validateResultMap.isEmpty()) {
      throw new PermissionException("token 已经失效");
    }
    // TODO 将用户信息存放在请求header中传递给下游业务
    Route gatewayUrl = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
    URI uri = gatewayUrl.getUri();
    //表示下游请求对应的服务名如 SPRING-CLOUD-SERVICE SPRING-CLOUD-GATEWAY
    String serviceName = uri.getHost();

    ServerHttpRequest.Builder mutate = request.mutate();
    mutate.header("x-user-id", validateResultMap.get("userid"));
    mutate.header("x-user-name", validateResultMap.get("user"));
    mutate.header("x-user-serviceName", serviceName);
    ServerHttpRequest buildReuqest = mutate.build();

    //todo 如果响应中需要放数据,也可以放在response的header中
    //ServerHttpResponse response = exchange.getResponse();
    //response.getHeaders().add("new_token","token_value");
    return chain.filter(exchange.mutate().request(buildReuqest).build());
  }
}

4编写相关接口API

package spring.cloud.gateway.controller;
import org.springframework.web.bind.annotation.*;
import spring.cloud.gateway.common.JwtUtil;
import java.util.Map;
@RestController
@RequestMapping("/token")
public class TokenController {

  /**
   * 登录接口
   * @param user(userID +pwd)
   * @return
   */
  @PostMapping("/userId/pwd")
  public String getToken(@RequestBody Map<String,String> user) {
    //用户名密码需要加密处理
    String result = "";
    if (user == null || user.isEmpty()) {
      return result;
    }
    String userId = user.get("userId");
    String pwd = user.get("pwd");
    if (!doLogin(userId,pwd)) {
      return result;
    }
    String token = JwtUtil.generateToken(userId);
    // todo 将token放入redis中,设置超时时间为 2 * t
    return token;
  }

  private Boolean doLogin(String userId,String pwd) {
    //后续对接user表验证
    if ("admin".equals(userId) && "123".equals(pwd)) {
      return true;
    }
    if ("spring".equals(userId) && "123".equals(pwd)) {
      return true;
    }
    if ("gateway".equals(userId) && "123".equals(pwd)) {
      return true;
    }
    return false;
  }
  /**
   * 登出接口
   */
  /**
   * 刷新token的接口
   * 在刷新前校验userId和token是否匹配
   */
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

Java实现序列化与反序列化的简单示例

序列化与反序列化是指Java对象与字节序列的相互转换,一般在保存或传输字节序列的时候会用到,下面有两个Java实现序列化与反序列化的简单示例,不过还是先来看看序列和反序列化的具体概念:
收藏 0 赞 0 分享

以Java代码为例讲解设计模式中的简单工厂模式

简单来说,工厂模式就是按照需求来返回一个类型的对象,使用工厂模式的意义就是,如果对象的实例化与代码依赖太大的话,不方便进行扩展和维护,使用工厂的目的就是使对象的实例化与主程序代码就行解耦.来具体看一下:
收藏 0 赞 0 分享

Java线程池的几种实现方法及常见问题解答

下面小编就为大家带来一篇Java线程池的几种实现方法及常见问题解答。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Java线程池的几种实现方法和区别介绍

下面小编就为大家带来一篇Java线程池的几种实现方法和区别。小编觉得挺不错的,现在分享给大家,也给大家做个参考,一起跟随小编过来看看吧,祝大家游戏愉快哦
收藏 0 赞 0 分享

深入理解Java 对象和类

下面小编就为大家带来一篇深入理解Java 对象和类。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

浅析Java编程中类和对象的定义

下面小编就为大家带来一篇浅析Java编程中类和对象的定义。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧,祝大家游戏愉快哦
收藏 0 赞 0 分享

SpringMVC文件上传的配置实例详解

本文通过实例代码给大家介绍SpringMVC文件上传的配置相关内容,本文介绍的非常详细,具有参考借鉴价值,感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

java发送http请求并获取状态码的简单实例

下面小编就为大家带来一篇java发送http请求并获取状态码的简单实例。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

详解Java中格式化日期的DateFormat与SimpleDateFormat类

DateFormat其本身是一个抽象类,SimpleDateFormat 类是DateFormat类的子类,一般情况下来讲DateFormat类很少会直接使用,而都使用SimpleDateFormat类完成,下面我们具体来看一下两个类的用法:
收藏 0 赞 0 分享

Java使用设计模式中的工厂方法模式实例解析

当系统准备为用户提供某个类的子类的实例,又不想让用户代码和该子类形成耦合时,就可以使用工厂方法模式来设计系统.工厂方法模式的关键是在一个接口或抽象类中定义一个抽象方法,下面我们会具体介绍Java使用设计模式中的工厂方法模式实例解析.
收藏 0 赞 0 分享
查看更多