目录
- 背景
- 步骤
- 1、首先创建springboot项目
- 2、引入依赖
- 3、配置文件!!!!!(超级重要!!!根据自己的需要进行配置)
- 4、相关类
- 我们在服务中进行的白名单中接口的操作如下
- 测试
- 存:
- 拿:
- 总结
背景
现在想要进行token校验,故引入gateway服务。
首先阅读官网,知道概念:Gateway官网详解
步骤
1、首先创建springboot项目
看整体的目录结构
2、引入依赖
<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、配置文件!!!!!(超级重要!!!根据自己的需要进行配置)
server:port: 8088servlet:context-path: /apispring:cloud:gateway:discovery:locator:enabled: trueroutes:- id: login_routeuri: 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/filetokenExpire: 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、相关类
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 {@Beanpublic AuthorizeFilter authGatewayFilter() {//配置拦截器参数//order:序号,设置拦截器执行顺序,AuthGatewayFilter为1return new AuthorizeFilter(0);}@Beanpublic RenewFilter renewFilter(){// 续约Tokenreturn new RenewFilter(5);}}
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 {@Autowiredprivate FilterProperties filterProperties;public boolean isAllowPath(String path) {//遍历白名单for (String allowPath : filterProperties.getAllowPaths()) {//判断是否允许if(path.startsWith(allowPath)){return true;}}return false;}}
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 {@Beanpublic 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);}
}
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();@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate IgnorePath ignorePath;private final int order;public AuthorizeFilter(int order) {this.order = order;}@Overridepublic 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中是否有tokenBoolean 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));}}}@Overridepublic int getOrder() {return this.order;}}
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;@Autowiredprivate StringRedisTemplate stringRedisTemplate;public RenewFilter(int order) {this.order = order;}@Autowiredprivate IgnorePath ignorePath;@Autowiredprivate FilterProperties filterProperties;@Overridepublic int getOrder() {return this.order;}@Overridepublic 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);}}
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;}
}
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;}
}
package com.tfjybj.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
//@EnableDiscoveryClientpublic class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}}
我们在服务中进行的白名单中接口的操作如下
(主要是根据传过来的信息生成token,然后以键值对的形式存入redis中,方便后续通过redis根据token拿人的信息):
RestController
@RequestMapping("/login")
public class LoginController {@Autowiredprivate Actor actorInfo;@ApiOperation("学生登录验证")@RequestMapping(value="checkLogin",method= RequestMethod.POST)@Transactional(rollbackFor = Exception.class)public Actor checkLoginInfo(@RequestBody Actor actor){return actorInfo.notifyStudentCheckLoginInfo(actor);}
}
public Actor notifyStudentCheckLoginInfo(Actor student){Actor actor;for (Actor studentInfo:allStudent){actor=studentInfo.checkLoginInfo(student);if (!ObjectUtils.isEmpty(actor)){//生成UUIDString uuid = CreateUUID.createUUID();//存入redissaveRedis(uuid, actor);//生成token,封装到请求头putHeader(uuid);return actor;}}return null;}
public class CreateUUID {public CreateUUID() {}public static String createUUID() {String preUuid = UUID.randomUUID().toString();String newUUID = preUuid.replace("-", "");return newUUID;}
}
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);}
private void putHeader(String token) {ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletResponse response = sra.getResponse();response.setHeader("Authorization", token);}
测试
存:
将断点打到这里,可以观察到我们要请求的服务IP+端口号还有url地址,如下
相当手动访问
调通后存入redis中如下
拿:
然后拿着根据token获取信息
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 {@Autowiredprivate 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() {//从请求头获取tokenServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();String token = sra.getRequest().getHeader("Authorization");token = token.split("Bearer ")[1];//解析tokenJSONObject userJson = this.analysisToken(token);return userJson;}public JSONObject analysisToken (String token){//解析tokenString 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、知道配置文件中各个参数是什么