无法解决 Spring Boot 和 Angular 的 CORS 问题。

huangapple go评论87阅读模式
英文:

Unable to fix CORS issue with Spring Boot and Angular

问题

我是Spring Boot和Angular的新手,正在处理CORS问题。

我想要实现一个全局CORS过滤器,并尝试使用CorsFilter、WebMvcConfigurer以及WebMvcConfigurerAdapter类来实现,但是似乎这些方法都不起作用。我知道我漏掉了什么,但是无法弄清楚是什么。如果有人能够在我的代码中找到解决方案,将会非常有帮助。

我还实现了基本身份验证,并添加了相关代码,如果有帮助的话。

以下是我的代码。

**Angular API调用**

**api.service.ts**
```typescript
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  constructor(private httpClient: HttpClient) { }

  userLogin(username: String, password: String) {
    let postBody = {
      "username": username,
      "password": password
    };
    let httpOptions = {
      headers: new HttpHeaders({
        "Content-Type": "application/json",
        "Authorization": "Basic " + btoa('myUsername:myPassword')
      })
    }
    return this.httpClient.post("http://localhost:8080/userLogin", postBody, httpOptions);
  }

}

以下是我的API代码。

MainApp.java

@SpringBootApplication
public class MainApp extends SpringBootServletInitializer {
    public static void main(String... str) {
        SpringApplication.run(MainApp.class, str);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(MainApp.class);
    }

}

Config.java

@Configuration
public class Config {

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        configuration.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept"));
        configuration.setAllowedMethods(Arrays.asList("POST"));
        source.registerCorsConfiguration("/**", configuration);
        return new CorsFilter(source);
    }
}

NotebookController.java

@RestController
public class NotebookController {

    @Autowired
    IUsersRepo usersRepo;
    @Autowired
    INotesRepo notesRepo;

    @PostMapping("userLogin")
    @ResponseBody
    public ResponseEntity<String> userLogin(@RequestParam("username") String username, @RequestParam("password") String password) {
        ObjectWriter objectWriter = new ObjectMapper().writer().withDefaultPrettyPrinter();
        List<Users> usersList = usersRepo.findByUsernameEqualsAndPasswordEquals(username, password);
        boolean userVerified = usersList.size() > 0 ? true : false;
        HashMap<String, Boolean> outputMap = new HashMap<>();
        outputMap.put("authenticated", userVerified);
        String outputJson = "";
        try {
            outputJson = objectWriter.writeValueAsString(outputMap);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        outputJson = outputJson.toString();
        ResponseEntity<String> response = new ResponseEntity<String>(outputJson, HttpStatus.OK);
        return response;
    }
}

Authentication.java

@Configuration
@EnableWebSecurity
public class Authentication extends WebSecurityConfigurerAdapter {

    @Autowired
    AuthenticationEntryPoint authEntryPoint;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.cors().and().csrf().disable();

        // Authorise all incoming requests
        http.authorizeRequests().and().httpBasic();
        http.authorizeRequests().anyRequest().authenticated();

        // Use AuthenticationEntryPoint to authorise username/password
        http.httpBasic().authenticationEntryPoint(authEntryPoint);
    }

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        configuration.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept"));
        configuration.setAllowedMethods(Arrays.asList("POST"));
        source.registerCorsConfiguration("/**", configuration);
        return new CorsFilter(source);
    }

    @Bean
    public BCryptPasswordEncoder getEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        String username = "myUsername";
        String password = "myPassword";

        InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> mngConfig = authenticationManagerBuilder.inMemoryAuthentication();

        mngConfig.withUser(username).password(password).roles("USER");
    }

}

AuthEntryPoint.java

@Component
public class AuthEntryPoint extends BasicAuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        super.commence(request, response, authException);
        response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName());
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        PrintWriter writer = response.getWriter();
        writer.println("HTTP Status 401: Unauthorised | " + authException.getMessage());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        setRealmName("Online Notebook");
        super.afterPropertiesSet();
    }

}

请同时查看浏览器控制台。

https://imgur.com/a/hHceJDw


<details>
<summary>英文:</summary>
I am new to Spring Boot and Angular and struggling with the CORS issue. 
I want to implement a global CORS filter and tried implementing CorsFilter, WebMvcConfigurer and also tried with WebMvcConfigurerAdapter class but none of the methods seem to be working. I know I&#39;m missing something but can&#39;t figure out what. If someone could please find a solution in my code, it would be great help. 
I also have implemented Basic Auth and added that code if that&#39;s any help.
Here&#39;s my code.
**Angular API Call**
**api.service.ts**

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable({
providedIn: 'root'
})
export class ApiService {

constructor(private httpClient: HttpClient) { }

userLogin(username: String, password: String) {
let postBody = {
"username" : username,
"password" : password
};
let httpOptions = {
headers : new HttpHeaders({
"Content-Type" : "application/json",
"Authorization" : "Basic " + btoa('myUsername:myPassword')
})
}
return this.httpClient.post("http://localhost:8080/userLogin", postBody, httpOptions);
}

}


Here&#39;s my API code.
**MainApp.java**

@SpringBootApplication
public class MainApp extends SpringBootServletInitializer {
public static void main(String... str) {
SpringApplication.run(MainApp.class, str);
}

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(MainApp.class);
}

}


**Config.java**

@Configuration
public class Config {

@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOrigins(Collections.singletonList(&quot;*&quot;));
configuration.setAllowedHeaders(Arrays.asList(&quot;Origin&quot;, &quot;Content-Type&quot;, &quot;Accept&quot;));
configuration.setAllowedMethods(Arrays.asList(&quot;POST&quot;));
source.registerCorsConfiguration(&quot;/**&quot;, configuration);
return new CorsFilter(source);
}

}


**NotebookController.java**

@RestController
public class NotebookController {

@Autowired
IUsersRepo usersRepo;
@Autowired
INotesRepo notesRepo;
@PostMapping(&quot;userLogin&quot;)
@ResponseBody
public ResponseEntity&lt;String&gt; userLogin(@RequestParam(&quot;username&quot;) String username, @RequestParam(&quot;password&quot;) String password) {
ObjectWriter objectWriter = new ObjectMapper().writer().withDefaultPrettyPrinter();
List&lt;Users&gt; usersList = usersRepo.findByUsernameEqualsAndPasswordEquals(username, password);
boolean userVerified = usersList.size() &gt; 0 ? true : false;
HashMap&lt;String, Boolean&gt; outputMap = new HashMap&lt;&gt;();
outputMap.put(&quot;authenticated&quot;, userVerified);
String outputJson = &quot;&quot;;
try {
outputJson = objectWriter.writeValueAsString(outputMap);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
outputJson = outputJson.toString();
ResponseEntity&lt;String&gt; response = new ResponseEntity&lt;String&gt;(outputJson, HttpStatus.OK);
return response;
}

}


**Authentication.java**

@Configuration
@EnableWebSecurity
public class Authentication extends WebSecurityConfigurerAdapter {

@Autowired
AuthenticationEntryPoint authEntryPoint;
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.cors().and().csrf().disable();
//Authorise all incoming requests
http.authorizeRequests().and().httpBasic();
http.authorizeRequests().anyRequest().authenticated();
//Use AuthenticationEntryPoint to authorise username/password
http.httpBasic().authenticationEntryPoint(authEntryPoint);
}
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOrigins(Collections.singletonList(&quot;*&quot;));
configuration.setAllowedHeaders(Arrays.asList(&quot;Origin&quot;, &quot;Content-Type&quot;, &quot;Accept&quot;));
configuration.setAllowedMethods(Arrays.asList(&quot;POST&quot;));
source.registerCorsConfiguration(&quot;/**&quot;, configuration);
return new CorsFilter(source);
}
@Bean
public BCryptPasswordEncoder getEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
String username = &quot;myUsername&quot;;
String password = &quot;myPassword&quot;;
InMemoryUserDetailsManagerConfigurer&lt;AuthenticationManagerBuilder&gt; mngConfig = authenticationManagerBuilder.inMemoryAuthentication();
mngConfig.withUser(username).password(password).roles(&quot;USER&quot;);
}

}


**AuthEntryPoint.java**

@Component
public class AuthEntryPoint extends BasicAuthenticationEntryPoint {

@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
super.commence(request, response, authException);
response.addHeader(&quot;WWW-Authenticate&quot;, &quot;Basic realm=&quot; + getRealmName());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println(&quot;HTTP Status 401: Unauthorised | &quot; + authException.getMessage());
}
@Override
public void afterPropertiesSet() throws Exception {
setRealmName(&quot;Online Notebook&quot;);
super.afterPropertiesSet();
}

}


Please see the browser console as well.
https://imgur.com/a/hHceJDw
</details>
# 答案1
**得分**: 0

由于您正在使用 Spring Security,您可以像下面这样配置 Spring Security,以便将 CORS 与 Spring Security 一起使用,以便预检查请求能够通过 Spring Security。

@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
//其他您想要的配置
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
//或者您想要限制的任何域
configuration.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
//根据需要添加支持的方法
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

您可能遇到的问题是 Spring Security 不允许预检查请求通过,导致预检查请求失败。为了进行更详细的分析,请分享您的浏览器日志或请求中的错误信息。

从浏览器错误中可以看出,您在请求中添加的头部 "Authorization" 在 CORS 配置中是不允许的,因为您在 CORS 配置中只指定了 "Origin"、"Content-Type" 和 "Accept" 这些头部。请将 "Authorization" 头部也添加到 CORS 配置中,问题应该会得到解决。


<details>
<summary>英文:</summary>
Since you are using spring security , you could configure cors along with spring security like below so that the pre-flight check request goes through spring security.
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
//other configurations that you want
}
@Bean
CorsConfigurationSource corsConfigurationSource()
{
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList(&quot;*&quot;));
//or any domain that you want to restrict to 
configuration.setAllowedHeaders(Arrays.asList(&quot;Origin&quot;, &quot;Content-Type&quot;, &quot;Accept&quot;,&quot;Authorization&quot;));
configuration.setAllowedMethods(Arrays.asList(&quot;GET&quot;,&quot;POST&quot;));
//Add the method support as you like 
UrlBasedCorsConfigurationSource source = new     UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(&quot;/**&quot;, configuration);
return source;
}
The issue that you might be facing is that spring security is not letting through the pre-flight checkup requests and failing them. For more analysis , please share your browser logs or errors from the request .
From the browser error it can be seen that the header `&quot;Authorization&quot;` that you have added to the request is not allowed by the cors configuration as you have specified only the headers `&quot;Origin&quot;, &quot;Content-Type&quot;, &quot;Accept&quot;` in your cors configuration. Add the `&quot;Authorization&quot;` header as well to cors configuration and the issue should be fixed.
</details>

huangapple
  • 本文由 发表于 2020年4月7日 13:46:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/61073514.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定