Spring Cloud:微服务认证通过 Eureka Discovery 工作,而不是通过 Zuul
Posted
技术标签:
【中文标题】Spring Cloud:微服务认证通过 Eureka Discovery 工作,而不是通过 Zuul【英文标题】:Spring Cloud: Microservice authentication works through Eureka Discovery, but not through Zuul 【发布时间】:2017-09-15 08:07:12 【问题描述】:我对 Spring Cloud 有点陌生。我正在尝试使用 Spring Cloud Eureka Discovery 和 Zuul Gateway 构建一些微服务。
我可以通过 Zuul 网关访问微服务并执行操作,但前提是不涉及安全性。如果我的微服务是安全的,那么我无法通过 Zuul 做任何事情,因为它不会返回 JWT 令牌。如果我通过 Eureka 发现客户端执行此操作,它就像一个魅力。
也许我的 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);
【问题讨论】:
您是否删除了“授权”标头表单sensitiveHeaders 列表? sensitiveHeaders:Cookie、Set-Cookie、Authorization 是默认值。所以授权标头被zuul代理过滤掉。如果需要,请声明不带授权标头的sensitiveHeaders。 @yongsung.yoon 你是说这个吗?zuul.sensitive-headers=Authorisation
您可以为每个路由定义敏感标头。您需要在没有授权的情况下重新声明它以允许授权头字段。请参考cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html中的“Cookies and Sensitive Headers”部分
@yongsung.yoon zuul.routes.users.path=/myusers/** zuul.routes.users.sensitiveHeaders=Cookie,Set-Cookie,Authorization zuul.routes.users.url=downstream
喜欢这样吗?
对。同样正如您所写,您可以使用“zuu.sensitive-headers=Cookie, Set-Cookie”全局定义它。您需要从列表中删除“授权”,因为它是默认的
【参考方案1】:
尝试定义以下内容。
zuul.sensitiveHeaders=Cookie,Set-Cookie
在 zuul 中,Cookie、Set-Cookie 和授权标头是默认的敏感标头。如果你想在你的 api 服务器中授权标头,你需要重新定义它而不像上面的 Authroization
标头。
您也可以为每条路线定义它。 请参考文档:http://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html [Cookies and Sensitive Headers]
【讨论】:
以上是关于Spring Cloud:微服务认证通过 Eureka Discovery 工作,而不是通过 Zuul的主要内容,如果未能解决你的问题,请参考以下文章
7.Spring Cloud Alibaba微服务的用户认证与授权
Spring Cloud中Feign如何统一设置验证token