这是我最终使用 Spring 2.2.2 Cloud Gateway、Eureka 和 KeyCloak 实现的。
安全微服务。
@GetMapping(value = "/grantedAuthorities/{applicationName}/{userId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
// JWT not sent when called within jwtAuthenticationConverter, so pass user id as param !!
public Flux<String> getUsersApplicationAuthorities(@PathVariable String applicationName, @PathVariable String userId) {
return Flux.fromIterable(roleRepository.getRolesByUserId(applicationName, userId));
}
这会返回类似的东西;
SCREEN1_READ
SCREEN2_WRITE
我的其他微服务。
public class SecurityConfig {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(exchanges ->
exchanges
.pathMatchers("/screen1.html").hasAnyAuthority("SCREEN1_READ", "SCREEN1_WRITE")
.pathMatchers("/screen2.html").hasAnyAuthority("SCREEN1_READ", "SCREEN2_WRITE")
.anyExchange().authenticated()
)
.oauth2ResourceServer(spec ->
spec.jwt().jwtAuthenticationConverter(jwt -> {
/* ServerBearerExchangeFilterFunction does not work here! So I have to send userId instead of JWT */
WebClient webClient = loadBalancedWebClientBuilder().build();
String userId = jwt.getClaimAsString("preferred_username").toUpperCase();
String uri = "lb://SECURITY/drs/grantedAuthorities/" + applicationName + "/" + userId;
return webClient.get().uri(uri)
.retrieve()
.bodyToFlux(String.class)
.map(s -> new SimpleGrantedAuthority(s))
.doOnNext(l -> log.info("Has authority " + l))
.collectList()
.map(gaList -> new JwtAuthenticationToken(jwt, gaList));
})
)
.csrf().disable()
.cors().disable();
return http.build();
}
}
用于在 READ/WRITE for Thymeleaf 时启用/禁用按钮;
@Controller
public class PageController {
@GetMapping("/screen1.html")
public String index(@AuthenticationPrincipal JwtAuthenticationToken jwt, Model model) {
model.addAttribute("update", jwt.getAuthorities().contains(new SimpleGrantedAuthority("SCREEN1_WRITE")));
return "screen1";
}
}
<html>
<body>
<div id="updateableScreen" th:attr="data-update=${update}"></div>
</body>
</html>
之后使用 JQuery/Javascript 通过检查 $("#updateableScreen") 来更改对表单控件的访问