1. 修改 LoginBody,添加登录类型字段
@Data
public class LoginBody {
/**
* 用户名
*/
private String username;
/**
* 用户密码
*/
private String password;
/**
* 验证码
*/
private String code;
/**
* 唯一标识
*/
private String uuid;
/**
* 登录类型
*/
private String loginType;
}
2.定义一个常量
package com.ruoyi.common.core.domain.constant;
/**
* @author Ls
* @date 2024/6/4
*/
public class LoginTypeConstant {
public static final String PASSWORD_LOGIN = "password";
public static final String MOBILE_LOGIN = "mobile";
public static final String QR_CODE_LOGIN = "qrCode";
}
3.修改登录接口
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody) {
AjaxResult ajax = AjaxResult.success();
loginBody.setLoginType(LoginTypeConstant.MOBILE_LOGIN);
// 生成令牌
String token = loginService.login(loginBody);
ajax.put(Constants.TOKEN, token);
return ajax;
}
4.重载登录实现类,基本不用改,loginbody 作为 credentials传入UsernamePasswordAuthenticationToken
public String login(LoginBody loginBody) {
String username = loginBody.getUsername();
// // 验证码校验
// validateCaptcha(username, code, uuid);
// // 登录前置校验
// loginPreCheck(username, password);
// 用户验证
Authentication authentication = null;
try {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, loginBody);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);
} catch (Exception e) {
if (e instanceof BadCredentialsException) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
} else {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
} finally {
AuthenticationContextHolder.clearContext();
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
5.自定义MyAuthenticationProvider类
package com.ruoyi.framework.security.mobile;
import com.ruoyi.common.core.domain.constant.LoginTypeConstant;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.framework.web.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
/**
* @author Ls
* @date 2024/6/4
*/
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException, UserPasswordNotMatchException {
String name = authentication.getName();
LoginBody loginBody = (LoginBody) authentication.getCredentials();
String loginType = loginBody.getLoginType();
UserDetails user;
switch (loginType) {
case LoginTypeConstant.PASSWORD_LOGIN:
user = userDetailsService.loadUserByUsername(name);
String password = loginBody.getPassword();
String encoderPassword = bCryptPasswordEncoder.encode(password);
// 数据库账号密码的校验能通过就通过
if (bCryptPasswordEncoder.matches(password, user.getPassword())) {
return new UsernamePasswordAuthenticationToken(user, encoderPassword);
}
case LoginTypeConstant.MOBILE_LOGIN:
user = userDetailsService.loadUserByPhone(loginBody);
return new UsernamePasswordAuthenticationToken(user, loginBody.getCode());
case LoginTypeConstant.QR_CODE_LOGIN:
break;
default:
break;
}
// 如果都登录不了,就返回异常输出
throw new UserPasswordNotMatchException();
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
6.用户验证类新增手机号登录 UserDetailsServiceImpl
public UserDetails loadUserByPhone(LoginBody loginBody) throws UsernameNotFoundException {
// TODO: 2024/6/4 各种业务效验,这里图省事验证码放在password
if (!Objects.equals(loginBody.getPassword(), "123456")) {
throw new ServiceException("验证码错误");
}
//这里应该是根据手机号查询,我这里图省事直接拿账号登录
String username = loginBody.getUsername();
SysUser user = userService.selectUserByUserName(username);
return createLoginUser(user);
}
7.SecurityConfig调整,配置一下MyAuthenticationProvider
/**
* 自定义用户认证逻辑
*/
@Autowired
private MyAuthenticationProvider myAuthenticationProvider;
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(myAuthenticationProvider);
}