使用 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 - 我使用正确吗?