手动搭建gateway,项目集成gateway实现Token效果

目录

背景

现在想要进行token校验,故引入gateway服务。

首先阅读官网,知道概念:Gateway官网详解

步骤

1、首先创建springboot项目

看整体的目录结构

2、引入依赖

c 复制代码
 <dependencies>
        <!--gateway的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--nacos注册与发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--nacos配置中心来做配置管理-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <dependency>
            <groupId>com.tfjybj</groupId>
            <artifactId>fewCode-common-redis</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>


    </dependencies>

3、配置文件!!!!!(超级重要!!!根据自己的需要进行配置)

c 复制代码
server:
  port: 8088
  servlet:
    context-path: /api

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: login_route
          uri: lb://fewCode-provider-login
          #uri: http://localhost:8001/
          predicates:
            - Path=/login/**,/Curriculum/**,/classes/**
          filters:
            - RewritePath=/(?<segment>.*),/$\{segment}
leyou:
  filter:  #需要进行过滤的白名单
    allowPaths:
      - /login/checkLogin
      - /login/user
      - /login/userLogin
      - /upload/file
    tokenExpire: 1440

这份配置是用于Spring Cloud Gateway的配置文件,用于构建API网关。让我来解释一下每个部分的含义:

服务器配置:

服务器监听端口为8088。

Servlet的上下文路径设置为"/api",意味着所有API的端点都将具有这个前缀。

Spring Cloud Gateway配置:

服务发现定位器:启用,这意味着网关将使用服务发现来查找后端服务的路由。在动态的微服务环境中非常有用。

路由:路由配置指定了网关如何将传入的请求路由到后端服务。在这里,定义了一个路由:

id: login_route - 这是该路由的唯一标识符。

uri: lb://fewCode-provider-login - 后端服务的URI,用于处理具有负载均衡器(lb)的请求。服务名为"fewCode-provider-login",网关将使用服务发现来定位该服务。

predicates: 定义了应用此路由的条件。在这里,该路由将用于以"/login/"、"/Curriculum/"或"/classes/"开头的路径的请求。

filters: 对请求应用的过滤器,在将其转发到后端服务之前。在这里,使用了一个RewritePath过滤器,用于删除路径末尾的斜杠。

自定义配置:

leyou:这似乎是一些与"leyou"相关的自定义配置,用于特定的过滤逻辑。

filter:这是一个允许白名单路径的配置。

allowPaths:列出的路径将无需任何额外过滤而被允许。这些路径的请求不会受到任何额外的检查。

tokenExpire:设置令牌过期的时间限制(以分钟为单位),这里设置为1440分钟(24小时)。

总体而言,这份配置将Spring Cloud Gateway配置成将传入的请求路由到名为"fewCode-provider-login"的后端服务,前提是请求路径以"/login/"、"/Curriculum/"或"/classes/"开头。同时,它提供了一个白名单,允许无需任何额外过滤的路径。

4、相关类

c 复制代码
package com.tfjybj.gateway.config;


import com.tfjybj.gateway.filter.AuthorizeFilter;
import com.tfjybj.gateway.filter.RenewFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class FilterConfig {

    @Bean
    public AuthorizeFilter authGatewayFilter() {
        //配置拦截器参数
        //order:序号,设置拦截器执行顺序,AuthGatewayFilter为1
        return new AuthorizeFilter(0);
    }

    @Bean
    public RenewFilter renewFilter(){
        // 续约Token
        return new RenewFilter(5);
    }



}
c 复制代码
package com.tfjybj.gateway.config;

import com.tfjybj.gateway.util.FilterProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 请求白名单
 *

 */
@Component
public class IgnorePath {

    @Autowired
    private FilterProperties filterProperties;

    public boolean isAllowPath(String path) {
        //遍历白名单
        for (String allowPath : filterProperties.getAllowPaths()) {
            //判断是否允许
            if(path.startsWith(allowPath)){
                return true;
            }
        }
        return  false;
    }

}
c 复制代码
package com.tfjybj.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class LapCorsConfiguration {
    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();

        //1、配置跨域
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}
c 复制代码
package com.tfjybj.gateway.filter;

import com.alibaba.fastjson.JSON;

import com.tfjybj.gateway.config.IgnorePath;
import com.tfjybj.gateway.result.ResultMsgEnum;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;


public class AuthorizeFilter implements GlobalFilter, Ordered {

    private static final Logger log = LogManager.getLogger();

    @Autowired
    private StringRedisTemplate redisTemplate;


    @Autowired
    private IgnorePath ignorePath;

    private final int order;

    public AuthorizeFilter(int order) {
        this.order = order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();

        //获取请求的url路径
        String path = request.getURI().getPath();
        boolean flag=ignorePath.isAllowPath(path);
        if (flag) {
            log.info("请求在白名单中,metaverse.filter: {}",path);
            return chain.filter(exchange);
        } else {

            //获取token值
            String authorization = headers.getFirst("Authorization");
            log.info("Authorization值{}", authorization);
            authorization = authorization.split("Bearer ")[1];
            //判断redis中是否有token
            Boolean aBoolean = redisTemplate.hasKey("fewCode:userinfo:" + authorization);
            if (aBoolean){
                return chain.filter(exchange);
            }else {
                //声明变量
                ServerHttpResponse response = exchange.getResponse();
                HashMap map = new HashMap();
                String resp;
                DataBuffer bodyDataBuffer;

                //设置响应头
                response.setStatusCode(HttpStatus.FORBIDDEN);
                map.put("code", "403");
                map.put("message", ResultMsgEnum.AUTH_FAILED.getMsg());
                resp = JSON.toJSONString(map);
                bodyDataBuffer = response.bufferFactory().wrap(resp.getBytes());
                response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
                return response.writeWith(Mono.just(bodyDataBuffer));

            }


        }
    }


    @Override
    public int getOrder() {
        return this.order;
    }

}
c 复制代码
package com.tfjybj.gateway.filter;

import com.tfjybj.gateway.config.IgnorePath;
import com.tfjybj.gateway.util.FilterProperties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.concurrent.TimeUnit;

/**
 * token续约的逻辑
 *

 */
public class RenewFilter implements GlobalFilter, Ordered {


    private static final Logger log = LogManager.getLogger();

    private final int order;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public RenewFilter(int order) {
        this.order = order;
    }

    @Autowired
    private IgnorePath ignorePath;

    @Autowired
    private FilterProperties filterProperties;

    @Override
    public int getOrder() {
        return this.order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();

        //获取请求的url路径
        String path = request.getURI().getPath();
        boolean flag=ignorePath.isAllowPath(path);
        if (flag) {
            log.info("请求在白名单中,metaverse.filter: {}", path);
            return chain.filter(exchange);
        }
        //token值
        String authorization = headers.getFirst("Authorization");
        authorization = authorization.split("Bearer ")[1];
        log.info("Authorization值{}", authorization);


        //TOKEN续活


        //解析TOKEN


        //根据uuid,延长用户信息
        String uuid = authorization;
        String key = "fewCode:userinfo:" + uuid;
        stringRedisTemplate.expire(key, filterProperties.getTokenExpire(), TimeUnit.MINUTES);


        return chain.filter(exchange);

    }



}
c 复制代码
package com.tfjybj.gateway.result;

public enum ResultMsgEnum {

    FIND_SUCCESS("查询成功!"),
    FIND_FAIL("查询失败!"),

    UPDATE_SUCCESS("更新成功"),
    UPDATE_FAIL("更新失败"),

    DELETE_SUCCESS("删除成功"),
    DELETE_FAIL("删除失败"),

    SEND_SUCCESS("发送成功"),
    SEND_FAIL("发送失败"),

    EXECUTE_SUCCESS("执行成功!"),
    EXECUTE_FAIL("执行失败!"),

    AUTH_FAILED("权限认证失败"),
    AUTH_SUCCESS("权限认证成功");

    private final String msg;

    ResultMsgEnum(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}
c 复制代码
package com.tfjybj.gateway.util;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.util.List;


@Component
@RefreshScope
@ConfigurationProperties(prefix = "leyou.filter")
public class FilterProperties {
    public void setAllowPaths(List<String> allowPaths) {
        this.allowPaths = allowPaths;
    }

    public List<String> getAllowPaths() {
        return allowPaths;
    }

    private List<String> allowPaths;

    /**
     * token过期时间
     */
    private Integer tokenExpire;

    public Integer getTokenExpire() {
        return tokenExpire;
    }

    public void setTokenExpire(Integer tokenExpire) {
        this.tokenExpire = tokenExpire;
    }
}
c 复制代码
package com.tfjybj.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
//@EnableDiscoveryClient

public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }

}

我们在服务中进行的白名单中接口的操作如下

(主要是根据传过来的信息生成token,然后以键值对的形式存入redis中,方便后续通过redis根据token拿人的信息):

c 复制代码
RestController
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private Actor actorInfo;

    @ApiOperation("学生登录验证")
    @RequestMapping(value="checkLogin",method= RequestMethod.POST)
    @Transactional(rollbackFor = Exception.class)
    public Actor checkLoginInfo(@RequestBody Actor actor){
        return actorInfo.notifyStudentCheckLoginInfo(actor);
    }
}
c 复制代码
public Actor notifyStudentCheckLoginInfo(Actor student){
        Actor actor;
        for (Actor studentInfo:allStudent){
            actor=studentInfo.checkLoginInfo(student);
            if (!ObjectUtils.isEmpty(actor)){
                //生成UUID
                String uuid = CreateUUID.createUUID();
                //存入redis
                saveRedis(uuid, actor);
                //生成token,封装到请求头
                putHeader(uuid);
                return actor;
            }
        }
        return null;
    }
c 复制代码
public class CreateUUID {
    public CreateUUID() {
    }

    public static String createUUID() {
        String preUuid = UUID.randomUUID().toString();
        String newUUID = preUuid.replace("-", "");
        return newUUID;
    }
}
c 复制代码
   private void saveRedis(String uuid, Object userInfo) {
        //拼接key,user信息序列化,存入redis,过期时间在nacos中设置
        String key = "fewCode:userinfo:" + uuid;
        String userJson = JSONObject.toJSONString(userInfo);
        redisTemplate.opsForValue().set(key, userJson);
        redisTemplate.expire(key, 1440, TimeUnit.MINUTES);
    }
c 复制代码
   private void putHeader(String token) {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = sra.getResponse();
        response.setHeader("Authorization", token);
    }

测试

存:

将断点打到这里,可以观察到我们要请求的服务IP+端口号还有url地址,如下

相当手动访问

调通后存入redis中如下

拿:

然后拿着根据token获取信息

c 复制代码
package com.tfjybj.login.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Service
public class AnalysisTokenService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 获取当前登陆人id
     *
     * @return
     */
    public String getUserId() {
        JSONObject userData = getUserData();
        return userData.get("id").toString();
    }

    /**
     * 获取用户code
     *
     * @return
     */
    public String getUserAccount() {
        JSONObject userData = getUserData();
        return userData.get("account").toString();
    }

    /**
     * 获取当前登陆人name
     *
     * @return
     */
    public String getUserName() {
        JSONObject userData = getUserData();
        return userData.get("name").toString();

    }

    public JSONObject getUserData() {
        //从请求头获取token
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String token = sra.getRequest().getHeader("Authorization");
        token = token.split("Bearer ")[1];
        //解析token
        JSONObject userJson = this.analysisToken(token);
        return userJson;
    }


    public JSONObject analysisToken  (String token){

        //解析token
        String key= "fewCode:userinfo:"+token;
        String userInfoStr = redisTemplate.opsForValue().get(key);
        JSONObject userJson = JSONObject.parseObject(userInfoStr);
//        String data = jsonObject.get("data").toString();
//        JSONObject userJson = JSONObject.parseObject(data);
        return userJson;

    }

}

总结

1、搞懂gateway是干嘛的

2、知道配置文件中各个参数是什么

相关推荐
Java后端的Ai之路20 小时前
【Spring全家桶】-一文弄懂Spring Cloud Gateway
java·后端·spring cloud·gateway
研究司马懿5 天前
【云原生】Gateway API介绍
云原生·gateway
研究司马懿5 天前
【云原生】Gateway API路由、重定向、修饰符等关键操作
云原生·gateway
研究司马懿5 天前
【云原生】初识Gateway API
云原生·gateway
七夜zippoe6 天前
API网关设计模式实战 Spring Cloud Gateway路由过滤限流深度解析
java·设计模式·gateway·路由·api网关
汪碧康6 天前
一文讲解kubernetes的gateway Api的功能、架构、部署、管理及使用
云原生·容器·架构·kubernetes·gateway·kubelet·xkube
大佐不会说日语~6 天前
Docker Compose 部署 Spring Boot 应用 502 Bad Gateway 问题排查与解决
spring boot·docker·gateway·maven·故障排查
Dontla8 天前
Kubernetes流量管理双雄:Ingress与Gateway API解析(Nginx与Ingress与Gateway API的关系)
nginx·kubernetes·gateway
JavaLearnerZGQ8 天前
Gateway网关将登录用户信息传递给下游微服务(完整实现方案)
微服务·架构·gateway
Ares-Wang9 天前
网络》》BGP Border Gateway Protocol,边界网关协议
网络·gateway