要实现一个基于 OAuth2.1 的统一认证网关,可以按照以下步骤进行操作:

  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>
  1. 配置认证服务器

在 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
  1. 配置网关

在 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
  1. 编写 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 {
    }

}
  1. 编写 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 {
    }

}
  1. 启动应用程序

启动应用程序后,访问 http://localhost:8080/service-A 或 http://localhost:8080/service-B,应该会被重定向到 OAuth2 认证服务器进行认证。认证成功后,网关将转发请求到相应的后端服务,并将 OAuth2 令牌传递给后端服务。在后端服务中,可以通过 SecurityContextHolder.getContext().getAuthentication() 方法获取当前用户的信息。


原文地址: https://www.cveoy.top/t/topic/nl7R 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录