- 原创 | Java 2021 超神之路,很肝~
- 中文详细注释的开源项目
- RPC 框架 Dubbo 源码解析
- 网络应用框架 Netty 源码解析
- 消息中间件 RocketMQ 源码解析
- 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析
- 作业调度中间件 Elastic-Job 源码解析
- 分布式事务中间件 TCC-Transaction 源码解析
- Eureka 和 Hystrix 源码解析
- Java 并发源码
来源:zhanjq.blog.csdn.net/article/details/127574020
本文主要内容是通过SpringCloud Gateway构建一个网关微服务,作为统一的认证授权和访问入口。
配置文件
先引入相关依赖,对应的pom文件内容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>oauth2-demo</artifactId> <groupId>com.zjq</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>ms-gateway</artifactId> <dependencies> <!-- spring cloud gateway --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!-- eureka client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- commons 公共项目 --> <dependency> <groupId>com.zjq</groupId> <artifactId>commons</artifactId> <version>1.0-SNAPSHOT</version> <!-- 和 webflux 冲突 --> <exclusions> <exclusion> <groupId>com.battcn</groupId> <artifactId>swagger-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <!-- 自定义的元数据依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> </project>
网关服务的yml配置内容如下:
server: port: 80 spring: application: name: ms-gateway cloud: gateway: discovery: locator: enabled: true # 开启配置注册中心进行路由功能 lower-case-service-id: true # 将服务名称转小写 routes: - id: ms-users uri: lb://ms-users predicates: - Path=/users/** filters: - StripPrefix=1 - id: ms-oauth2-server uri: lb://ms-oauth2-server predicates: - Path=/auth/** filters: - StripPrefix=1 secure: ignore: urls: # 配置白名单路径 - /actuator/** - /auth/oauth/** - /users/signin > 基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能 > > * 项目地址:<https://github.com/YunaiV/ruoyi-vue-pro> > * 视频教程:<https://doc.iocoder.cn/video/> # 配置 Eureka Server 注册中心 eureka: instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${server.port} client: service-url: defaultZone: http://localhost:7000/eureka/ logging: pattern: console: '%d{HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n'
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
请求白名单配置
加载配置文件中的配置,注入到spring容器中。
secure: ignore: urls: # 配置白名单路径 - /actuator/** - /auth/oauth/** - /users/signin /** * 网关白名单配置 * @author zjq */ @Data @Component @ConfigurationProperties(prefix = "secure.ignore") public class IgnoreUrlsConfig { private List<String> urls; }
异常处理和rest请求配置
异常处理在全局过滤器中会有用到,代码如下:
@Component public class HandleException { @Resource private ObjectMapper objectMapper; public Mono<Void> writeError(ServerWebExchange exchange, String error) { ServerHttpResponse response = exchange.getResponse(); ServerHttpRequest request = exchange.getRequest(); response.setStatusCode(HttpStatus.OK); response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); ResultInfo resultInfo = ResultInfoUtil.buildError(ApiConstant.NO_LOGIN_CODE, ApiConstant.NO_LOGIN_MESSAGE, request.getURI().getPath()); String resultInfoJson = null; DataBuffer buffer = null; try { resultInfoJson = objectMapper.writeValueAsString(resultInfo); buffer = response.bufferFactory().wrap(resultInfoJson.getBytes(Charset.forName("UTF-8"))); } catch (JsonProcessingException ex) { ex.printStackTrace(); } return response.writeWith(Mono.just(buffer)); } }
申请授权和认证过程中需要远程调用其他接口,所以我们引入rest请求配置,代码如下:
/** * REST请求配置 * @author zjq */ @Configuration public class RestTemplateConfiguration { @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
全局过滤器配置
配置好了白名单,我们需要在网关过滤器中使用该白名单配置,放行对应的白名单,网关过滤器需要实现全局过滤器接口org.springframework.cloud.gateway.filter.GlobalFilter
和过滤器顺序接口org.springframework.core.Ordered
相关代码如下:
/** * 网关全局过滤器 * @author zjq */ @Component public class AuthGlobalFilter implements GlobalFilter, Ordered { @Resource private IgnoreUrlsConfig ignoreUrlsConfig; @Resource private RestTemplate restTemplate; @Resource private HandleException handleException; /** * 身份校验处理 * * @param exchange * @param chain * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 判断当前的请求是否在白名单中 AntPathMatcher pathMatcher = new AntPathMatcher(); boolean flag = false; String path = exchange.getRequest().getURI().getPath(); for (String url : ignoreUrlsConfig.getUrls()) { if (pathMatcher.match(url, path)) { flag = true; break; } } // 白名单放行 if (flag) { return chain.filter(exchange); } // 获取 access_token String access_token = exchange.getRequest().getQueryParams().getFirst("access_token"); // 判断 access_token 是否为空 if (StringUtils.isBlank(access_token)) { return handleException.writeError(exchange, "请登录"); } // 校验 token 是否有效 String checkTokenUrl = "http://ms-oauth2-server/oauth/check_token?token=".concat(access_token); try { // 发送远程请求,验证 token ResponseEntity<String> entity = restTemplate.getForEntity(checkTokenUrl, String.class); // token 无效的业务逻辑处理 if (entity.getStatusCode() != HttpStatus.OK) { return handleException.writeError(exchange, "Token was not recognised, token: ".concat(access_token)); } if (StringUtils.isBlank(entity.getBody())) { return handleException.writeError(exchange, "This token is invalid: ".concat(access_token)); } } catch (Exception e) { return handleException.writeError(exchange, "Token was not recognised, token: ".concat(access_token)); } // 放行 return chain.filter(exchange); } /** * 网关过滤器的排序,数字越小优先级越高 * * @return */ @Override public int getOrder() { return 0; } }
测试验证
登录:
获取当前登录用户信息:
退出登录: