Spring security loadByUsername的用户名字段为空

时间:2016-08-18 22:02:32

标签: java spring spring-security

我正在开发一个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
}

但是,电子邮件字符串为空。我无法理解为什么,因为我很难理解何时调用此方法以及作为此方法的参数发送了什么值,因为这是一个REST后端被发送JSON。这是我的WebSecurityConfigurerAdapter设置:

@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")时应该向该方法发送电子邮件,因此我不确定为什么它不会填充电子邮件参数。我在前端使用AngularJS并使用JSON将凭据发送到后端。

2 个答案:

答案 0 :(得分:1)

如果您要在json中发送凭据,则此处的错误是由于电子邮件不是http参数,因此包含在RequestBody中。

默认UsernamePasswordAuthenticationFilter从http param获取凭据:

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的位置将其设置在您的配置中。

您可以查看https://stackoverflow.com/a/35724932/4190848https://stackoverflow.com/a/35699200/4190848

答案 1 :(得分:0)

另一个更简单的解决方案是在客户端应用程序中执行此操作 - 与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的默认实现非常喜欢这样。