英文:
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();
}
}
请同时查看浏览器控制台。
<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'm missing something but can'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's any help.
Here'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'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("*"));
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();
}
}
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("*"));
//or any domain that you want to restrict to
configuration.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept","Authorization"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
//Add the method support as you like
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", 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 `"Authorization"` that you have added to the request is not allowed by the cors configuration as you have specified only the headers `"Origin", "Content-Type", "Accept"` in your cors configuration. Add the `"Authorization"` header as well to cors configuration and the issue should be fixed.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论