使用zuul时我遇到了问题。首先,我提供有关我的项目的信息。我在项目中一起使用Zuul和Consul,我正在尝试创建自己的身份验证和授权系统。为此,我决定在Spring Boot中使用oAuth2协议。
所以在这个架构中,
Zuul是接收请求和控制访问令牌。如果标头中不存在访问令牌,则路由oAuth服务。 oAuth服务认证用户和返回访问令牌到客户端。客户端发送带有访问令牌和Zuul检查访问令牌的头,而不是路由适当的服务。
最后,我使用jwt作为访问令牌并存储为jwt数据存储。
让我们通过问题。
首先是问题,Zuul将请求转发给未进行安全配置或进行默认配置的服务。但不要转发到自定义安全配置的服务。
现在,Zuul服务是资源服务,并且在我的项目中具有单独的身份验证服务。
现在,如果我在Authentication Service的引导类中仅使用@EnableAutoConfiguration
注释。 Zuul转发请求,但我不希望它是这样的。
附加信息:根据我的研究如果@EnableAutoConfiguration and @Component
注释一起使用,Spring启动扫描自定义配置但只使用@EnableAutoConfiguration
然后Spring启动不扫描自定义配置。而不是这个,Spring启动使用默认配置。
如果我在身份验证服务中使用@EnableAutoConfiguration @ComponentScan
而不是Zuul throw错误,如下所示。
有什么问题?我尝试了所有的解决方案,但没有工作,作为最后的手段来到这里请帮助我。
com.netflix.zuul.exception.ZuulException: Forwarding error
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.handleException(RibbonRoutingFilter.java:190) ~[spring-cloud-netflix-zuul-2.0.0.M5.jar:2.0.0.M5]
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.forward(RibbonRoutingFilter.java:165) ~[spring-cloud-netflix-zuul-2.0.0.M5.jar:2.0.0.M5]
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.run(RibbonRoutingFilter.java:112) ~[spring-cloud-netflix-zuul-2.0.0.M5.jar:2.0.0.M5]
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112) ~[zuul-core-1.3.0.jar:1.3.0]
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193) ~[zuul-core-1.3.0.jar:1.3.0]
at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157) ~[zuul-core-1.3.0.jar:1.3.0]
at com.netflix.zuul.FilterProcessor.route(FilterProcessor.java:118) ~[zuul-core-1.3.0.jar:1.3.0]
at com.netflix.zuul.ZuulRunner.route(ZuulRunner.java:96) ~[zuul-core-1.3.0.jar:1.3.0]
at com.netflix.zuul.http.ZuulServlet.route(ZuulServlet.java:116) ~[zuul-core-1.3.0.jar:1.3.0]
at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:81) ~[zuul-core-1.3.0.jar:1.3.0]
at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:165) [spring-webmvc-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequest(ZuulController.java:44) [spring-cloud-netflix-zuul-2.0.0.M5.jar:2.0.0.M5]
Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: auth-service
at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483) ~[ribbon-loadbalancer-2.2.4.jar:2.2.4]
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184) ~[ribbon-loadbalancer-2.2.4.jar:2.2.4]
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180) ~[ribbon-loadbalancer-2.2.4.jar:2.2.4]
at rx.Observable.unsafeSubscribe(Observable.java:10256) ~[rxjava-1.3.4.jar:1.3.4]
at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94) ~[rxjava-1.3.4.jar:1.3.4]
at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42) ~[rxjava-1.3.4.jar:1.3.4]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.3.4.jar:1.3.4]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.3.4.jar:1.3.4]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.3.4.jar:1.3.4]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.3.4.jar:1.3.4]
at rx.Observable.subscribe(Observable.java:10352) ~[rxjava-1.3.4.jar:1.3.4]
at rx.Observable.subscribe(Observable.java:10319) ~[rxjava-1.3.4.jar:1.3.4]
at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:443) ~[rxjava-1.3.4.jar:1.3.4]
at rx.observables.BlockingObservable.single(BlockingObservable.java:340) ~[rxjava-1.3.4.jar:1.3.4]
at com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:112) ~[ribbon-loadbalancer-2.2.4.jar:2.2.4]
at org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommand.run(AbstractRibbonCommand.java:117) ~[spring-cloud-netflix-zuul-2.0.0.M5.jar:2.0.0.M5]
at org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommand.run(AbstractRibbonCommand.java:46) ~[spring-cloud-netflix-zuul-2.0.0.M5.jar:2.0.0.M5]
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) ~[hystrix-core-1.5.12.jar:1.5.12]
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) ~[hystrix-core-1.5.12.jar:1.5.12]
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ~[rxjava-1.3.4.jar:1.3.4]
Spring Cloud版本:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M7</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
如果你想要第一次我把Zuul服务yml属性。
server:
port: 8080
spring:
application:
name: zuul-service
cloud:
consul:
discovery:
healthCheckPath: /health
healthChechInterval: 15s
prefer-ip-address: true
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
host: 127.0.0.1
port: 8500
config:
enabled: true
watch:
enabled: true
sleuth:
sampler:
percentage: 1
zuul:
routes:
accounts-service:
path: /account/**
serviceId: accounts-service
product-service:
path: /product/**
sensitiveHeaders:
serviceId: product-service
auth-service:
path: /auth/**
serviceId: auth-service
security:
basic:
enabled: false
oauth2:
sso:
loginPath: http://localhost:8081/login
client:
clientId: zuulfilter
clientSecret: secret
accessTokenUri: http://localhost:8081/oauth/token
userAuthorizationUri: http://localhost:8081/oauth/authorize
resource:
preferTokenInfo:false
和Zuul Boot Class
@EnableZuulProxy
@EnableDiscoveryClient
@EnableAutoConfiguration
@ComponentScan
@Configuration
@EnableResourceServer
public class ZuulServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServiceApplication.class,args);
}
}
和Zuul Web Security Config
@Configuration
@EnableWebSecurity
@Order(-10)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String CSRF_COOKIE_NAME = "XSRF-TOKEN";
private static final String CSRF_HEADER_NAME = "X-XSRF-TOKEN";
@Autowired
private ResourceServerTokenServices resourceServerTokenServices;
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/auth/**", "/auth/login", "/account/**","/product/**").permitAll().anyRequest().authenticated()
.and()
.csrf().requireCsrfProtectionMatcher(requestMatcher()).csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
.addFilterAfter(oAuth2AuthenticationProcessingFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.logout().logoutSuccessUrl("/").permitAll();
}
private OAuth2AuthenticationProcessingFilter oAuth2AuthenticationProcessingFilter() {
OAuth2AuthenticationProcessingFilter oAuth2AuthenticationProcessingFilter =
new OAuth2AuthenticationProcessingFilter();
oAuth2AuthenticationProcessingFilter.setAuthenticationManager(authenticationManagerBean());
oAuth2AuthenticationProcessingFilter.setStateless(false);
return oAuth2AuthenticationProcessingFilter;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() {
OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
oAuth2AuthenticationManager.setResourceId("zuulservice");
oAuth2AuthenticationManager.setTokenServices(resourceServerTokenServices);
oAuth2AuthenticationManager.setClientDetailsService(null);
return oAuth2AuthenticationManager;
}
private RequestMatcher requestMatcher() {
return new RequestMatcher() {
private final Pattern allowedMethods = Pattern.compile("^(GET|HEAD|OPTIONS|TRACE)$");
private final AntPathRequestMatcher [] antPathRequestMatcher = { new AntPathRequestMatcher("/auth/**")};
@Override
public boolean matches(HttpServletRequest request) {
if(allowedMethods.matcher(request.getMethod()).matches()) {
return false;
}
for(AntPathRequestMatcher matcher : antPathRequestMatcher) {
if(matcher.matches(request)) {
return false;
}
}
return true;
}
};
}
private static Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if(csrfToken != null) {
Cookie cookie = new Cookie(CSRF_COOKIE_NAME, csrfToken.getToken());
cookie.setPath("/");
cookie.setSecure(true);
response.addCookie(cookie);
}
filterChain.doFilter(request, response);
}
};
}
private static CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository csrfTokenRepository = new HttpSessionCsrfTokenRepository();
csrfTokenRepository.setHeaderName(CSRF_HEADER_NAME);
return csrfTokenRepository;
}
}
现在其次是身份验证yml属性。
server:
port: 8081
spring:
application:
name: auth-service
cloud:
consul:
discovery:
healthCheckPath: /health
healthChechInterval: 15s
prefer-ip-address: true
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
host: 127.0.0.1
port: 8500
config:
enabled: true
watch:
enabled: true
sleuth:
sampler:
percentage: 1
security:
basic:
enabled : false
身份验证服务器配置。
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Override
public void configure(final AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("zuulfilter")
.secret("secret")
.authorizedGrantTypes("authorization_code", "refresh_token")
.redirectUris("http://localhost:8081")
.scopes("openid");
}
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore());
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("examplekeystore.jks"), "1234".toCharArray());
jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("examplekeystore"));
return jwtAccessTokenConverter;
}
}
身份验证网络安全配置。
@Configuration
public class LoginConfiguration extends WebSecurityConfigurerAdapter {
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/test").permitAll().anyRequest().authenticated().and()
.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll();
httpSecurity.formLogin().loginPage("/login").permitAll().
and().
authorizeRequests().
anyRequest().
authenticated();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
@Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
PasswordEncoder encoder = passwordEncoder();
String password = encoder.encode("1234");
authManagerBuilder.inMemoryAuthentication().passwordEncoder(encoder).
withUser("deneme").
password(password).
roles("USER");
}
}
谢谢你的帮助。