I'm kind of new to Spring Cloud. I'm trying to build a few microservices by using Spring Cloud Eureka Discovery and Zuul Gateway.
I can access the microservices and perform actions through Zuul Gateway, but only when there is no security involved. If my microservice is secured, then I cannot do anything through Zuul as it doesn't return back a JWT token. If I do it through Eureka discovery client it works like a charm.
Maybe there is something wrong with my Zuul configuration? Or maybe I have chosen an invalid way to secure the services? Thanks in advance!
Here is my Zuul Gateway configuration:
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
Here is my microservice JWT & security configuration:
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);
}
}
try to define the below.
zuul.sensitiveHeaders=Cookie,Set-Cookie
In zuul, Cookie, Set-Cookie and Authroization headers are default sensitive headers. If you want to Authroization header in your api server, you need to redefine it without Authroization
header like above.
Also you can define it for each route. Please refer to the doc : http://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html [Cookies and Sensitive Headers]