如果在微服务中具有自定义安全配置,则Zuul不进行转发

时间:2018-03-17 19:23:39

标签: spring spring-boot spring-cloud spring-security-oauth2 spring-cloud-netflix

使用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");
    }

}

谢谢你的帮助。

0 个答案:

没有答案