Spring Cloud Gateway实现统一认证和鉴权
时间: 2024-01-22 08:44:29 浏览: 195
Spring Cloud Gateway是一个基于Spring Boot 2.x的API网关,可以作为微服务架构中的统一入口,提供路由、转发、负载均衡、限流、降级、统一认证和鉴权等功能。在实现统一认证和鉴权时,可以结合Spring Security和JWT来实现。
具体实现步骤如下:
1. 引入Spring Security和JWT的依赖
在Spring Cloud Gateway的pom.xml文件中,引入Spring Security和JWT的依赖:
```
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
```
2. 配置Spring Security
在Spring Cloud Gateway的配置类中,配置Spring Security:
```
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Autowired
private JwtAuthenticationManager jwtAuthenticationManager;
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http.csrf().disable()
.authorizeExchange()
.pathMatchers("/login").permitAll()
.anyExchange().authenticated()
.and()
.addFilterAt(new JwtAuthenticationFilter(jwtAuthenticationManager), SecurityWebFiltersOrder.AUTHENTICATION)
.build();
}
}
```
在上面的配置中,首先禁用了CSRF防护,然后配置了登录接口不需要认证,其它接口都需要认证。最后添加了一个JWT认证过滤器。
3. 配置JWT
在Spring Cloud Gateway的配置类中,配置JWT:
```
@Configuration
public class JwtConfig {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
@Bean
public JwtAuthenticationManager jwtAuthenticationManager() {
return new JwtAuthenticationManager(secret);
}
@Bean
public JwtTokenGenerator jwtTokenGenerator() {
return new JwtTokenGenerator(secret, expiration);
}
}
```
在上面的配置中,配置了JWT的密钥和过期时间,并创建了JWT的管理器和生成器。
4. 实现登录接口
实现登录接口,生成JWT并返回给客户端:
```
@RestController
public class LoginController {
@Autowired
private JwtTokenGenerator jwtTokenGenerator;
@PostMapping("/login")
public Mono<ResponseEntity<Map<String, String>>> login(@RequestBody LoginRequest loginRequest) {
// 验证用户名和密码
if (validateUsernameAndPassword(loginRequest)) {
// 生成JWT
String token = jwtTokenGenerator.generateToken(loginRequest.getUsername());
// 返回JWT
Map<String, String> responseBody = new HashMap<>();
responseBody.put("token", token);
return Mono.just(ResponseEntity.ok(responseBody));
} else {
return Mono.just(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
}
}
private boolean validateUsernameAndPassword(LoginRequest loginRequest) {
// 验证用户名和密码逻辑
}
}
```
在上面的代码中,先验证用户名和密码是否正确,如果正确则生成JWT并返回给客户端,否则返回401未授权状态码。
5. 实现JWT认证过滤器
实现JWT认证过滤器,从请求头中获取JWT并验证:
```
public class JwtAuthenticationFilter extends AuthenticationWebFilter {
public JwtAuthenticationFilter(JwtAuthenticationManager jwtAuthenticationManager) {
super(jwtAuthenticationManager);
}
@Override
protected Mono<Void> onAuthSuccess(Authentication authentication, ServerWebExchange exchange) {
return super.onAuthSuccess(authentication, exchange);
}
@Override
protected Mono<Void> onAuthFailure(AuthenticationException e, ServerWebExchange exchange) {
return super.onAuthFailure(e, exchange);
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, AuthenticationFilterChain chain) {
String token = extractToken(exchange.getRequest().getHeaders().getFirst("Authorization"));
if (StringUtils.isEmpty(token)) {
return chain.filter(exchange);
} else {
JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(token);
return super.filter(exchange, chain)
.subscriberContext(ReactiveSecurityContextHolder.withAuthentication(jwtAuthenticationToken));
}
}
private String extractToken(String header) {
// 从Authorization头中提取JWT
}
}
```
在上面的代码中,先从请求头中提取JWT,如果JWT为空则直接调用下一个过滤器,否则创建JwtAuthenticationToken并将其设置到SecurityContext中。
6. 实现JWT认证管理器
实现JWT认证管理器,验证JWT是否正确:
```
public class JwtAuthenticationManager implements ReactiveAuthenticationManager {
private final String secret;
public JwtAuthenticationManager(String secret) {
this.secret = secret;
}
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
String token = authentication.getCredentials().toString();
try {
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
String username = claimsJws.getBody().getSubject();
return Mono.just(new JwtAuthenticationToken(username, token));
} catch (JwtException e) {
return Mono.error(e);
}
}
}
```
在上面的代码中,使用JWT解析器解析JWT,并验证签名和过期时间,如果验证通过则创建JwtAuthenticationToken。
7. 实现JWT认证令牌
实现JWT认证令牌:
```
public class JwtAuthenticationToken extends AbstractAuthenticationToken {
private final String token;
private final String username;
public JwtAuthenticationToken(String token) {
super(Collections.emptyList());
this.token = token;
this.username = null;
}
public JwtAuthenticationToken(String username, String token) {
super(Collections.emptyList());
this.token = token;
this.username = username;
setAuthenticated(true);
}
@Override
public Object getCredentials() {
return token;
}
@Override
public Object getPrincipal() {
return username;
}
}
```
在上面的代码中,实现了AbstractAuthenticationToken的两个抽象方法,并添加了一个token和username属性。
8. 配置路由规则
最后,配置路由规则,启用Spring Cloud Gateway:
```
@Configuration
public class GatewayConfig {
@Autowired
private JwtTokenGenerator jwtTokenGenerator;
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("login", r -> r.path("/login")
.uri("http://localhost:8080/login"))
.route("hello", r -> r.path("/hello")
.filters(f -> f.requestHeader("Authorization", "Bearer " + jwtTokenGenerator.generateToken("user")))
.uri("http://localhost:8081/hello"))
.build();
}
}
```
在上面的配置中,配置了两个路由规则,一个是登录接口,另一个是hello接口,hello接口需要通过JWT认证才能访问。
阅读全文