Java Spring security loadByUsername';s的用户名字段为空

Java Spring security loadByUsername';s的用户名字段为空,java,spring,spring-security,Java,Spring,Spring Security,我正在开发一个RESTful web服务,并且登录正常。我想添加安全性和访问令牌,因此我添加了一个UserDetailsService,如下所示: @Component public class CustomLoginAuthenticationProvider implements UserDetailsService { @Autowired private BusinessUserService businessUserService; @Override

我正在开发一个RESTful web服务,并且登录正常。我想添加安全性和访问令牌,因此我添加了一个
UserDetailsService
,如下所示:

@Component
public class CustomLoginAuthenticationProvider implements UserDetailsService {

    @Autowired
    private BusinessUserService businessUserService;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        if(email == null || email.isEmpty() || !email.contains("@")) {
            System.out.println("ERROR - THIS IS THE USERNAME:" + email);
            throw new UsernameNotFoundException(email);
        }
    //... More below, but it doesn't matter. Exception thrown every time
}
但是,电子邮件字符串为空。我不明白为什么,因为我很难准确地理解何时调用此方法,以及发送什么值作为此方法的参数,因为这是发送给JSON的REST后端。这是我的
websecurityConfigureAdapter
设置:

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Bean
    public CustomLoginAuthenticationProvider customLoginAuthenticationProvider() {
        return new CustomLoginAuthenticationProvider();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http
          .authorizeRequests()
          .antMatchers("/css/**", "/js/**", "/images/**", "/fonts/**", 
                       "/videos/**", "/", "/register", "/login", "/about", 
                       "/contact", "/test")
          .permitAll()
        .and()
          .authorizeRequests()
          .anyRequest()
          .authenticated()                            
        .and()
          .exceptionHandling()
          .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
        .and()
          .formLogin()
          .loginPage("/login")
          .loginProcessingUrl("/login")
          .usernameParameter("email")
          .passwordParameter("password")
        .and()
          .logout()
          .logoutSuccessUrl("/")
          .permitAll()
        .and()
          .csrf()
          .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
        .and()
          .addFilterAfter(new CsrfTokenFilter(), CsrfFilter.class);
    }
}

我指定在使用
.usernameParameter(“email”)
时应将电子邮件发送到该方法,因此我不确定它为什么不填充email参数。我在前端使用AngularJS,并使用JSON将凭据发送到后端。

如果您使用JSON发送凭据,这里的错误是因为电子邮件不是http参数,包含在RequestBody中

默认设置从http参数获取凭据:

public class UsernamePasswordAuthenticationFilter extends
        AbstractAuthenticationProcessingFilter {


    public Authentication attemptAuthentication(HttpServletRequest request,
                HttpServletResponse response) throws AuthenticationException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException(
                        "Authentication method not supported: " + request.getMethod());
            }

            String username = obtainUsername(request);
            String password = obtainPassword(request);
            ...
    }

    /**
         * Enables subclasses to override the composition of the password, such as by
         * including additional values and a separator.
         * <p>
         * This might be used for example if a postcode/zipcode was required in addition to
         * the password. A delimiter such as a pipe (|) should be used to separate the
         * password and extended value(s). The <code>AuthenticationDao</code> will need to
         * generate the expected password in a corresponding manner.
         * </p>
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the password that will be presented in the <code>Authentication</code>
         * request token to the <code>AuthenticationManager</code>
         */
        protected String obtainPassword(HttpServletRequest request) {
            return request.getParameter(passwordParameter);
        }

        /**
         * Enables subclasses to override the composition of the username, such as by
         * including additional values and a separator.
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the username that will be presented in the <code>Authentication</code>
         * request token to the <code>AuthenticationManager</code>
         */
        protected String obtainUsername(HttpServletRequest request) {
            return request.getParameter(usernameParameter);
        }
您必须编写自己的筛选器,从中读取来自RequestBody的传入凭据,并在配置中将其设置在UsernamePasswordAuthenticationFilter的位置


您可以查看和

如果您是用json发送凭据,这里的错误是由于电子邮件不是http参数,而是包含在RequestBody中

默认设置从http参数获取凭据:

public class UsernamePasswordAuthenticationFilter extends
        AbstractAuthenticationProcessingFilter {


    public Authentication attemptAuthentication(HttpServletRequest request,
                HttpServletResponse response) throws AuthenticationException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException(
                        "Authentication method not supported: " + request.getMethod());
            }

            String username = obtainUsername(request);
            String password = obtainPassword(request);
            ...
    }

    /**
         * Enables subclasses to override the composition of the password, such as by
         * including additional values and a separator.
         * <p>
         * This might be used for example if a postcode/zipcode was required in addition to
         * the password. A delimiter such as a pipe (|) should be used to separate the
         * password and extended value(s). The <code>AuthenticationDao</code> will need to
         * generate the expected password in a corresponding manner.
         * </p>
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the password that will be presented in the <code>Authentication</code>
         * request token to the <code>AuthenticationManager</code>
         */
        protected String obtainPassword(HttpServletRequest request) {
            return request.getParameter(passwordParameter);
        }

        /**
         * Enables subclasses to override the composition of the username, such as by
         * including additional values and a separator.
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the username that will be presented in the <code>Authentication</code>
         * request token to the <code>AuthenticationManager</code>
         */
        protected String obtainUsername(HttpServletRequest request) {
            return request.getParameter(usernameParameter);
        }
您必须编写自己的筛选器,从中读取来自RequestBody的传入凭据,并在配置中将其设置在UsernamePasswordAuthenticationFilter的位置


您可以看一看,另一个更简单的解决方案是在客户端应用程序中执行此操作,它可以与Spring Security配合使用:

public login(username: string, password: string) {
    const headers = new Headers();
    headers.append('Content-Type', 'application/x-www-form-urlencoded');

    return this.http.post(
      `http://www.myserver.com:8080/myApp/login`,
      encodeURI(`username=${username}&password=${password}`),
      { headers }
    );
  }
}

这会从url中隐藏参数并将其编码为表单数据,spring security的默认实现非常喜欢这样做。

另一个更简单的解决方案是在客户端应用程序中执行此操作-可以与spring security配合使用:

public login(username: string, password: string) {
    const headers = new Headers();
    headers.append('Content-Type', 'application/x-www-form-urlencoded');

    return this.http.post(
      `http://www.myserver.com:8080/myApp/login`,
      encodeURI(`username=${username}&password=${password}`),
      { headers }
    );
  }
}

这会从url中隐藏参数并将其编码为表单数据,spring security的默认实现非常类似于此。

能否将登录表单放在此处?能否将登录表单放在此处?确保将其作为参数而不是JSON数据传递。JSON数据作为参数$http({'url':'signup','method':'POST','params':user})传递的一种角度方法,请确保将它们作为参数而不是JSON数据传递。以参数$http({'url':'signup','method':'POST','params':user})的形式传递JSON数据的一种有角度的方法