spring security与gateway结合进行网关鉴权和授权
在Spring Cloud Gateway中集成Spring Security 6以实现鉴权和认证工作,可以在网关代理层完成权限校验和认证。这种架构通常被称为“边缘安全”或“API网关安全”,它允许你在请求到达后端服务之前进行集中式的安全控制。
以下是如何配置Spring Cloud Gateway与Spring Security 6来实现这一目标的详细步骤:
1. 添加依赖
首先,确保你的项目中包含必要的依赖。你需要Spring Security、Spring Cloud Gateway以及JWT相关的依赖。
<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路由上。
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令牌。
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验证逻辑。
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。
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路由
最后,在你的网关配置文件中定义路由。
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,从而实现在网关代理层进行鉴权和认证的功能。主要步骤包括:
- 添加依赖:引入必要的依赖。
- 配置SecurityFilterChain:禁用CSRF保护并设置为无状态会话。
- 实现JWT工具类:用于生成和解析JWT令牌。
- 实现JWT认证管理器:处理JWT验证逻辑。
- 实现Bearer Token转换器:从请求头中提取Bearer Token。
- 配置Gateway路由:定义路由规则。
这样,你的应用程序就可以使用JWT在网关层进行安全认证和授权了。