spring security与gateway结合进行网关鉴权和授权

在Spring Cloud Gateway中集成Spring Security 6以实现鉴权和认证工作,可以在网关代理层完成权限校验和认证。这种架构通常被称为"边缘安全"或"API网关安全",它允许你在请求到达后端服务之前进行集中式的安全控制。

以下是如何配置Spring Cloud Gateway与Spring Security 6来实现这一目标的详细步骤:

1. 添加依赖

首先,确保你的项目中包含必要的依赖。你需要Spring Security、Spring Cloud Gateway以及JWT相关的依赖。

xml 复制代码
<dependencies>
    <!-- Spring Boot Starter Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- Spring Cloud Gateway -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <!-- JWT Support -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>

    <!-- Spring Cloud Dependencies Management -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2022.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</dependencies>

2. 配置SecurityFilterChain

在Spring Security配置类中设置SecurityFilterChain,以支持基于Token的认证机制,并应用到Gateway路由上。

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import reactor.core.publisher.Mono;

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, JwtAuthenticationManager jwtAuthenticationManager) {
        http
            .csrf().disable() // 禁用CSRF保护
            .authorizeExchange(exchanges -> exchanges
                .pathMatchers("/login").permitAll() // 公开路径,例如登录
                .anyExchange().authenticated() // 其他所有请求都需要认证
            )
            .addFilterAt(jwtAuthenticationFilter(jwtAuthenticationManager), AuthenticationWebFilter.class);

        return http.build();
    }

    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        return new MapReactiveUserDetailsService(userDetails);
    }

    private AuthenticationWebFilter jwtAuthenticationFilter(JwtAuthenticationManager jwtAuthenticationManager) {
        AuthenticationWebFilter filter = new AuthenticationWebFilter(jwtAuthenticationManager);
        filter.setServerAuthenticationConverter(new BearerTokenServerAuthenticationConverter());
        return filter;
    }
}

3. 实现JWT工具类

创建一个工具类来生成和解析JWT令牌。

java 复制代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
public class JwtUtil {

    private String secret = "yourSecretKey"; // 应该存储在一个安全的地方,并且不要硬编码

    public Mono<String> extractUsername(String token) {
        return Mono.just(extractClaim(token, Claims::getSubject));
    }

    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    public Mono<String> generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return Mono.just(createToken(claims, userDetails.getUsername()));
    }

    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时过期
                .signWith(SignatureAlgorithm.HS256, secret).compact();
    }

    public Mono<Boolean> validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token).block();
        return Mono.just(username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
}

4. 实现JWT认证管理器

创建一个自定义的认证管理器来处理JWT验证逻辑。

java 复制代码
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

@Component
public class JwtAuthenticationManager implements ReactiveAuthenticationManager {

    private final JwtUtil jwtUtil;
    private final UserDetailsService userDetailsService;

    public JwtAuthenticationManager(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
        this.jwtUtil = jwtUtil;
        this.userDetailsService = userDetailsService;
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        String token = (String) authentication.getCredentials();
        return jwtUtil.extractUsername(token)
                .flatMap(username -> {
                    if (jwtUtil.validateToken(token, userDetailsService.loadUserByUsername(username)).block()) {
                        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                        return Mono.just(new UsernamePasswordAuthenticationToken(userDetails, token, userDetails.getAuthorities()));
                    } else {
                        return Mono.empty();
                    }
                });
    }
}

5. 实现Bearer Token转换器

创建一个转换器来从请求头中提取Bearer Token。

java 复制代码
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class BearerTokenServerAuthenticationConverter implements ServerAuthenticationConverter {

    @Override
    public Mono<org.springframework.security.oauth2.server.resource.authentication.ServerAuthenticationToken> convert(ServerWebExchange exchange) {
        String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            return Mono.just(new org.springframework.security.oauth2.server.resource.authentication.ServerAuthenticationToken(token, token));
        }
        return Mono.empty();
    }
}

6. 配置Gateway路由

最后,在你的网关配置文件中定义路由。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: service_route
          uri: lb://your-backend-service
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>.*), /$\{segment}

总结

通过上述步骤,你可以在Spring Cloud Gateway中集成Spring Security 6,从而实现在网关代理层进行鉴权和认证的功能。主要步骤包括:

  1. 添加依赖:引入必要的依赖。
  2. 配置SecurityFilterChain:禁用CSRF保护并设置为无状态会话。
  3. 实现JWT工具类:用于生成和解析JWT令牌。
  4. 实现JWT认证管理器:处理JWT验证逻辑。
  5. 实现Bearer Token转换器:从请求头中提取Bearer Token。
  6. 配置Gateway路由:定义路由规则。

这样,你的应用程序就可以使用JWT在网关层进行安全认证和授权了。

相关推荐
m0_7482482320 分钟前
Java进阶14 TCP&日志&枚举
java·开发语言·tcp/ip
Morgenyao28 分钟前
常用工具类——Collections集合框架
java
白露与泡影32 分钟前
阿里 Java 岗个人面经分享(技术三面 + 技术 HR 面):Java 基础 +Spring+JVM+ 并发编程 + 算法 + 缓存
java·jvm·spring
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ39 分钟前
消费kafka消息示例
java·kafka
fengdongnan41 分钟前
idea隐藏无关文件
java·ide·intellij-idea
web1508509664144 分钟前
Java进阶学习笔记18——接口的注意事项
java·笔记·学习
suuijbd1 小时前
谈谈对IOC的理解
java·spring
快乐就好ya1 小时前
skywalking服务追踪
java·spring boot·spring cloud·微服务·服务发现
不听话的小耳朵1 小时前
Junit5使用教程(5)
java·junit
FoolRabbit1 小时前
JDK17主要特性
java·jdk