Spring Security 5.6.8 + Spring Cloud 2.6.13 + Spring Cloud Gateway: 实现基于 OAuth2.1 的统一认证网关
要实现一个基于 OAuth2.1 的统一认证网关,可以按照以下步骤进行操作:
- 添加依赖
在 pom.xml 文件中添加 spring-security-oauth2-autoconfigure、spring-cloud-starter-gateway 和 spring-boot-starter-web 依赖:
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.5.5</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.3</version>
</dependency>
- 配置认证服务器
在 application.yml 文件中添加认证服务器的配置:
spring:
security:
oauth2:
client:
registration:
service-A:
client-id: client-A
client-secret: secret-A
authorization-grant-type: authorization_code
redirect-uri: '{$baseUrl}/login/oauth2/code/{registrationId}'
scope: read,write
client-name: Service A
client-alias: service-A
provider: oauth2-provider
provider:
oauth2-provider:
authorization-uri: http://localhost:8080/oauth/authorize
token-uri: http://localhost:8080/oauth/token
user-info-uri: http://localhost:8080/user-info
user-name-attribute: name
- 配置网关
在 application.yml 文件中添加网关的配置:
spring:
cloud:
gateway:
routes:
- id: service-A
uri: lb://service-A
predicates:
- Path=/service-A/**
filters:
- TokenRelay=
- id: service-B
uri: http://localhost:8081
predicates:
- Path=/service-B/**
filters:
- TokenRelay=
global-filters:
- OAuth2GatewayFilterFactory:
resource:
userInfoUri: http://localhost:8080/user-info
- 编写 TokenRelay 过滤器
在网关服务中编写 TokenRelay 过滤器,将请求中的 OAuth2 令牌传递给后端服务:
public class TokenRelayGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenRelayGatewayFilterFactory.Config> {
public TokenRelayGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof OAuth2AuthenticationToken) {
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication.getPrincipal();
String token = oauthToken.getAuthorizedClientRegistrationId() + ' ' + oauthToken.getAccessToken().getTokenValue();
ServerHttpRequest request = exchange.getRequest().mutate()
.header('Authorization', token)
.build();
return chain.filter(exchange.mutate().request(request).build());
}
return chain.filter(exchange);
};
}
public static class Config {
}
}
- 编写 OAuth2GatewayFilterFactory 过滤器
在网关服务中编写 OAuth2GatewayFilterFactory 过滤器,验证 OAuth2 令牌并获取用户信息:
public class OAuth2GatewayFilterFactory extends AbstractGatewayFilterFactory<OAuth2GatewayFilterFactory.Config> {
private final ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService;
public OAuth2GatewayFilterFactory(ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService) {
super(Config.class);
this.userService = userService;
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
return exchange.getPrincipal()
.cast(OAuth2AuthenticationToken.class)
.flatMap(token -> userService.loadUser(token.getAuthorizedClientRegistrationId(), token.getAccessToken().getTokenValue())
.map(user -> {
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()));
return exchange;
}))
.flatMap(chain::filter);
};
}
public static class Config {
}
}
- 启动应用程序
启动应用程序后,访问 http://localhost:8080/service-A 或 http://localhost:8080/service-B,应该会被重定向到 OAuth2 认证服务器进行认证。认证成功后,网关将转发请求到相应的后端服务,并将 OAuth2 令牌传递给后端服务。在后端服务中,可以通过 SecurityContextHolder.getContext().getAuthentication() 方法获取当前用户的信息。
原文地址: https://www.cveoy.top/t/topic/nl7R 著作权归作者所有。请勿转载和采集!