我有一个带有端点POST/login的Spring启动应用程序,它验证凭据并在响应标头中返回JWT。还有另一个端点/api/cars/list 需要具有有效 JWT 的授权标头。此应用部署到具有 3 个节点的 Kubernetes 群集。之后,我在集群中安装了用于 L7 路由的 ngnix 入口控制器,并添加了入口资源。
遵循本教程 - https://cloud.google.com/community/tutorials/nginx-ingress-gke。
当我使用从 POST/login 生成的 JWT 并将其用于 GET/api/cars/listsing 时,我在响应中收到 403 错误。 我需要在 Nginx 入口控制器中配置什么,以便根据请求 IP 将请求路由到同一节点?
kind: Ingress
metadata:
name: ingress-resource
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- path: /jwt(/|$)(.*)
backend:
serviceName: jwt-app-service
servicePort: 80
POST/jwt/login
GET/jwt/api/cars/listings
查看 kubectl 日志后,发现问题与 JWT 密钥生成有关。每次 Spring 启动应用程序重新启动时,都会动态生成密钥。
我在 Spring 配置文件中使用了Keys.secretKeyFor(SignatureAlgorithm.HS512);
,如下所示。这可以配置为部署环境变量或以其他安全方式配置。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtTokenService jwtTokenService;
private AppUserDetailsService appUserDetailsService;
@Autowired
public SecurityConfig(AppUserDetailsService appUserDetailsService) {
this.jwtTokenService = jwtTokenService();
this.appUserDetailsService = appUserDetailsService;
}
public SecurityConfig() {
this.jwtTokenService = jwtTokenService();
}
private Key base64EncodedSecretKey() {
return Keys.secretKeyFor(SignatureAlgorithm.HS512);
}
private JwtTokenService jwtTokenService() {
return new JwtTokenService(base64EncodedSecretKey());
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.appUserDetailsService)
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET,"/greetings").permitAll()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated()
.and()
.addFilterBefore(new LoginFilter("/login", this.jwtTokenService, authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtAuthenticationFilter(this.jwtTokenService, "/api/**"), UsernamePasswordAuthenticationFilter.class);
}
}