wuyouzhuguli/SpringAll · error · InternalAuthenticationServiceException

未找到与该手机号对应的用户

Error message

未找到与该手机号对应的用户

What it means

Spring Security's InternalAuthenticationServiceException is thrown by SmsAuthenticationProvider.authenticate when userDetailService.loadUserByUsername(mobile) returns null during SMS-based login. It signals an authentication-service-layer problem (as opposed to a normal bad-credentials rejection) and surfaces during the custom SMS authentication flow where the principal is the mobile phone number. The contract of UserDetailsService actually expects a UsernameNotFoundException when no user exists, so a null return is itself an implementation smell that this guard compensates for.

Source

Thrown at 65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationProvider.java:20

import cc.mrbird.security.service.UserDetailService;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;

public class SmsAuthenticationProvider implements AuthenticationProvider {

    private UserDetailService userDetailService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;
        UserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());

        if (userDetails == null)
            throw new InternalAuthenticationServiceException("未找到与该手机号对应的用户");

        SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities());

        authenticationResult.setDetails(authenticationToken.getDetails());

        return authenticationResult;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return SmsAuthenticationToken.class.isAssignableFrom(aClass);
    }

    public UserDetailService getUserDetailService() {
        return userDetailService;
    }

    public void setUserDetailService(UserDetailService userDetailService) {

View on GitHub (pinned to 614d2578d9)

Solutions

  1. Verify the UserDetailsService used by SmsAuthenticationProvider actually returns a UserDetails for the submitted mobile — check the DB query and the column it matches against.
  2. Make loadUserByUsername throw new UsernameNotFoundException(...) instead of returning null, which is the documented contract; let Spring's normal bad-credentials flow handle unknown users rather than surfacing an internal-service exception.
  3. Ensure the mobile value passed as principal is normalized (trim, +prefix, country code) identically to how it is stored when the SMS code was generated, so the lookup key matches.
  4. If self-registration is intended, register/link the user on first SMS login instead of failing; otherwise return a clear user-facing 'unregistered mobile' message.
  5. Confirm the correct UserDetailsService bean is wired into SmsAuthenticationProvider (userDetailService setter/field injection), not the default JDBC/in-memory one that has no mobile mapping.

Example fix

// before
UserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());
if (userDetails == null)
    throw new InternalAuthenticationServiceException("未找到与该手机号对应的用户");

// after — let the service obey its contract and surface a normal auth failure
UserDetails userDetails;
try {
    userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());
} catch (UsernameNotFoundException e) {
    throw new BadCredentialsException("未找到与该手机号对应的用户", e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before authentication, confirm a user is linked to the mobile
boolean registered = userService.existsByMobile(mobile);
if (!registered) {
    return "该手机号尚未注册";
}
// then proceed with the SmsAuthenticationToken flow

Type guard

// Narrow before relying on the cast/null — validate the principal is a mobile string
private boolean isMobilePrincipal(Authentication auth) {
    return auth.getPrincipal() instanceof String
        && ((String) auth.getPrincipal()).matches("1\\d{10}");
}

Try / catch

try {
    Authentication result = authenticationManager.authenticate(token);
} catch (InternalAuthenticationServiceException | UsernameNotFoundException e) {
    // map to a clean user-facing 'unregistered mobile' failure
    throw new BadCredentialsException("手机号未注册", e);
} catch (BadCredentialsException e) {
    // normal wrong-credentials path
    throw e;
}

Prevention

When it happens

Trigger: An SMS login request reaches the provider (a SmsAuthenticationToken is submitted), loadUserByUsername is invoked with the mobile number as the principal, and the backing UserDetailsService returns null — e.g. the mobile is unregistered, the user record was deleted, or the service queries the wrong column. The null check at line 20 then throws this exception instead of producing a populated authentication token.

Common situations: The mobile-to-user mapping in the database is missing or misconfigured (storing the phone under a different column than the query reads); a fresh/anonymous user attempts SMS login before any account is linked to that number; a test environment seeded users without phone fields; or the custom UserDetailsService implementation forgot to throw UsernameNotFoundException and silently returns null.

Related errors


AI-assisted analysis of wuyouzhuguli/SpringAll@614d2578d9 (2026-08-14). Data as JSON: /api/errors/830d765bf061f19e. Report an issue: GitHub.