macrozheng/mall · error · UsernameNotFoundException

用户名或密码错误

Error message

用户名或密码错误

What it means

This is the Spring Security UserDetailsService.loadUserByUsername contract implemented for the mall-admin (backend) module. Spring's DaoAuthenticationProvider calls loadUserByUsername(username) during login; the method looks up UmsAdmin via getAdminByUsername, builds an AdminUserDetails (admin + its resourceList), and only throws UsernameNotFoundException when the lookup returns null — i.e. the submitted username does not exist in the ums_admin table. Note: by default DaoAuthenticationProvider.hideUserNotFoundExceptions=true swallows this and rethrows it as BadCredentialsException, so callers usually see a generic bad-credentials error rather than this exact message.

Source

Thrown at mall-admin/src/main/java/com/macro/mall/service/impl/UmsAdminServiceImpl.java:272

        UmsAdmin umsAdmin = adminList.get(0);
        if(!passwordEncoder.matches(param.getOldPassword(),umsAdmin.getPassword())){
            return -3;
        }
        umsAdmin.setPassword(passwordEncoder.encode(param.getNewPassword()));
        adminMapper.updateByPrimaryKey(umsAdmin);
        getCacheService().delAdmin(umsAdmin.getId());
        return 1;
    }

    @Override
    public UserDetails loadUserByUsername(String username){
        //获取用户信息
        UmsAdmin admin = getAdminByUsername(username);
        if (admin != null) {
            List<UmsResource> resourceList = getResourceList(admin.getId());
            return new AdminUserDetails(admin,resourceList);
        }
        throw new UsernameNotFoundException("用户名或密码错误");
    }

    @Override
    public UmsAdminCacheService getCacheService() {
        return SpringUtil.getBean(UmsAdminCacheService.class);
    }

    @Override
    public void logout(String username) {
        //清空缓存中的用户相关数据
        UmsAdmin admin = getCacheService().getAdmin(username);
        getCacheService().delAdmin(admin.getId());
        getCacheService().delResourceList(admin.getId());
    }
}

View on GitHub (pinned to 0504e86b1f)

Solutions

  1. Verify the username exists: SELECT id,username FROM ums_admin WHERE username = ? — confirm spelling, case, and absence of leading/trailing whitespace.
  2. If logging in via JWT, the token's sub claim must match a current admin row; clear the client token and perform a fresh interactive login.
  3. Confirm getAdminByUsername and the underlying UmsAdminMapper/SQL run against the correct database and that the Redis cache key is not poisoned (flush the ums_admin cache keys and retry).
  4. If the default account is missing, re-seed ums_admin from the project's SQL (macro/mall schema) so the admin/admin seed row exists.
  5. At the controller/login layer, catch BadCredentialsException and UsernameNotFoundException together and return one generic 'username or password incorrect' response to prevent username enumeration.

Example fix

// before (in the admin login controller):
try {
    authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(username, password));
} catch (Exception e) {
    throw e; // leaks which credential was wrong
}

// after:
UmsAdmin admin;
try {
    admin = adminService.login(username, password);
} catch (BadCredentialsException | UsernameNotFoundException e) {
    return CommonResult.validateFailed("用户名或密码错误");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate before invoking authentication, so a missing admin is a clean 404/401,
// not a UsernameNotFoundException leaking from the security stack.
UmsAdmin admin = adminService.getAdminByUsername(username);
if (admin == null) {
    return CommonResult.validateFailed("用户名或密码错误");
}

Try / catch

// Treat UsernameNotFoundException and BadCredentialsException identically at the
// boundary to prevent username enumeration. DaoAuthenticationProvider already
// converts the former into the latter when hideUserNotFoundExceptions=true.
try {
    Authentication auth = authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(username, password));
    // ... issue JWT ...
} catch (BadCredentialsException | UsernameNotFoundException e) {
    return CommonResult.validateFailed("用户名或密码错误");
} catch (LockedException | DisabledException e) {
    return CommonResult.forbidden("账号已被禁用");
}

Prevention

When it happens

Trigger: POST to the admin login endpoint with a username absent from ums_admin; a JWT (subject = admin username) presented for a since-deleted or renamed admin account; the Redis admin cache (UmsAdminCacheService) holding a stale null/empty entry for that username.

Common situations: Typo or wrong-case username at the admin login screen; admin record deleted by another operator while the browser still holds a valid token; a fresh database/migration where ums_admin was not seeded with the default admin/admin account; a misconfigured MyBatis mapper or DB connection causing getAdminByUsername to silently return null.

Related errors


AI-assisted analysis of macrozheng/mall@0504e86b1f (2026-08-13). Data as JSON: /api/errors/bad536c1356aa0f6. Report an issue: GitHub.