使用 Spring Boot、Apache 和 Tomcat 在客户端/服务器系统中配置 CORS

Posted

技术标签:

【中文标题】使用 Spring Boot、Apache 和 Tomcat 在客户端/服务器系统中配置 CORS【英文标题】:Config CORS in a client/server system with Spring Boot, Apache and Tomcat 【发布时间】:2019-06-12 22:11:14 【问题描述】:

我是第一次开发客户端-服务器架构,我在配置服务器以接受 CORS 时遇到了一些问题。

我已经阅读、搜索和测试了很多,但我无法让它在我的系统中运行,我不知道出了什么问题。

我在带有 Oauth2 安全性的 Spring Boot 2.0.4 中开发了客户端 inAngular 和 Web 服务。在服务器上运行着一个 Apache,它只接受来自端口 443 的请求来为 Web 提供服务,并通过端口 8443 将请求重定向到部署在 Tomcat 8.5 中的 Web 服务,该服务正在侦听端口 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>

安全配置

@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()");
    

资源服务器配置

@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()

从源“https:// ---.---.---”访问位于“https://---.---.---:8443/folder/oauth/token”的 XMLHttpRequest 已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:它没有 HTTP ok 状态。

【问题讨论】:

【参考方案1】:

您是否在客户端发送“带有凭据”的标头?如果它是一个 Angular 7 应用程序,您必须在服务器端允许 with credentials 标头,添加 cors 配置,并在客户端为每个 http 客户端请求添加一个拦截器。除此之外,您不应该让“*”作为允许的来源,否则 with credentials 标头将不起作用。

在 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;

【讨论】:

我没有使用'withCredentials'标头,应该使用它吗? 是的,没有那个 spring 不会在客户端设置隐含的 cookie 来授权你的应用程序。 我不知道该怎么做,你能用一些代码更新你的答案吗? 更新了 angular 和 java 代码。如果要查看过滤器链的顺序,请在 FilterChainProxy 的方法 doFilter 上设置断点并检查 filterChain 对象。

以上是关于使用 Spring Boot、Apache 和 Tomcat 在客户端/服务器系统中配置 CORS的主要内容,如果未能解决你的问题,请参考以下文章

带有 JavaConfig 和 Spring Boot 的 Apache Shiro JdbcRealm

带有 JWT 的 Spring Boot 和 Apache Shiro - 我使用正确吗?

Spring Boot 整合 Apache Dubbo

Spring Boot 整合 Apache Dubbo

使用 Spring Boot、Apache 和 Tomcat 在客户端/服务器系统中配置 CORS

使用 Apache Shiro 的 Spring Boot