目录
- 背景
- 步骤
- 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: 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、相关类
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);
}
}
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;
}
}
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);
}
}
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;
}
}
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);
}
}
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
//@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
我们在服务中进行的白名单中接口的操作如下
(主要是根据传过来的信息生成token,然后以键值对的形式存入redis中,方便后续通过redis根据token拿人的信息):
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);
}
}
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;
}
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 {
@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、知道配置文件中各个参数是什么