SpringCloud Gateway网关为认证中心和用户微服务构建统一的认证授权入口
本文主要内容是通过构建一个网关微服务,作为统一的认证授权和访问入口。
·
本文主要内容是通过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
# 配置 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容器中。
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;
}
}
测试验证
登录:
获取当前登录用户信息:
退出登录:
本文内容到此结束了,
如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。
如有错误❌疑问💬欢迎各位指出。
主页:共饮一杯无的博客汇总👨💻保持热爱,奔赴下一场山海。🏃🏃🏃
更多推荐
已为社区贡献2条内容
所有评论(0)