CORS 预检通道在 Spring Security 中没有成功
Posted
技术标签:
【中文标题】CORS 预检通道在 Spring Security 中没有成功【英文标题】:CORS preflight channel did not succeed with Spring Security 【发布时间】:2017-11-01 21:36:22 【问题描述】:我正在构建一个带有 Spring Boot 后端的 Angular 2 应用程序。我正在尝试解决 CORS 预检的问题好几天。根据这个topic,它应该像这样与CORS过滤器一起使用:
@Component
public class CorsFilter extends OncePerRequestFilter
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token");
response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
if ("OPTIONS".equals(request.getMethod()))
response.setStatus(HttpServletResponse.SC_OK);
else
filterChain.doFilter(request, response);
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
@Override
public void configure(HttpSecurity http) throws Exception
http
.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class)
.headers()
.frameOptions().disable()
.and()
.authorizeRequests()
.antMatchers("/", "/home", "/register", "/login").permitAll()
.antMatchers("/cottages").authenticated();
角度前端:
import Injectable from '@angular/core';
import Headers, Http from "@angular/http";
import AppSettings from "../app.settings";
import URLSearchParams from '@angular/http'
import User from "../_models/_index";
import Observable from "rxjs";
@Injectable()
export class AuthenticationService
private headers = new Headers('Content-Type': 'application/json');
private tokenHeaders = new Headers(
'Content-Type': 'application/json',
'client_id': 'xxx',
'client_secret': 'xxx');
constructor(private http: Http)
login(user: User)
let urlSearchParams = new URLSearchParams();
urlSearchParams.append('username', user.username);
urlSearchParams.append('password', user.password);
let body = urlSearchParams.toString();
return this.http.post(AppSettings.getApiUrl() + "oauth/token", body, withCredentials: true, headers: this.tokenHeaders )
.map((responseData) =>
return responseData.json();
)
.map((item: any) =>
return new User(item);
)
.catch((error: any) => Observable.of(error.json().error || 'Server error'));
我尝试了在 this 和 Spring 文档的其他来源中找到的其他配置。
我总是收到此错误消息:
跨域请求被阻止:同源策略不允许读取 http://localhost:8080/oauth/token 的远程资源。 (原因: CORS 预检通道未成功)。
对我自己的控制器的简单 CORS 请求(例如注册用户)效果很好。
谁能向我解释我做错了什么?我的 Java 或 Typescript 代码有错误吗?
编辑:
授权服务器配置:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception
endpoints.authenticationManager(authenticationManager);
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
clients.inMemory().withClient("my-trusted-client").authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT").scopes("read", "write", "trust")
.resourceIds("oauth2-resource").accessTokenValiditySeconds(5000).secret("xxx");
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception
security.checkTokenAccess("isAuthenticated()");
【问题讨论】:
您是否将路径/oauth/token
添加到您的 permitAll()
部分?
感谢您的回答@ochi。我没有。我在 ResourceServerConfiguration 中尝试过这种方式:.authorizeRequests().antMatchers("/", "/oauth/token").permitAll().antMatchers("/cottages").authenticated();
不幸的是它不能解决问题或让我出错?
【参考方案1】:
我终于找到了解决问题的方法。双方都有几个错误(Angular/Java Spring Boot、Security)。我将在这里发布我的工作代码并进行解释。 我将从后端开始:
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter
@Override
public void configure(HttpSecurity http) throws Exception
http
.authorizeRequests()
.antMatchers("/", "/users").permitAll().anyRequest()
.authenticated()
.and()
.csrf().disable()
根据 spring.io 教程,WebSecurityConfiguration 是我工作的更好选择 - 它也可以与 ResourceServerConfiguration 一起使用。老实说,我不知道有什么区别(什么时候必须使用这个,什么时候使用另一个)。
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter
public SimpleCorsFilter()
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, content-type");
if ("OPTIONS".equalsIgnoreCase(request.getMethod()))
response.setStatus(HttpServletResponse.SC_OK);
else
chain.doFilter(req, res);
@Override
public void init(FilterConfig filterConfig)
@Override
public void destroy()
如果没有这个 CorsFilter,我只能从服务器获得 OPTIONS 响应。
我没有更改上面发布的 AuthorizationServerConfiguration。
实际上大多数错误都发生在 Angular / 前端。这对我有用:
@Injectable()
export class AuthenticationService
private headers = new Headers('Content-Type': 'application/json');
private auth64 = btoa("my-trusted-client:secret");
private tokenHeaders = new Headers(
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic '+this.auth64
);
constructor(private http: Http)
login(user: User)
let body = new URLSearchParams();
body.append('grant_type', 'password');
body.append('username', user.username);
body.append('password', user.password);
return this.http.post(AppSettings.getApiUrl() + "oauth/token", body, headers: this.tokenHeaders)
.map(data =>
console.log("it works!");
, error =>
console.log(error.json());
);
之前的映射有问题。它总是导致预检 CORS 问题。我没有收到此映射的预检错误消息 - 即使我不使用 CORS 过滤器 - 但您需要 CORS 过滤器才能从服务器获取 OPTIONS 响应之外的其他内容。
因此,我收到以下错误消息(在 JSON 响应中): "访问此资源需要完全身份验证"
为了解决这个问题,我做了以下步骤:
将内容类型更改为 application/x-www-form-urlencoded(对 oauth2 很重要) 删除标头client_id/client_secret 添加授权标头 使用 Base64(btoa) 对我的 client_id/client_secrect 的值进行编码 将编码值添加到我的授权标头也许有其他/更好的方法来解决这些问题,但这段代码对我来说很好 - 也许它可以帮助这里的其他人:-)
【讨论】:
哇,我的头撞到墙上了。非常感谢你这么详细的解释。我的问题是相同的飞行前 CORS 没有通过。将 OPTIONS 设置为返回 OK,就成功了。谢谢。【参考方案2】:您在请求中将withCredentials
设置为true
,但在预检响应中缺少Access-Control-Allow-Credentials
。请参阅here 了解标题。
return ...withCredentials: true, headers: this.tokenHeaders )
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token");
response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
// add this
response.setHeader("Access-Control-Allow-Headers", "true");
我注意到您在请求中添加了一些其他头像,例如 client_id
,但您没有将它们添加到 Access-Control-Allow-Headers
。
【讨论】:
感谢您的回答!不幸的是,这对我不起作用。我将此行和缺少的标头(client_id 和 client_secret)添加到允许的标头中。我仍然收到Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/oauth/token. (Reason: CORS preflight channel did not succeed).
以上是关于CORS 预检通道在 Spring Security 中没有成功的主要内容,如果未能解决你的问题,请参考以下文章
来自 CORS 预检通道的 CORS 标头“Access-Control-Allow-Headers”中缺少令牌
来自 CORS 预检通道的 CORS 标头“Access-Control-Allow-Headers”中缺少令牌“x-auth”[重复]
来自 CORS 预检通道的 CORS 标头“Access-Control-Allow-Headers”中缺少令牌“access-control-allow-origin”