spring boot cors表单后身份验证不仅适用于获取api

dffbzjpn  于 2021-09-29  发布在  Java
关注(0)|答案(1)|浏览(178)

我正在建设我的第一个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甚至没有相同的内容:
在此处输入图像描述
如有任何建议,将不胜感激
非常令人困惑的是,表单机制的哪一部分起作用和哪一部分不起作用并没有明确的界限
基本上我想要一个完全解耦的前端

h4cxqtbf

h4cxqtbf1#

我相信错误会告诉你你需要什么(强调我的):
同源策略不允许在以下位置读取远程资源(原因:如果cors标头“访问控制允许来源”为“*”,则不支持凭据)
您的配置:

corsConfiguration.setAllowedOrigins(Arrays.asList("*"));

与您发送凭据的愿望相冲突。
我建议看一看spring的cors指南以获得更多帮助。

相关问题