我正在建设我的第一个Spring Boot 项目的大学,我只是得到应用程序工作流程前端和后端工作,有一个非常奇怪的事情正在进行。
我有一个react前端,目标是允许多个客户端访问同一个后端。
我希望能够从多个客户端连接到后端。
我已经成功地设置了spring引导,并从同一端口进行了响应。但是我使用了一个不同的端口只是为了测试。我已经检查过cors是否有效。
我已经设法禁用了cors,表单登录可以从postman和html(csrf)中的一个虚拟表单中工作,但我似乎无法让它与FetchAPI一起工作。
如有任何建议,将不胜感激。代码如下:
//WebSecurityConfigurerAdapter.java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig
extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userAccountManager;
@Override
protected void configure(
AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userAccountManager);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//Whole login form breaks without this?
super.configure(http);
http
.cors()
.configurationSource(getCorsConfigurationSource())
.and()
.csrf()
.disable();
}
@Bean
public CorsConfigurationSource getCorsConfigurationSource(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(
Arrays.asList("GET","POST","PUT","DELETE","OPTIONS")
);
corsConfiguration.setAllowedHeaders(
Collections.singletonList("*"));
corsConfiguration.addExposedHeader(
"Access-Control-Allow-Origin"
);
UrlBasedCorsConfigurationSource
source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
@Bean
public PasswordEncoder getPasswordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
//App.java Sends the React App
@Controller
public class App {
@GetMapping("/")
public ModelAndView index(){
return new ModelAndView("index");
}
}
//UserDetailsService
public class UserAccountManager implements UserDetailsService {
@Autowired
private UserRepositoryInterface userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findUserByUsername(username);
if (user == null){
throw new UsernameNotFoundException("User: " + username + " not found.");
}
return user;
}
}
@Entity
public class User implements Serializable, UserDetails {
@Id
private long id;
private String firstName;
private String lastName;
private String username;
private String password;
private String startDateTime;
@OneToMany
@JoinColumn(name = "userId")
private List<CurrencyAccount> currencyAccounts;
public User() {
}
public User(
String firstName,
String lastName,
String username,
String password
){
this.firstName = firstName;
this.lastName = lastName;
this.username = username;
this.password = password;
}
public long getId() {
return id;
}
public User setId(long id) {
this.id = id;
return this;
}
public String getFirstName() {
return firstName;
}
public User setFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public String getLastName() {
return lastName;
}
public User setLastName(String lastName) {
this.lastName = lastName;
return this;
}
@Override
public String getUsername() {
return username;
}
public User setUsername(String username) {
this.username = username;
return this;
}
@Override
public String getPassword() {
return password;
}
public User setPassword(String password) {
this.password = password;
return this;
}
public String getStartDateTime() {
return startDateTime;
}
public User setStartDateTime(LocalDateTime startDateTime) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
this.startDateTime = dtf.format(startDateTime);
return this;
}
public List<CurrencyAccount> getCurrencyAccounts() {
return currencyAccounts;
}
public User addCurrencyAccount(CurrencyAccount currencyAccount) {
this.currencyAccounts.add(currencyAccount);
return this;
}
/*
* For now we are hard coding the default ROLE USER.
* Later we will make provisions for extra roles
* */
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority>
authorities = new ArrayList<SimpleGrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("USER"));
return authorities;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
/*Crappy methods:
* All these need to return true otherwise we cant access the account
*
* */
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
结果:
它可以在浏览器上完美地工作。
Postman 的作品非常完美,
当我禁用csrf时,我甚至成功地从浏览器进行了csrf攻击。但不知怎的,它在fetchapi中无法工作?
我甚至能够从源代码复制表单,将其粘贴到本地主机上的另一个端口,并发出成功的请求。但是,当我尝试使用fetchapi时。我得到401未经授权?很奇怪
SpringBoot的默认表单机制是打开的,使用/login进行登录,使用/logout进行注销。
以下是基本的javascript代码:
let jsonBody = JSON.stringify({
"username":"ha07",
"password":"ha07"
});
console.log(jsonBody);
let ftch = fetch("http://localhost:8080/", {
method: "POST",
mode: "cors",
body: new URLSearchParams({
username: "ha07",
password: "ha07"
}),
});
ftch
.then(response => {
console.log(response);
return response.text();
})
.then(data => {
console.log(data);
})
.catch(error => {
console.log(`Error: ${error}`)
});
我想它可能无法理解json主体,所以我改为urlsearchparams没有区别。
然后我尝试了这两种数据类型,与postman完美配合。
编辑:当我在提取请求中包含凭据时,我得到一个cors错误:
function App() {
useEffect(() => {
fetch("http://localhost:8080/login", {
method: "POST",
mode: 'cors',
headers:{
Origin: "http://localhost:3000"
},
credentials: 'include',
body: new URLSearchParams({
username: 'ha07',
password: 'ha07'
})
})
.then((response) => {
console.log(response);
})
},[]);
return (
<div className="App">
<h1>Hello</h1>
</div>
);
}
在此处输入图像描述
当我拿走凭证:“包括”时,我得到401:
在此处输入图像描述
cookie甚至没有相同的内容:
在此处输入图像描述
如有任何建议,将不胜感激
非常令人困惑的是,表单机制的哪一部分起作用和哪一部分不起作用并没有明确的界限
基本上我想要一个完全解耦的前端
1条答案
按热度按时间h4cxqtbf1#
我相信错误会告诉你你需要什么(强调我的):
同源策略不允许在以下位置读取远程资源(原因:如果cors标头“访问控制允许来源”为“*”,则不支持凭据)
您的配置:
与您发送凭据的愿望相冲突。
我建议看一看spring的cors指南以获得更多帮助。