Spring Authorization Server入门 (十六) Spring Cloud Gateway对接认证服务

这篇具有很好参考价值的文章主要介绍了Spring Authorization Server入门 (十六) Spring Cloud Gateway对接认证服务。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

        之前虽然单独讲过Security Client和Resource Server的对接,但是都是基于Spring webmvc的,Gateway这种非阻塞式的网关是基于webflux的,对于集成Security相关内容略有不同,且涉及到代理其它微服务,所以会稍微比较麻烦些,今天就带大家来实现Gateway网关对接OAuth2认证服务。

Gateway对接说明

身份问题

        在本次示例中网关既是客户端(OAuth2 Client Server)又是资源服务(OAuth2 Resource Server),Client服务负责认证,Resource负责鉴权,这样如果有在浏览器直接访问网关的需要可以直接在浏览器由框架引导完成OAuth2认证过程。

框架版本与架构说明

架构图

spring cloud 微服务.jpg

Spring Cloud依赖版本

框架 版本号
Spring Boot 3.1.0
Nacos Server 2.2.1
Spring Cloud 2022.0.4
Spring Cloud Alibaba 2022.0.0.0
Spring Security 6.1.0
Spring OAuth2 Client 6.1.0
Spring OAuth2 Resource Server 6.1.0

读者可以自选版本使用,作为对接方版本问题不大;不确定Spring Cloud Alibaba 在部署时会不会有Spring Boot的版本限制,如果3.1.x无法使用请降级至3.0.10版本,开发时测试都是没问题的。

网关集成认证服务请求流程图说明

Spring Cloud Gateway对接OAuth2认证服务.jpg

  1. 用户请求受限资源
  2. 网关检测没有认证信息,通过RedirectServerAuthenticationEntryPoint处理并发起OAuth2登录授权申请
  3. 授权申请到达认证服务,认证服务检测到未登录重定向至登录页面并展示给用户
  4. 用户登录成功后请求重定向至授权申请接口,通过校验后携带Token重定向至回调地址(redirect_uri),注意:这里回调地址要设置为网关的地址,htttp://{网关ip}:{网关port}/login/oauth2/code/{registrationId},后边的/login/oauth2/code/{registrationId}路径是固定的,这是框架(Security OAuth2 Client)自带的端点
  5. 请求到达网关,由OAuth2LoginAuthenticationWebFilter拦截并调用父类AuthenticationWebFilterfilter方法进行处理
  6. AuthenticationWebFilter调用OidcAuthorizationCodeReactiveAuthenticationManagerOAuth2LoginReactiveAuthenticationManager类处理(由授权申请的scope决定,包含openid就走OidcAuthorizationCodeReactiveAuthenticationManager,否则走另一个)
  7. 在获取AccessToken成功以后调用ReactiveOAuth2UserService获取用户信息
  8. 获取到用户信息后会解析并将认证信息保存至ReactiveSecurityContextHolder
  9. 完成这一系列的认证之后会重定向至最一开始请求的受限资源,这时候就能获取到认证信息了
  10. 如果访问的是被网关代理的服务则会通过令牌中继(TokenRelay)携带token访问

这就是网关通过认证服务获取认证信息的一个流程,基本上只需要添加配置文件即可由框架引导进行OAuth2认证流程。

开始编码

前置条件

  1. 搭建好标准OAuth2认证服务
  2. 搭建nacos服务

项目结构

项目结构

gateway-example # 父模块
 │  
 ├─gateway-client-example # 网关
 │  
 ├─normal-resource-example # webmvc资源服务
 │  
 ├─webflux-resource-example # webflux资源服务
 │  
 └─pom.xml # 公共依赖,依赖管理
 

创建一个空的maven项目

引入Spring Boot、Spring Cloud、Spring Cloud Alibaba,如下

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>gateway-example</artifactId>
    <version>0.0.1</version>
    <packaging>pom</packaging>
    <name>gateway-example</name>
    <description>gateway-example</description>
    <modules>
        <module>gateway-client-example</module>
        <module>normal-resource-example</module>
        <module>webflux-resource-example</module>
    </modules>

    <properties>
        <java.version>17</java.version>
        <!-- 修复漏洞 -->
        <snakeyaml.version>2.0</snakeyaml.version>
        <!-- Spring Cloud版本号 -->
        <spring-cloud.version>2022.0.4</spring-cloud.version>
        <!-- Spring Cloud Alibaba版本号 -->
        <spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
    </properties>

    <dependencies>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- Spring Boot 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- 服务注册与发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- 配置中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <!-- 资源服务器starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

里边的modules标签是在新建module时自动添加的

创建网关gateway-client-example模块

创建gateway-client-example模块

Spring Cloud 相关依赖已经在parent模块中引入,所以该模块只需要引入Gateway、Client依赖,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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>gateway-example</artifactId>
        <version>0.0.1</version>
    </parent>

    <artifactId>gateway-client-example</artifactId>
    <name>gateway-client-example</name>
    <description>gateway-client-example</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- 负载均衡依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

编写客户端配置

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * 客户端配置
 *
 * @author vains
 */
@Configuration
public class ClientServerConfig {

    /**
     * 解析用户权限信息(当在浏览器中直接访问接口,框架自动调用OIDC流程登录时会用到该配置)
     *
     * @return GrantedAuthoritiesMapper
     */
    @Bean
    public GrantedAuthoritiesMapper userAuthoritiesMapper() {
        return (authorities) -> {
            Set<GrantedAuthority> mappedAuthorities = new HashSet<>();

            authorities.forEach(authority -> {
                if (authority instanceof OAuth2UserAuthority oAuth2UserAuthority) {
                    // 从认证服务获取的用户信息中提取权限信息
                    Object userAuthorities = oAuth2UserAuthority.getAttributes().get("authorities");
                    if (userAuthorities instanceof Collection<?> collection) {
                        // 转为SimpleGrantedAuthority的实例并插入mappedAuthorities中
                        collection.stream().filter(a -> a instanceof String)
                                .map(String::valueOf)
                                .map(SimpleGrantedAuthority::new)
                                .forEach(mappedAuthorities::add);
                    }
                }
            });

            return mappedAuthorities;
        };
    }


}

该配置会在获取到用户信息后解析用户的权限信息,详见文档

编写网关资源服务配置

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

/**
 * 资源服务器配置
 *
 * @author vains
 */
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class ResourceServerConfig {

    /**
     * 配置认证相关的过滤器链
     *
     * @param http Spring Security的核心配置类
     * @return 过滤器链
     */
    @Bean
    public SecurityWebFilterChain defaultSecurityFilterChain(ServerHttpSecurity http) {
        // 禁用csrf与cors
        http.csrf(ServerHttpSecurity.CsrfSpec::disable);
        http.cors(ServerHttpSecurity.CorsSpec::disable);

        // 开启全局验证
        http.authorizeExchange((authorize) -> authorize
                //全部需要认证
                .anyExchange().authenticated()
        );

        // 开启OAuth2登录
        http.oauth2Login(Customizer.withDefaults());

        // 设置当前服务为资源服务,解析请求头中的token
        http.oauth2ResourceServer((resourceServer) -> resourceServer
                        // 使用jwt
                        .jwt(jwt -> jwt
                                // 请求中携带token访问时会触发该解析器适配器
                                .jwtAuthenticationConverter(grantedAuthoritiesExtractor())
                        )
                /*
                // xhr请求未携带Token处理
                .authenticationEntryPoint(this::authenticationEntryPoint)
                // 权限不足处理
                .accessDeniedHandler(this::accessDeniedHandler)
                // Token解析失败处理
                .authenticationFailureHandler(this::failureHandler)
                */
        );

        return http.build();
    }

    /**
     * 自定义jwt解析器,设置解析出来的权限信息的前缀与在jwt中的key
     *
     * @return jwt解析器适配器 ReactiveJwtAuthenticationConverterAdapter
     */
    public Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        // 设置解析权限信息的前缀,设置为空是去掉前缀
        grantedAuthoritiesConverter.setAuthorityPrefix("");
        // 设置权限信息在jwt claims中的key
        grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }

}

需要注意的是开启方法级别鉴权的注解变了,webflux的注解和webmvc的注解不一样,并且过滤器链也换成SecurityWebFilterChain了;jwt自定义解析器的方式也不一致,实现方式见上方代码

说明文档如下

EnableReactiveMethodSecurity注解文档

Jwt解析器适配器详见文档

编写application.yml,添加nacos配置

spring:
  cloud:
    nacos:
      serverAddr: 127.0.0.1:8848
  config:
    import:
      - nacos:gateway.yml?refresh=true
  application:
    name: gateway

在nacos中创建gateway.yml配置文件

添加客户端与资源服务配置,并添加其它资源服务的代理配置

server:
  port: 7000
spring:
  security:
    oauth2:
      # 资源服务器配置
      resourceserver:
        jwt:
          # Jwt中claims的iss属性,也就是jwt的签发地址,即认证服务器的根路径
          # 资源服务器会进一步的配置,通过该地址获取公钥以解析jwt
          issuer-uri: http://192.168.119.1:8080
      client:
        provider:
          # 认证提供者,自定义名称
          custom-issuer:
            # Token签发地址(认证服务地址)
            issuer-uri: http://192.168.119.1:8080
            # 获取用户信息的地址,默认的/userinfo端点需要IdToken获取,为避免麻烦自定一个用户信息接口
            user-info-uri: ${spring.security.oauth2.client.provider.custom-issuer.issuer-uri}/user
            user-name-attribute: name
        registration:
          messaging-client-oidc:
            # oauth认证提供者配置,和上边配置的认证提供者关联起来
            provider: custom-issuer
            # 客户端名称,自定义
            client-name: gateway
            # 客户端id,从认证服务申请的客户端id
            client-id: messaging-client
            # 客户端秘钥
            client-secret: 123456
            # 客户端认证方式
            client-authentication-method: client_secret_basic
            # 获取Token使用的授权流程
            authorization-grant-type: authorization_code
            # 回调地址,这里设置为Spring Security Client默认实现使用code换取token的接口,当前服务(gateway网关)的地址
            redirect-uri: http://127.0.0.1:7000/login/oauth2/code/messaging-client-oidc
            scope:
              - message.read
              - message.write
              - openid
              - profile

  cloud:
    gateway:
      default-filters:
        # 令牌中继
        - TokenRelay=
        # 代理路径,代理至服务后会去除第一个路径的内容
        - StripPrefix=1
      routes:
        # 资源服务代理配置
        - id: resource
          uri: lb://resource
          predicates:
            - Path=/resource/**
        # 资源服务代理配置
        - id: webflux
          uri: lb://webflux-resource
          predicates:
            - Path=/webflux/**

注意:配置文件中令牌中继(TokenRelay)的配置就是添加一个filterTokenRelay=; 当网关引入spring-boot-starter-oauth2-client依赖并设置spring.security.oauth2.client.*属性时,会自动创建一个TokenRelayGatewayFilterFactory过滤器,它会从认证信息中获取access token,并放入下游请求的请求头中。 详见Gateway关于TokenRelay的文档

项目结构

网关项目结构

创建webmvc资源服务模块normal-resource-example

在pom.xml中添加web依赖,如下

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>gateway-example</artifactId>
        <version>0.0.1</version>
    </parent>

    <artifactId>normal-resource-example</artifactId>
    <name>normal-resource-example</name>
    <description>normal-resource-example</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

创建资源服务器配置,添加自定义jwt解析器

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;

/**
 * 资源服务器配置
 *
 * @author vains
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(jsr250Enabled = true, securedEnabled = true)
public class ResourceServerConfig {

    /**
     * 自定义jwt解析器,设置解析出来的权限信息的前缀与在jwt中的key
     *
     * @return jwt解析器 JwtAuthenticationConverter
     */
    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        // 设置解析权限信息的前缀,设置为空是去掉前缀
        grantedAuthoritiesConverter.setAuthorityPrefix("");
        // 设置权限信息在jwt claims中的key
        grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }

}

编写application.yml添加nacos配置

spring:
  cloud:
    nacos:
      serverAddr: 127.0.0.1:8848
  config:
    import:
      - nacos:resource.yml?refresh=true
  application:
    name: resource

在nacos中创建resource.yml配置文件,添加资源服务配置

server:
  port: 7100

spring:
  security:
    oauth2:
      # 资源服务器配置
      resourceserver:
        jwt:
          # Jwt中claims的iss属性,也就是jwt的签发地址,即认证服务器的根路径
          # 资源服务器会进一步的配置,通过该地址获取公钥以解析jwt
          issuer-uri: http://192.168.119.1:8080

注意端口,不能与网关和认证服务重复

模块结构

normal-resource-example结构

创建webflux资源服务模块

pom.xml添加webflux依赖,如下

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>gateway-example</artifactId>
        <version>0.0.1</version>
    </parent>

    <artifactId>webflux-resource-example</artifactId>
    <name>webflux-resource-example</name>
    <description>webflux-resource-example</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

创建资源服务配置并且添加jwt解析器适配器

跟网关的资源服务配置差不多,如下

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

/**
 * 资源服务器配置
 *
 * @author vains
 */
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class ResourceServerConfig {

    /**
     * 配置认证相关的过滤器链
     *
     * @param http Spring Security的核心配置类
     * @return 过滤器链
     */
    @Bean
    public SecurityWebFilterChain defaultSecurityFilterChain(ServerHttpSecurity http) {
        // 禁用csrf与cors
        http.csrf(ServerHttpSecurity.CsrfSpec::disable);
        http.cors(ServerHttpSecurity.CorsSpec::disable);

        // 开启全局验证
        http.authorizeExchange((authorize) -> authorize
                //全部需要认证
                .anyExchange().authenticated()
        );

        // 设置当前服务为资源服务,解析请求头中的token
        http.oauth2ResourceServer((resourceServer) -> resourceServer
                // 使用jwt
                .jwt(jwtSpec -> jwtSpec
                        // 设置jwt解析器适配器
                        .jwtAuthenticationConverter(grantedAuthoritiesExtractor())
                )
        );
        return http.build();
    }

    /**
     * 自定义jwt解析器,设置解析出来的权限信息的前缀与在jwt中的key
     *
     * @return jwt解析器适配器 ReactiveJwtAuthenticationConverterAdapter
     */
    public Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        // 设置解析权限信息的前缀,设置为空是去掉前缀
        grantedAuthoritiesConverter.setAuthorityPrefix("");
        // 设置权限信息在jwt claims中的key
        grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }

}

编写application.yml添加nacos配置

spring:
  cloud:
    nacos:
      serverAddr: 127.0.0.1:8848
  config:
    import:
      - nacos:webflux.yml?refresh=true
  application:
    name: webflux-resource

nacos中添加webflux.yml配置文件并添加资源服务配置

server:
  port: 7200

spring:
  security:
    oauth2:
      # 资源服务器配置
      resourceserver:
        jwt:
          # Jwt中claims的iss属性,也就是jwt的签发地址,即认证服务器的根路径
          # 资源服务器会进一步的配置,通过该地址获取公钥以解析jwt
          issuer-uri: http://192.168.119.1:8080

与webmvc的资源服务的配置是一样的,注意端口不能与其它服务端口冲突

模块结构

webflux-resource-example模块结构

在三个模块中添加测试类,一式三份

webmvc测试接口

package com.example.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试接口
 *
 * @author vains
 */
@RestController
public class TestController {

    @GetMapping("/test01")
    @PreAuthorize("hasAnyAuthority('message.write')")
    public String test01() {
        return "test01";
    }

    @GetMapping("/test02")
    @PreAuthorize("hasAnyAuthority('test02')")
    public String test02() {
        return "test02";
    }

    @GetMapping("/app")
    @PreAuthorize("hasAnyAuthority('app')")
    public String app() {
        return "app";
    }

}

webflux测试接口

package com.example.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

/**
 * 测试接口
 *
 * @author vains
 */
@RestController
public class TestController {

    @GetMapping("/test01")
    @PreAuthorize("hasAnyAuthority('message.write')")
    public Mono<String> test01() {
        return Mono.just("test01");
    }

    @GetMapping("/test02")
    @PreAuthorize("hasAnyAuthority('test02')")
    public Mono<String> test02() {
        return Mono.just("test02");
    }

    @GetMapping("/app")
    @PreAuthorize("hasAnyAuthority('app')")
    public Mono<String> app() {
        return Mono.just("app");
    }

}

测试

前置条件

  1. 启动认证服务
  2. 启动nacos
  3. nacos中都有相关配置

启动项目

依次启动三个服务,顺序无所谓

在postman中直接访问网关接口或代理的服务接口

在postman中直接访问网关
被重定向至登录了,携带X-Requested-With请求头访问,代表当前是xhr请求

携带请求头访问
响应401,框架有区分是浏览器请求还是xhr请求,对于浏览器请求会重定向到页面,对于xhr请求默认会响应401状态码,可自己实现异常处理,这里错误信息在请求头中是因为没有重写异常处理,网关资源服务配置代码中有注释。

在浏览器中访问网关接口或代理的服务接口

访问

浏览器打开地址:http://127.0.0.1:7000/resource/app

引导请求至认证服务发起授权申请
请求到达网关后检测到未登录会引导用户进行OAuth2认证流程

登录后提交

登录后提交
登录提交后认证服务重定向授权申请接口,校验通过后会生成code并携带code重定向至回调地址,注意,这里的回调地址是网关的服务地址,由网关中的OAuth2 Client处理,如图

网关根据code换取token
网关会根据code换取token,获取token后根据token获取用户信息,并调用网关客户端配置中自定义的userAuthoritiesMapper解析权限信息。

访问权限不足的接口

权限不足
响应403,并将错误信息放入响应头中

使用token访问网关

过期token

过期token
响应401并在响应头中提示token已过期

错误token

错误token
响应401并在响应头中提示token无法解析

权限不足token

权限不足

响应403并提示权限不足

正常请求

正常请求
响应200并正确响应接口信息

写在最后

本文带大家简单实现了Spring Cloud Gateway对接认证服务,Gateway中添加客户端主要是为了如果代理服务有静态资源(html、css、image)时可以直接发起OAuth2授权流程,在浏览器登录后直接访问,同时也是开启令牌中继的必要依赖;引入Resource Server依赖是当需要对网关的接口鉴权时可以直接使用,如果网关只负责转发应该是可以去掉资源服务相关依赖和配置的,由各个被代理的微服务对自己的接口进行鉴权。这些东西在之前基本都是讲过的内容,所以本文很多地方都是一笔带过的,如果某些地方不清楚可以针对性的翻翻之前的文章,也可以在评论区中提出。
如果有什么问题或者需要补充的请在评论区指出,谢谢。

附录

Gitee仓库地址

Gateway令牌中继文档

OAuth2登录后用户权限解析文档

webflux开启方法鉴权EnableReactiveMethodSecurity注解说明文档

webflux的Jwt解析器适配器说明文档

webflux对接OAuth2 Client文档

webflux对接OAuth2 Resource Server文档
文章来源地址https://www.toymoban.com/news/detail-683272.html

到了这里,关于Spring Authorization Server入门 (十六) Spring Cloud Gateway对接认证服务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【spring authorization server系列教程】(一)入门系列,spring authorization server简介。快速构建一个授权服务器(基于最新版本0.3.0)

    【spring authorization server系列教程】(一)入门系列,快速构建一个授权服务器 spring authorization server是spring团队最新的认证授权服务器,之前的oauth2后面会逐步弃用。不过到现在发文的时候,我看到官网已经把之前oauth2仓库废弃了。 现在spring authorization server已经到生产就绪阶段了

    2024年02月05日
    浏览(21)
  • spring cloud gateway 网关 server Web Exchange方法

    1.String getHeader (String name) 该方法用于获取一个指定头字段的值,如果请求消息中没有包含指定的头字段,则 getHeader() 方法返回 null;如果请求消息中包含多个指定名称的头字段,则 getHeader() 方法返回其中第一个头字段的 2.getQueryParams()获取 请求参数中的信息

    2024年02月12日
    浏览(15)
  • Spring Authorization Server入门 (二十) 实现二维码扫码登录

    打开网页,发起授权申请/未登录被重定向到登录页面 选择二维码登录,页面从后端请求二维码 页面渲染二维码图片,并轮询请求,获取二维码的状态 事先登录过APP的手机扫描二维码,然后APP请求服务器端的API接口,把用户认证信息传递到服务器中 后端收到APP的请求后更改

    2024年02月21日
    浏览(13)
  • Spring Authorization Server入门 (十五) 分离授权确认与设备码校验页面

    2023-12-01修改:在session-data-redis(Github)分支中添加了基于 spring-session-data-redis 的实现,无需借助 nonceId 来保持认证状态,该分支已去除所有 nonceId 相关内容,需要注意的是 axios 在初始化时需要添加配置 withCredentials: true ,让请求携带cookie。当然一些响应json的处理还是使用下方的

    2024年02月14日
    浏览(10)
  • 【Spring Authorization Server 系列】(一)入门篇,快速搭建一个授权服务器

    官方主页:https://spring.io/projects/spring-authorization-server Spring Authorization Server 是一个框架,提供了 OAuth 2.1 和 OpenID Connect 1.0 规范以及其他相关规范的实现。 它建立在 Spring Security 之上,为构建 OpenID Connect 1.0 Identity Providers 和 OAuth2 Authorization Server 产品提供安全、轻量级和可定制

    2024年02月16日
    浏览(20)
  • Spring Authorization Server入门 (一) 初识SpringAuthorizationServer和OAuth2.1协议

    经过近些年网络和设备的不断发展,之前的oauth2.0发布的授权协议标准已经远远不能满足现在的场景和需求,根据其安全最佳实践,在oauth2.0的基础上移除了一些不安全的授权方式,并且对扩展协议进行整合。该协议定义了一系列关于授权的开放网络标准,允许用户授权第三方

    2024年02月11日
    浏览(14)
  • Spring Authorization Server入门 (十二) 实现授权码模式使用前后端分离的登录页面

    2023-12-01修改:在session-data-redis(Github)分支中添加了基于 spring-session-data-redis 的实现,无需借助 nonceId 来保持认证状态,该分支已去除所有 nonceId 相关内容,需要注意的是 axios 在初始化时需要添加配置 withCredentials: true ,让请求携带cookie。当然一些响应json的处理还是使用下方的

    2024年02月13日
    浏览(18)
  • Spring Authorization Server入门 (三) 集成流程说明、细节补充和各种方式获取token测试

    在上一篇文章中的AuthorizationConfig.java配置类中,类上有三个注解,分别是@Configuration、@EnableWebSecurity和@EnableMethodSecurity注解,虽然在类中有注释,但是这里在细讲一下,同时放一下官网的说明 @EnableWebSecurity 加载了WebSecurityConfiguration配置类, 配置安全认证策略。 加载了Authenti

    2024年02月11日
    浏览(18)
  • Spring Security 6.x 系列【28】授权服务器篇之Spring Authorization Server 1.0 入门案例

    有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.0.4 本系列Spring Security 版本 6.0.2 本系列Spring Authorization Server 版本 1.0.2 源码地址:https://gitee.com/pearl-organization/study-spring-security-demo 在前几篇文档中,我们学习了 OAuth 2.0 协议,并使用 spring-security-oauth2-client 完成

    2024年02月12日
    浏览(17)
  • Spring Cloud Gateway快速入门(二)——断言工厂

    Spring Cloud Gateway包含许多内置的Route Predicate工厂。所有这些断言都匹配HTTP请求的不同属性。多路由断言工厂通过and组合。 官方提供的路由工厂: 这些断言工厂的配置方式,参照官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html

    2024年02月07日
    浏览(12)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包