Spring + Vue登录:超时和错误处理

osh3o9ms  于 2022-11-17  发布在  Vue.js
关注(0)|答案(1)|浏览(123)

我试图在Spring中建立一个后端,在Vue中建立一个前端。同一台机器,分开的项目。我解决或禁用了所有的cors,csrf和登录问题,但这一个让我完全飘飘欲仙。
Spring :SecurityConfig.java

package main.java.it.coderevo.security.config;

import java.util.Arrays;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    DataSource dataSource;

    @Autowired
    @Qualifier("allowedOrigin")
    String allowedOriginDev;

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {

        auth
            .jdbcAuthentication().dataSource(dataSource)
            .passwordEncoder(passwordEncoder())
            .usersByUsernameQuery(
                  " SELECT username, password, enabled "
                + " FROM User where username = ?"
            )
            .authoritiesByUsernameQuery(
                  " SELECT username, Role.name as 'role' "
                + " FROM User "
                +   " JOIN Role ON User.idRole = Role.id "
                + " WHERE username = ?"             
            );
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {

        http
            .authorizeRequests()
                .antMatchers("/god**").hasRole("GOD")
                .antMatchers("/admin**").hasAnyRole("ADMIN", "GOD")
                .antMatchers("/api**").hasAnyRole("USER", "ADMIN", "GOD")
                .antMatchers("/login**").permitAll()
                .antMatchers("/public**").permitAll()
                .anyRequest().authenticated()
            .and()
                .logout()
                .clearAuthentication(true)
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID") 
            .and()
                .cors()
            .and()
                .formLogin().disable()
                .csrf().disable().httpBasic();
    }

    // To enable CORS
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public CorsConfigurationSource corsConfigurationSource() {

        final CorsConfiguration configuration = new CorsConfiguration();

        configuration.setAllowCredentials(true);

        configuration.setAllowedOrigins(Arrays.asList(allowedOriginDev));
        configuration.setAllowedMethods(Arrays.asList(
                    RequestMethod.GET.name(),
                    RequestMethod.POST.name()
                ));
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "Cache-Control"));

        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

         return source;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

和LoginController.java:

package main.java.it.coderevo.controller;

import java.security.Principal;

import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import main.java.it.coderevo.algo.Helper;
import main.java.it.coderevo.pojo.User;

@RestController
@RequestMapping("/login*")
public class LoginController {

        @Autowired
        private Helper helper;

        @RequestMapping(method = { RequestMethod.GET, RequestMethod.POST}, 
                        produces = "application/json")
        String getUserInfo(Principal principal) {

            JSONObject json = new JSONObject();         
            User user = helper.getLoggedUser(principal);

            json.put("logged", user != null);
            if (user == null)
                json
                    .put("msg", "error looking for user info!");
            else
                json
                    .put("username", user.getUsername())
                    .put("mail", user.getMail())
                    .put("verified", user.isVerified())
                    .put("role", user.getRole().getName());

            return json.toString();         
        }
}

在另一边,我们发现一个经典的API axios:

api.login(this.usr, this.pws)
   .then( res => this.loginPass(res))
   .catch(err => this.loginError(err));

在库中解析:

login: (usr, pws) => {

      let conf = {
        withCredentials: true
      }
      if (usr && pws)
        conf.headers = {

          'Authorization': "Basic " + btoa(usr + ":" + pws)
        }

      return server.post(SERVER_URL + "login", {}, conf)
    }

然后,重点:都在工作;登录、注销、cookie......但如果我发布了一些未经授权的凭据,服务器会给我一个超时错误,而不是报告发生了什么:

Error: timeout of 1000ms exceeded
    at createError (createError.js?2d83:16)
    at XMLHttpRequest.handleTimeout (xhr.js?b50d:95)

而且Vue也向我展示了一种有点奇怪行为:x1c 0d1x正如您可以从当我发送axios post请求到当请求超时(默认值是1秒,但修改该值只会导致警报形式变短或变长)中看到,由于某种原因,出现了bad-ugly-old-school-alert-form,我无法休息。
作为一个人,至少我可以开始挖掘一些方向?我其实真的很困惑。

更新1

主要问题似乎是在axios调用中,特别是withCredentials: true。我使用该选项的主要原因是处理现成的会话cookie。禁用该选项会导致正常的登录行为(但没有会话管理),启用该选项会给我带来不受欢迎的丑陋形式,我不能把我的手。

fsi0uk1n

fsi0uk1n1#

在最后一个例子中,这就是重点:axios向API发布了一个带有错误用户名和/或密码的请求。服务器重放401,XXX-Authenticate头设置为Authentication: Basic realm
Google Chrome武断地决定在axios超时之前用一个提示来处理这种情况。
因此,更简单的解决方案是修改服务器重放的这2个信息中的至少一个:您可以使用不同的状态代码(例如400或403),但最好的方法是保留401代码(401的含义是unauthorized request),并避免在XXX-Authentication报头中使用身份验证部分。
这是一个简单的身份验证入口点,在 Spring 附加到我的httpBasic(),似乎可以解决以下问题:

@Override
    protected void configure(final HttpSecurity http) throws Exception {

        http
            .authorizeRequests()
                [...]
                .httpBasic().authenticationEntryPoint(new BasicAuthenticationEntryPoint() {

                    { setRealmName("codrev"); }

                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response,
                            AuthenticationException authException) throws IOException {

                        response.setStatus(HttpStatus.UNAUTHORIZED.value());

                        response.setHeader("WWW-Authenticate", getRealmName());
                        response.setHeader("Accept", "application/json, text/plain, */*");
                        response.setHeader("Content-Type", "application/json;charset=utf-8");
                        response.setHeader("cache-control", "no-cache, no-store, max-age=0, must-revalidate");

                        Map<String, Object> data = new HashMap<String, Object>();
                        data.put("logged", false);

                        response.getOutputStream().println(new ObjectMapper().writeValueAsString(data));
                    }

                })
        ;
    }

!!super.commence(request, response, authException)调用导致无用的忽略响应修改,因此请避免!!

相关问题