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
- Verify the username exists: SELECT id,username FROM ums_admin WHERE username = ? — confirm spelling, case, and absence of leading/trailing whitespace.
- 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.
- 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).
- 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.
- 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
- Keep hideUserNotFoundExceptions=true (Spring default) so missing users are not distinguishable from wrong passwords.
- Never echo which credential was wrong — return one generic message for both cases.
- Cache the negative lookup result with a short TTL only if getAdminByUsername is DB-backed and hot, and always invalidate on admin create/rename/delete.
- Log authentication failures with the username but never the password, and rate-limit by username/IP to slow brute force.
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.