简体   繁体   中英

Config CORS in a client/server system with Spring Boot, Apache and Tomcat

I am developing a client-server architecture for the first time and I have some problems to configure the server to accept CORS.

I've read, searched and test a lot, but I can not make it work in my system, I do not know what is wrong.

I developed the client inAngular and the web service in Spring Boot 2.0.4 with Oauth2 security. On the server there are running an Apache that only accepts requests from port 443 to serve the web and redirect requests through port 8443 to the web service deployed in Tomcat 8.5 that is listening on port 8081.

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

Changes that I made in the Apache configuration

<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());
    }
}

And i getting an error message like this when i try to login in the web

Failed to load resource: the server responded with a status of 403 ()

Access to XMLHttpRequest at ' https://---.---.---:8443/folder/oauth/token ' from origin 'https:// ---.---.---' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

Are you sending the header "with credentials" on the client side? If it`s an angular 7 app you have to allow the with credentials header on the server side, adding on the cors configuration, and add an interceptor on the client side for every http client request. Besides that, you should not let "*" as allowed origins or the with credentials header will not work.

On Angular create this:

@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);
}
}

And add to app.module:

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

Another problem could be the order of the cors filter, it should be before the security filter on filterChain. You can handle it, with something like this:

@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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM