{"record":{"id":"830d765bf061f19e","repo":"wuyouzhuguli/SpringAll","slug":"error-830d76","errorCode":null,"errorMessage":"未找到与该手机号对应的用户","messagePattern":"未找到与该手机号对应的用户","errorType":"validation","errorClass":"InternalAuthenticationServiceException","httpStatus":null,"severity":"error","filePath":"65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationProvider.java","lineNumber":20,"sourceCode":"\nimport cc.mrbird.security.service.UserDetailService;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\n\npublic class SmsAuthenticationProvider implements AuthenticationProvider {\n\n    private UserDetailService userDetailService;\n\n    @Override\n    public Authentication authenticate(Authentication authentication) throws AuthenticationException {\n        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;\n        UserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());\n\n        if (userDetails == null)\n            throw new InternalAuthenticationServiceException(\"未找到与该手机号对应的用户\");\n\n        SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities());\n\n        authenticationResult.setDetails(authenticationToken.getDetails());\n\n        return authenticationResult;\n    }\n\n    @Override\n    public boolean supports(Class<?> aClass) {\n        return SmsAuthenticationToken.class.isAssignableFrom(aClass);\n    }\n\n    public UserDetailService getUserDetailService() {\n        return userDetailService;\n    }\n\n    public void setUserDetailService(UserDetailService userDetailService) {","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/wuyouzhuguli/SpringAll/blob/614d2578d9495acf53cc02f2dee9c6131cc5e51a/65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationProvider.java#L2-L38","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the UserDetailsService used by SmsAuthenticationProvider actually returns a UserDetails for the submitted mobile — check the DB query and the column it matches against.","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.","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.","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.","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."],"exampleFix":"// before\nUserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());\nif (userDetails == null)\n    throw new InternalAuthenticationServiceException(\"未找到与该手机号对应的用户\");\n\n// after — let the service obey its contract and surface a normal auth failure\nUserDetails userDetails;\ntry {\n    userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());\n} catch (UsernameNotFoundException e) {\n    throw new BadCredentialsException(\"未找到与该手机号对应的用户\", e);\n}","handlingStrategy":"try-catch","validationCode":"// Before authentication, confirm a user is linked to the mobile\nboolean registered = userService.existsByMobile(mobile);\nif (!registered) {\n    return \"该手机号尚未注册\";\n}\n// then proceed with the SmsAuthenticationToken flow","typeGuard":"// Narrow before relying on the cast/null — validate the principal is a mobile string\nprivate boolean isMobilePrincipal(Authentication auth) {\n    return auth.getPrincipal() instanceof String\n        && ((String) auth.getPrincipal()).matches(\"1\\\\d{10}\");\n}","tryCatchPattern":"try {\n    Authentication result = authenticationManager.authenticate(token);\n} catch (InternalAuthenticationServiceException | UsernameNotFoundException e) {\n    // map to a clean user-facing 'unregistered mobile' failure\n    throw new BadCredentialsException(\"手机号未注册\", e);\n} catch (BadCredentialsException e) {\n    // normal wrong-credentials path\n    throw e;\n}","preventionTips":["Make your UserDetailsService throw UsernameNotFoundException instead of returning null, honoring the documented contract.","Normalize the mobile (trim, country code) identically at send-code and authenticate time so lookups always hit.","Wire the dedicated SMS UserDetailsService bean into SmsAuthenticationProvider explicitly to avoid falling back to a default with no mobile mapping.","Register/link users on first SMS login if self-service is intended."],"tags":["spring-security","authentication","sms-login","userdetails","null-check"],"backgroundTag":null,"analyzedSha":"614d2578d9495acf53cc02f2dee9c6131cc5e51a","analyzedAt":"2026-08-14T04:40:03.488Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}