我正在使用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()");
}
}
答案 0 :(得分:2)
最后,我有一个解决我的问题的方法。双方都有几个错误(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 / Frontend方面。这对我有用:
@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响应中): "访问此资源需要完全身份验证"
要解决此问题,我执行了以下步骤:
也许有其他/更好的方法来解决这些问题,但这段代码对我来说很好 - 也许它可以帮助其他人: - )
答案 1 :(得分:0)
您在请求中将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
。