在Spring WebFlux中,使用Spring Security的Token模式(通常是基于JWT的认证)可以为你的应用提供安全保障。以下是如何配置Spring Security以支持基于Token的认证和鉴权的步骤:
1. 添加依赖
首先,确保你的pom.xml
或build.gradle
文件中包含必要的依赖项。
Maven:
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
Gradle:
groovy
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
}
2. 配置Spring Security
创建一个配置类来设置Spring Security。
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.authentication.ServerHttpBasicAuthenticationConverter;
import org.springframework.security.web.server.authentication.ServerHttpBearerAuthenticationConverter;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.pathMatchers("/login").permitAll()
.anyExchange().authenticated()
.and()
.addFilterAt(authenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
.csrf().disable();
return http.build();
}
@Bean
public AuthenticationWebFilter authenticationWebFilter() {
AuthenticationWebFilter filter = new AuthenticationWebFilter(new JwtAuthenticationManager());
filter.setServerAuthenticationConverter(new ServerHttpBearerAuthenticationConverter());
return filter;
}
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withUsername("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
3. JWT认证管理器
创建一个JWT认证管理器来处理Token的验证和生成。
java
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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 io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@Component
public class JwtAuthenticationManager implements ReactiveAuthenticationManager {
private final UserDetailsService userDetailsService;
private final String secretKey = "yourSecretKey"; // 应该从配置文件或环境变量中读取
public JwtAuthenticationManager(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
String token = (String) authentication.getCredentials();
Claims claims = Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)))
.build()
.parseClaimsJws(token)
.getBody();
String username = claims.getSubject();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
return Mono.just(new UsernamePasswordAuthenticationToken(userDetails, token, userDetails.getAuthorities()));
}
}
4. 登录端点
创建一个登录端点来生成JWT Token。
java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
@RestController
public class LoginController {
private final AuthenticationManager authenticationManager;
private final String secretKey = "yourSecretKey"; // 应该从配置文件或环境变量中读取
public LoginController(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@PostMapping("/login")
public Mono<String> login(@RequestBody LoginRequest loginRequest) {
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()))
.flatMap(authentication -> {
String token = Jwts.builder()
.setSubject(authentication.getName())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时过期
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
return Mono.just(token);
});
}
}
class LoginRequest {
private String username;
private String password;
// Getters and Setters
}
5. 测试
启动你的应用并测试登录端点和受保护的资源。确保只有经过认证的用户才能访问受保护的资源。
通过以上步骤,你可以在Spring WebFlux应用中实现基于Token的认证和鉴权。