手动搭建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、知道配置文件中各个参数是什么

相关推荐
Yengi6 小时前
【test】google cloud
cloud
大灰狼19131 天前
【基于spring-cloud-gateway实现自己的网关过滤器】
spring cloud·gateway
wangqiaowq2 天前
Gateway和VirtualService
gateway
世俗ˊ2 天前
微服务-- Gateway服务网关
java·微服务·gateway
她又在丛中笑3 天前
SpringBoot gateway如何支持跨域?
java·spring boot·gateway
为java添砖加瓦5 天前
【Oauth2整合gateway网关实现微服务单点登录】
spring boot·spring·spring cloud·微服务·架构·gateway
顾以沫5 天前
微服务--Gateway网关
微服务·架构·gateway
秋窗76 天前
Nginx反向代理出现502 Bad Gateway问题的解决方案
运维·nginx·gateway
Sunny_yiyi8 天前
Gateway--服务网关
java·开发语言·gateway
珍珠是蚌的眼泪9 天前
微服务_入门2
网关·微服务·gateway·远程调用·feign