Настройте CORS в системе клиент-сервер с помощью Spring Boot, Apache и Tomcat. - PullRequest
0 голосов
/ 18 января 2019

Я впервые разрабатываю архитектуру клиент-сервер, и у меня возникли некоторые проблемы с настройкой сервера для приема CORS.

Я много читал, искал и тестировал, но я не могу заставить его работать в моей системе, я не знаю, что не так.

Я разработал клиент в Angular и веб-сервис в Spring Boot 2.0.4 с безопасностью Oauth2. На сервере работает Apache, который принимает запросы только от порта 443 для обслуживания Интернета и перенаправляет запросы через порт 8443 на веб-службу, развернутую в Tomcat 8.5, которая прослушивает порт 8081.

<VirtualHost _default_:8443>
    ProxyPass / http://localhost:8081/
    ProxyPassReverse / http://localhost:8081/
    DocumentRoot /var/www/html
    ...

Изменения, которые я сделал в конфигурации Apache

<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "*"
    Header set Access-Control-Allow-Headers "authorization"
</IfModule>

SecurityConfig

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception
    {
        return super.authenticationManagerBean();
    }

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception
    { 
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(encoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        //@f:off
        http.cors()
            .and()
            .csrf()
            .disable()
            .anonymous()
            .disable()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .anyRequest()
            .authenticated();
        //@f:on
    }

    @Override
    public void configure(WebSecurity web) throws Exception
    {
        super.configure(web);
        web.ignoring()
        .antMatchers("/v1/user/save")
        .antMatchers("/v1/user/existsEMail")
        .antMatchers("/v1/userAccess/existsUsername");

        web.ignoring()
        .antMatchers(HttpMethod.OPTIONS,"/**");
    }

    @Bean
    public TokenStore tokenStore()
    {
        return new InMemoryTokenStore();
    }

    @Bean
    @Autowired
    public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore)
    {
        TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
        handler.setTokenStore(tokenStore);
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
        handler.setClientDetailsService(clientDetailsService);

        return handler;
    }

    @Bean
    @Autowired
    public ApprovalStore approvalStore(TokenStore tokenStore)
    {
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(tokenStore);

        return store;
    }

    @Bean
    public BCryptPasswordEncoder encoder()
    {
        BytesKeyGenerator keyGenerator = KeyGenerators.secureRandom();
        SecureRandom      random       = new SecureRandom(keyGenerator.generateKey());

        return new BCryptPasswordEncoder(10, random);
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource()
    {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("*");
        config.setAllowedOrigins(Arrays.asList("*"));
        config.setAllowedMethods(Arrays.asList(
                HttpMethod.GET.name(),
                HttpMethod.HEAD.name(),
                HttpMethod.POST.name(),
                HttpMethod.PUT.name(),
                HttpMethod.DELETE.name()));
        config.setAllowCredentials(true);
        config.combine(config.applyPermitDefaultValues());

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        return source;
    }
}

AuthorizationServerConfig

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter
{
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private UserAccessService userDetailsService;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(ClientDetailsServiceConfigurer configurer) throws Exception
    { 
        configurer.inMemory()
                .withClient(SecurityConstant.CLIENT_ID)
                .secret(SecurityConstant.CLIENT_SECRET)
.accessTokenValiditySeconds(SecurityConstant.ACCESS_TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(SecurityConstant.REFRESH_TOKEN_VALIDITY_SECONDS)
                .scopes(SecurityConstant.SCOPE_READ, SecurityConstant.SCOPE_WRITE) 
                .authorizedGrantTypes(SecurityConstant.GRANT_TYPE_PASSWORD, SecurityConstant.REFRESH_TOKEN)
                .resourceIds(SecurityConstant.RESOURCE_ID);

    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
    { 
        endpoints.tokenStore(tokenStore)
                .userApprovalHandler(userApprovalHandler)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .tokenEnhancer(new CustomTokenEnhancer());

        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.POST);

    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception
    {
        super.configure(security);
        security.checkTokenAccess("permitAll()");
    }
}

ResourceServerConfig

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter
{
    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources)
    { 
        resources.tokenStore(tokenStore)
                .resourceId(SecurityConstant.RESOURCE_ID); 
    }

    @Override
    public void configure(HttpSecurity http) throws Exception
    { 
        http.formLogin().disable()
            .anonymous().disable()
            .authorizeRequests()
            .antMatchers(Uri.DIET + "/**").authenticated()
            .anyRequest()
            .authenticated()
            .and()
            .exceptionHandling()
            .accessDeniedHandler(new OAuth2AccessDeniedHandler());
    }
}

И я получаю сообщение об ошибке, подобное этому, когда я пытаюсь войти в сеть

Не удалось загрузить ресурс: сервер ответил со статусом 403 ()

Доступ к XMLHttpRequest по адресу https: //---.---.---: 8443 / folder / oauth / token 'from origin' https: // ---.- --.--- 'был заблокирован политикой CORS: Ответ на предполётный запрос не проходит проверку контроля доступа: у него нет статуса HTTP ok.

1 Ответ

0 голосов
/ 18 января 2019

Отправляете ли вы заголовок "с учетными данными" на стороне клиента?Если это угловое 7 приложение, вы должны разрешить заголовок with credentials на стороне сервера, добавить конфигурацию cors и добавить перехватчик на стороне клиента для каждого http-запроса клиента.Кроме того, вы не должны указывать "*" как разрешенные источники, иначе заголовок с учетными данными не будет работать.

В Angular создайте это:

@Injectable()
export class CredentialsInterceptor implements HttpInterceptor {
constructor() {}

intercept(request: HttpRequest<any>, next: HttpHandler): 
Observable<HttpEvent<any>> {

request = request = request.clone({
    withCredentials: true
});
return next.handle(request);
}
}

И добавьте в app.module:

providers: [{
provide: HTTP_INTERCEPTORS,
useClass: CredentialsInterceptor,
multi: true
}  

Другой проблемой может быть порядок фильтра cors, он должен быть до фильтра безопасности на filterChain.Вы можете справиться с этим примерно так:

@Bean
FilterRegistrationBean<CorsFilter> corsFilter(CorsConfigurationSource 
corsConfigurationSource)
{
CorsFilter corsFilter = new CorsFilter(corsConfigurationSource);

FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<> 
();
bean.setFilter(corsFilter);
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
...