lenve/vhr · error · UsernameNotFoundException

用户名不存在!

Error message

用户名不存在!

What it means

Thrown by Spring Security's UserDetailsService.loadUserByUsername when the queried user record is null. The HrService delegates to HrMapper.loadUserByUsername(username); a null return means no row matched the supplied username in the database, so authentication cannot proceed and the framework propagates this to trigger the configured AuthenticationFailureHandler. It is the canonical 'no such principal' signal during the DaoAuthenticationProvider lookup phase.

Source

Thrown at vhr/vhrserver/vhr-service/src/main/java/org/javaboy/vhr/service/HrService.java:37

 * @公众号 江南一点雨
 * @微信号 a_java_boy
 * @GitHub https://github.com/lenve
 * @博客 http://wangsong.blog.csdn.net
 * @网站 http://www.javaboy.org
 * @时间 2019-09-20 8:21
 */
@Service
public class HrService implements UserDetailsService {
    @Autowired
    HrMapper hrMapper;
    @Autowired
    HrRoleMapper hrRoleMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Hr hr = hrMapper.loadUserByUsername(username);
        if (hr == null) {
            throw new UsernameNotFoundException("用户名不存在!");
        }
        hr.setRoles(hrMapper.getHrRolesById(hr.getId()));
        return hr;
    }

    public List<Hr> getAllHrs(String keywords) {
        return hrMapper.getAllHrs(HrUtils.getCurrentHr().getId(),keywords);
    }

    public Integer updateHr(Hr hr) {
        return hrMapper.updateByPrimaryKeySelective(hr);
    }

    @Transactional
    public boolean updateHrRole(Integer hrid, Integer[] rids) {
        hrRoleMapper.deleteByHrid(hrid);
        return hrRoleMapper.addRole(hrid, rids) == rids.length;
    }

View on GitHub (pinned to 03abbd35af)

Solutions

  1. Confirm the username being submitted matches a row in the hr table (SELECT id, username FROM hr WHERE username = ?) using the same datasource Spring Security is configured with.
  2. Verify the login payload key matches what LoginFilter parses — if posting JSON, ensure the field is 'username' exactly (not 'name' or 'account'), since a wrong key yields a null/blank lookup.
  3. Check HrMapper.loadUserByUsername SQL for WHERE-clause correctness and column mapping, and ensure the username column is not filtered out (e.g., WHERE enabled = 1) unintentionally.
  4. If the row exists but a soft-delete/active flag excludes it, correct the query or re-enable the account.
  5. Register/enable a proper AuthenticationFailureHandler so the missing-user case returns a clean 401/JSON instead of an uncaught stack trace.

Example fix

// No code change needed for a genuinely missing user — this is correct behavior.
// If you want a friendlier message to the caller, ensure the failure handler maps it:

// before (default): exception bubbles up with raw message
// after: register a handler that translates the Chinese message to the client
@Component
public class CustomAuthFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,
                                        AuthenticationException ex) throws IOException {
        resp.setContentType("application/json;charset=UTF-8");
        Map<String, Object> body = new HashMap<>();
        body.put("status", 401);
        body.put("msg", ex instanceof UsernameNotFoundException ? "用户名不存在" : ex.getMessage());
        resp.getWriter().write(new ObjectMapper().writeValueAsString(body));
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check the username exists before invoking Spring Security authentication,
// to give a controlled message rather than relying on the framework exception.
// (Use sparingly — it adds a query; the default DaoAuthenticationProvider path is fine.)
boolean exists = hrMapper.loadUserByUsername(username) != null;
if (!exists) {
    return ResponseEntity.status(401).body(Map.of("msg", "用户名不存在"));
}

Type guard

// Java has no structural type guards; narrow on the exception type instead.
if (ex instanceof UsernameNotFoundException) {
    // username does not exist in the hr table
}

Try / catch

// In a custom AuthenticationFailureHandler (the idiomatic place):
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,
                                    AuthenticationException ex) throws IOException {
    resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    resp.setContentType("application/json;charset=UTF-8");
    String msg = ex instanceof UsernameNotFoundException
        ? "用户名不存在" : ex.getMessage();
    resp.getWriter().write(new ObjectMapper().writeValueAsString(Map.of("status", 401, "msg", msg)));
}
// Avoid catching UsernameNotFoundException inside business code — let the security chain handle it.

Prevention

When it happens

Trigger: A POST to the login endpoint (handled by LoginFilter) with a username that does not exist in the hr table; or any code path that calls authenticationManager.authenticate(...) with a non-existent username. Also triggered if HrMapper.loadUserByUsername returns null due to a malformed SQL, a soft-deleted/locked account, or a column mismatch that yields no row.

Common situations: Typo in the username at login; the hr row was deleted or its name column changed after the user cached their credentials; database connection/transaction reads a different replica where the row is missing; the login form posts the 'username' field under a different key and it arrives as null/blank so the mapper finds nothing; migrating from a case-sensitive collation where the stored name differs in case.

Related errors


AI-assisted analysis of lenve/vhr@03abbd35af (2026-08-14). Data as JSON: /api/errors/7d2034cf72f57a0e. Report an issue: GitHub.