typescript - CORS 预检 channel 在 Spring Security 中未成功

标签 typescript spring-boot spring-security cors spring-security-oauth2

我正在构建一个带有 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 文档中的其他来源。

我总是收到这个错误信息:

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).

向我自己的 Controller 发出简单的 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()");
     }
}

最佳答案

我终于找到了解决问题的方法。双方都有几个错误(Angular/Java Spring Boot,安全)。我将在这里发布我的工作代码并进行解释。 我将从后端开始:

@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 响应中): “访问此资源需要完全身份验证”

为了解决这个问题,我执行了以下步骤:

  • 将内容类型更改为 application/x-www-form-urlencoded(对于 oauth2 很重要)
  • 删除 header client_id/client_secret
  • 添加授权 header
  • 使用 Base64(btoa) 对我的 client_id/client_secrect 的值进行编码
  • 将编码值添加到我的授权 header

也许有其他/更好的方法来解决这些问题,但这段代码对我来说工作正常 - 也许它可以帮助这里的其他人:-)

关于typescript - CORS 预检 channel 在 Spring Security 中未成功,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44295642/

相关文章:

TypeScript:Array.find 实际上可能未定义,但它作为固定类型返回

java - Spring 启动 : change the upload max size for a single endpoint

java - Spring @JmsListener 无法将 json 转换为对象

postgresql - Liquibase/Spring Boot/Postgres - 架构名称不起作用

java - 使用自定义范围/角色保护 Spring Boot 应用程序

java - 如何保护 RepositoryRestController 的安全

javascript - 检测 Angular 上的属性变化

typescript - WebStorm、空代码块和大括号

visual-studio - 是否可以在 Visual Studio 中为 TypeScript 方法/类生成注释?

spring-mvc - Spring Boot 过滤器顺序 : WebLogic 12c vs Tomcat 8