macrozheng/mall · error · BadCredentialsException
密码不正确
Error message
密码不正确
What it means
This is Spring Security's BadCredentialsException thrown inside UmsMemberServiceImpl.login when the submitted password does not match the stored hash: passwordEncoder.matches(rawPassword, userDetails.getPassword()) returns false. The method's own comment — '密码需要客户端加密后传递' (password must be encrypted by the client before transmission) — is the critical context: the portal expects the client to send a pre-hashed (typically MD5) password, and the stored hash is the BCrypt of that pre-hash, so a plaintext or differently-hashed input will fail matches. The exception is an AuthenticationException subclass and is caught by the same try block (catch AuthenticationException), logged as '登录异常:{}', and login() returns a null token — so the caller sees login failure, not the thrown exception, unless it inspects the return value.
Source
Thrown at mall-portal/src/main/java/com/macro/mall/portal/service/impl/UmsMemberServiceImpl.java:171
}
@Override
public UserDetails loadUserByUsername(String username) {
UmsMember member = getByUsername(username);
if(member!=null){
return new MemberDetails(member);
}
throw new UsernameNotFoundException("用户名或密码错误");
}
@Override
public String login(String username, String password) {
String token = null;
//密码需要客户端加密后传递
try {
UserDetails userDetails = loadUserByUsername(username);
if(!passwordEncoder.matches(password,userDetails.getPassword())){
throw new BadCredentialsException("密码不正确");
}
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
token = jwtTokenUtil.generateToken(userDetails);
} catch (AuthenticationException e) {
LOGGER.warn("登录异常:{}", e.getMessage());
}
return token;
}
@Override
public String refreshToken(String token) {
return jwtTokenUtil.refreshHeadToken(token);
}
//对输入的验证码进行校验
private boolean verifyAuthCode(String authCode, String telephone){
if(StrUtil.isEmpty(authCode)){View on GitHub (pinned to 0504e86b1f)
Solutions
- Ensure the client applies the same pre-hash as at registration (typically MD5 of the plaintext) before sending — verify in the browser/app network request that the password field is the hash, not plaintext.
- Inspect the stored hash: SELECT username, password FROM ums_member WHERE username = ? — confirm it is a BCrypt hash (starts with $2a$/$2b$) and is not null/empty.
- Reproduce matches locally: passwordEncoder.matches(expectedClientHash, storedHash) to confirm the encoder bean and hash format agree; if not, align the encoder (BCryptPasswordEncoder) registration-to-login.
- If the account is corrupt, trigger a password reset so a fresh hash is generated from the correct client-side input.
- Do not swallow the exception silently — surface a non-null token check or a thrown exception so the caller can distinguish 'wrong password' from 'system error' instead of receiving null.
Example fix
// before (silent swallow, returns null on any auth failure):
try {
UserDetails userDetails = loadUserByUsername(username);
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException("密码不正确");
}
...
} catch (AuthenticationException e) {
LOGGER.warn("登录异常:{}", e.getMessage());
}
return token; // null on failure — caller cannot tell why
// after (fail explicitly, keep the token null contract but log the cause):
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
LOGGER.warn("密码校验失败, username={}", username);
throw new BadCredentialsException("用户名或密码错误");
}
// client side (mirror of registration):
// const pwd = md5(rawPassword); axios.post('/sso/login', { username, password: pwd }) Defensive patterns
Strategy: validation
Validate before calling
// Validate the password format the portal expects (client-side MD5) before
// even calling login(), and guard against a null stored hash.
if (storedHash == null || storedHash.isEmpty()) {
throw new BadCredentialsException("用户名或密码错误");
}
boolean ok = passwordEncoder.matches(clientHashedPassword, storedHash);
// clientHashedPassword must equal md5(rawPassword) sent by the front-end Try / catch
// In UmsMemberServiceImpl.login, distinguish 'wrong password' from systemic
// errors instead of blanket-catching AuthenticationException and returning null.
try {
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException("用户名或密码错误");
}
// ... generate token ...
} catch (BadCredentialsException e) {
LOGGER.warn("密码校验失败 username={}", username);
throw e; // let the controller map it to a 401 with a clear message
} catch (AuthenticationException e) {
LOGGER.error("登录系统异常 username={}", username, e);
throw e;
} Prevention
- Lock the client-side pre-hash algorithm (MD5) and the server encoder (BCryptPasswordEncoder) in one shared constant so registration and login cannot drift.
- Write a unit test that registers a member with a known password and immediately logs in, to catch encoder/format regressions before deploy.
- Reject null/empty password hashes at registration with a clear error so a member can never be saved unloggable.
- Do not return null to signal failure — throw or return a result object so callers cannot mistake failure for success.
When it happens
Trigger: Client sends a plaintext password instead of the client-side hash the portal expects; client uses a different hash algorithm (e.g. SHA-256) than the MD5 the registration flow used; the stored password hash was created from a raw password while login compares a pre-hashed one (or vice-versa); member record's password field is null/empty so matches returns false.
Common situations: Front-end forgot to MD5 the password before POSTing to /sso/login; a member registered on a legacy flow (plaintext stored) now hitting the BCrypt matches path; password column migrated/corrupted so the hash no longer matches; copy-paste introducing trailing whitespace in the password; passwordEncoder bean changed (e.g. from BCrypt to noop) between registration and login.
Related errors
AI-assisted analysis of macrozheng/mall@0504e86b1f (2026-08-13).
Data as JSON: /api/errors/39bdc1aa3e717da8.
Report an issue: GitHub.