Spring 被动安全
Spring reactive security
我正在尝试反应式安全,未经身份验证的调用不会转到授权管理器。
@Configuration
@EnableWebFluxSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig{
@Autowired
private WebAuthenticationManager authenticationManager;
@Autowired
private ServerSecurityContextRepository securityContextRepository;
private static final String[] AUTH_WHITELIST = {
"/login/**",
"/logout/**",
"/authorize/**",
"/favicon.ico",
};
@Bean
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
return http.exceptionHandling().authenticationEntryPoint((swe, e) -> {
return Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
});
}).accessDeniedHandler((swe, e) -> {
return Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
});
}).and().csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
.authorizeExchange().pathMatchers(HttpMethod.OPTIONS).permitAll()
.pathMatchers(AUTH_WHITELIST).permitAll()
.anyExchange().authenticated().and().build();
}
@Bean
public PBKDF2Encoder passwordEncoder() {
return new PBKDF2Encoder();
}
}
Web 身份验证管理器,
@Component
public class WebAuthenticationManager implements ReactiveAuthenticationManager {
@Autowired
private JWTUtil jwtUtil;
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
String authToken = authentication.getCredentials().toString();
String username;
try {
username = jwtUtil.getUsernameFromToken(authToken);
} catch (Exception e) {
username = null;
}
if (username != null && jwtUtil.validateToken(authToken)) {
Claims claims = jwtUtil.getAllClaimsFromToken(authToken);
List<String> rolesMap = claims.get("role", List.class);
List<Role> roles = new ArrayList<>();
for (String rolemap : rolesMap) {
roles.add(Role.valueOf(rolemap));
}
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
username,
null,
roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList())
);
return Mono.just(auth);
} else {
return Mono.empty();
}
}
}
在这里,我已经在 Securityconfig 中注册了我的 WebAuthentication 管理器。但是,未经身份验证的调用仍然不会转到 WebAuthenticationManager。
当受保护的 URL 被命中时,预计会转到 AuthenticationManager。例如,
不确定,为什么调用没有转到 AuthManager。
在非反应式中,我们有 OncePerRequestFilter 并且那里正在处理身份验证。不确定,如何为反应式实现相同的功能。
您禁用了所有身份验证机制,因此无法调用您的身份验证管理器。正如您提到的,您可以通过过滤器实现身份验证流程。
身份验证过滤器的示例实现:
@Bean
public AuthenticationWebFilter webFilter() {
AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(authenticationManager);
authenticationWebFilter.setServerAuthenticationConverter(tokenAuthenticationConverter());
authenticationWebFilter.setRequiresAuthenticationMatcher(serverWebExchangeMatcher());
authenticationWebFilter.setSecurityContextRepository(NoOpServerSecurityContextRepository.getInstance());
return authenticationWebFilter;
}
然后将此过滤器添加到 ServerHttpSecurity:http.addFilterBefore(webFilter(),SecurityWebFiltersOrder.HTTP_BASIC)
然后最后将调用您的身份验证管理器。
您必须提供一些额外的东西才能使其正常工作。
匹配器检查 Authorization
header 是否添加到请求中:
@Bean
public ServerWebExchangeMatcher serverWebExchangeMatcher() {
return exchange -> {
Mono<ServerHttpRequest> request = Mono.just(exchange).map(ServerWebExchange::getRequest);
return request.map(ServerHttpRequest::getHeaders)
.filter(h -> h.containsKey(HttpHeaders.AUTHORIZATION))
.flatMap($ -> ServerWebExchangeMatcher.MatchResult.match())
.switchIfEmpty(ServerWebExchangeMatcher.MatchResult.notMatch());
};
}
令牌转换器负责从请求中获取令牌并准备基础AbstractAuthenticationToken
@Bean
public ServerAuthenticationConverter tokenAuthenticationConverter() {
return exchange -> Mono.justOrEmpty(exchange)
.map(e -> getTokenFromRequest(e))
.filter(token -> !StringUtils.isEmpty(token))
.map(token -> getAuthentication(token));
}
我故意省略了 getTokenFromRequest
和 getAuthentication
的实现,因为有很多可用的示例。
我正在尝试反应式安全,未经身份验证的调用不会转到授权管理器。
@Configuration
@EnableWebFluxSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig{
@Autowired
private WebAuthenticationManager authenticationManager;
@Autowired
private ServerSecurityContextRepository securityContextRepository;
private static final String[] AUTH_WHITELIST = {
"/login/**",
"/logout/**",
"/authorize/**",
"/favicon.ico",
};
@Bean
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
return http.exceptionHandling().authenticationEntryPoint((swe, e) -> {
return Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
});
}).accessDeniedHandler((swe, e) -> {
return Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
});
}).and().csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
.authorizeExchange().pathMatchers(HttpMethod.OPTIONS).permitAll()
.pathMatchers(AUTH_WHITELIST).permitAll()
.anyExchange().authenticated().and().build();
}
@Bean
public PBKDF2Encoder passwordEncoder() {
return new PBKDF2Encoder();
}
}
Web 身份验证管理器,
@Component
public class WebAuthenticationManager implements ReactiveAuthenticationManager {
@Autowired
private JWTUtil jwtUtil;
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
String authToken = authentication.getCredentials().toString();
String username;
try {
username = jwtUtil.getUsernameFromToken(authToken);
} catch (Exception e) {
username = null;
}
if (username != null && jwtUtil.validateToken(authToken)) {
Claims claims = jwtUtil.getAllClaimsFromToken(authToken);
List<String> rolesMap = claims.get("role", List.class);
List<Role> roles = new ArrayList<>();
for (String rolemap : rolesMap) {
roles.add(Role.valueOf(rolemap));
}
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
username,
null,
roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList())
);
return Mono.just(auth);
} else {
return Mono.empty();
}
}
}
在这里,我已经在 Securityconfig 中注册了我的 WebAuthentication 管理器。但是,未经身份验证的调用仍然不会转到 WebAuthenticationManager。
当受保护的 URL 被命中时,预计会转到 AuthenticationManager。例如,
不确定,为什么调用没有转到 AuthManager。
在非反应式中,我们有 OncePerRequestFilter 并且那里正在处理身份验证。不确定,如何为反应式实现相同的功能。
您禁用了所有身份验证机制,因此无法调用您的身份验证管理器。正如您提到的,您可以通过过滤器实现身份验证流程。
身份验证过滤器的示例实现:
@Bean
public AuthenticationWebFilter webFilter() {
AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(authenticationManager);
authenticationWebFilter.setServerAuthenticationConverter(tokenAuthenticationConverter());
authenticationWebFilter.setRequiresAuthenticationMatcher(serverWebExchangeMatcher());
authenticationWebFilter.setSecurityContextRepository(NoOpServerSecurityContextRepository.getInstance());
return authenticationWebFilter;
}
然后将此过滤器添加到 ServerHttpSecurity:http.addFilterBefore(webFilter(),SecurityWebFiltersOrder.HTTP_BASIC)
然后最后将调用您的身份验证管理器。
您必须提供一些额外的东西才能使其正常工作。
匹配器检查 Authorization
header 是否添加到请求中:
@Bean
public ServerWebExchangeMatcher serverWebExchangeMatcher() {
return exchange -> {
Mono<ServerHttpRequest> request = Mono.just(exchange).map(ServerWebExchange::getRequest);
return request.map(ServerHttpRequest::getHeaders)
.filter(h -> h.containsKey(HttpHeaders.AUTHORIZATION))
.flatMap($ -> ServerWebExchangeMatcher.MatchResult.match())
.switchIfEmpty(ServerWebExchangeMatcher.MatchResult.notMatch());
};
}
令牌转换器负责从请求中获取令牌并准备基础AbstractAuthenticationToken
@Bean
public ServerAuthenticationConverter tokenAuthenticationConverter() {
return exchange -> Mono.justOrEmpty(exchange)
.map(e -> getTokenFromRequest(e))
.filter(token -> !StringUtils.isEmpty(token))
.map(token -> getAuthentication(token));
}
我故意省略了 getTokenFromRequest
和 getAuthentication
的实现,因为有很多可用的示例。