Spring Cloud: микросервисная аутентификация работает через Eureka Discovery, но не через Zuul

Я новичок в Spring Cloud. Я пытаюсь создать несколько микросервисов, используя Spring Cloud Eureka Discovery и Zuul Gateway.

Я могу получить доступ к микросервисам и выполнять действия через шлюз Zuul, но только тогда, когда не задействована безопасность. Если мой микросервис защищен, я не могу ничего сделать через Zuul, поскольку он не возвращает токен JWT. Если я делаю это через Eureka Discovery Client, это работает как шарм.

Может быть, что-то не так с моей конфигурацией Zuul? Или, может быть, я выбрал неверный способ защиты услуг? Заранее спасибо!

Вот моя конфигурация шлюза Zuul:

Application.class

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class GatewayServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayServiceApplication.class, args);
    }

    @Bean
    public Filter shallowEtagHeaderFilter() {
        return new ShallowEtagHeaderFilter();
    }
}

application.properties

eureka.client.service-url.defaultZone=http://localhost:8010/eureka/
server.port=8011
zuul.prefix=/services

bootstrap.properties

spring.application.name=gateway-service
# specify where config server is up
spring.cloud.config.uri=http://localhost:8001

Вот моя микросервисная JWT и конфигурация безопасности:

WebSecurityConfig.class

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure (HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
                .antMatchers(HttpMethod.POST, "/login").permitAll()
                .anyRequest().authenticated()
                .and()
                // We filter the api/login requests
                .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
                // And filter other requests to check the presence of JWT in header
                .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure (AuthenticationManagerBuilder auth) throws Exception {
        // Create a default account
        auth.inMemoryAuthentication()
                .withUser("**")
                .password("**")
                .roles("**");
    }
}

TokenAuthenticationService.class

public class TokenAuthenticationService {
    static final long EXPIRATIONTIME = 864_000_000; // 10 days
    static final String SECRET = "*";
    static final String TOKEN_PREFIX = "Bearer";
    static final String HEADER_STRING = "Authorization";

    static void addAuthentication (HttpServletResponse res, String username) {
        String JWT = Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + JWT);
    }

    static Authentication getAuthentication (HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);
        if (token != null) {
            // parse the token.
            String user = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                    .getBody()
                    .getSubject();

            return user != null ?
                    new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList()) :
                    null;
        }
        return null;
    }
}

JWTLoginFilter.class

public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {

    public JWTLoginFilter (String url, AuthenticationManager authManager) {
        super(new AntPathRequestMatcher(url));
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication (HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException, IOException, ServletException {

        AccountCredentials creds = new ObjectMapper().readValue(req.getInputStream(), AccountCredentials.class);
        return getAuthenticationManager().authenticate(
                new UsernamePasswordAuthenticationToken(
                        creds.getUsername(),
                        creds.getPassword(),
                        Collections.emptyList()
                )
        );

    }

    @Override
    protected void successfulAuthentication (HttpServletRequest req,
                                             HttpServletResponse res,
                                             FilterChain chain,
                                             Authentication auth)
            throws IOException, ServletException {
        TokenAuthenticationService.addAuthentication(res, auth.getName());
    }
}

JWTAuthenticationFilter.class

public class JWTAuthenticationFilter extends GenericFilterBean {
    @Override
    public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        Authentication authentication = TokenAuthenticationService.getAuthentication((HttpServletRequest) servletRequest);

        SecurityContextHolder.getContext().setAuthentication(authentication);
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

1 ответ

Решение

Попробуйте определить ниже.

zuul.sensitiveHeaders=Cookie,Set-Cookie

В zuul заголовки Cookie, Set-Cookie и Authroization являются чувствительными заголовками по умолчанию. Если вы хотите использовать заголовок Authroization на вашем сервере API, вам нужно переопределить его без Authroization заголовок как выше.

Также вы можете определить его для каждого маршрута. Пожалуйста, обратитесь к документу: http://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html [Cookies и чувствительные заголовки]

Другие вопросы по тегам