解决若依框架短信验证码登录,并且不影响原有登录
文章目录
一、实现DaoAuthenticationProvider
1.位置
2.代码部分
二、修改SecurityConfig配置
1.位置
编辑
2.代码部分
三、验证码登录方法
1.位置
2.控制层代码
3.实现类代码
4.密码不验证
5.验证码验证
6.验证码发送
一、实现DaoAuthenticationProvider
1.位置
2.代码部分
Constants.CUSTOM_LOGIN_SMS是一个常量,随便定义一个字符串即可。
package com.ruoyi.framework.security.filter;
import com.ruoyi.common.constant.Constants;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* 自定义登录扩展
*/
public class CustomLoginAuthenticationProvider extends DaoAuthenticationProvider {
public CustomLoginAuthenticationProvider(UserDetailsService userDetailsService) {
super();
setUserDetailsService(userDetailsService);
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
this.logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
} else {
String password = authentication.getCredentials().toString();
if(Constants.CUSTOM_LOGIN_SMS.equals(password)){
//短信登录,不验证密码
}else{
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
this.logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
}
}
}
二、修改SecurityConfig配置
1.位置
2.代码部分
在最下面,复制上去即可。
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new CustomLoginAuthenticationProvider(userDetailsService));
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
三、验证码登录方法
1.位置
2.控制层代码
@PostMapping("/appCodeLogin")
public AjaxResult appCodeLogin(@RequestBody LoginBody loginBody) {
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.appCodeLogin(loginBody.getUsername(), loginBody.getCode(), loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return ajax;
}
3.实现类代码
至于位置,通过上面代码创建这个方法,在复制上即可,没有写发送验证码的,可以把第一句注释,即可只输入账号登录。
/**
* APP验证码登录方法
*
* @param username 手机号
* @param code 验证码
* @param uuid uuid
* @return 结果
*/
public String appCodeLogin(String username, String code, String uuid) {
validateCaptcha(username, code, uuid); //校验验证码
Authentication authentication = null; // 用户验证
try {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, Constants.CUSTOM_LOGIN_SMS);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);
}
catch (Exception e) {
if (e instanceof BadCredentialsException) {
throw new UserPasswordNotMatchException(); //抛出账号或者密码错误的异常
} else {
throw new ServiceException(e.getMessage()); //抛出其他异常
}
} finally {
AuthenticationContextHolder.clearContext();
}
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId()); //修改sys_user最近登录IP和登录时间
// 生成token
return tokenService.createToken(loginUser);
}
4.密码不验证
位置
这个方法下面加入一个密码为Constants.CUSTOM_LOGIN_SMS不验证密码的判断
5.验证码验证
这里验证码验证就是用的若依里面写好的代码。
/**
* 校验验证码
*
* @param username 用户名
* @param code 验证码
* @param uuid 唯一标识
*/
public void validateCaptcha(String username, String code, String uuid) {
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");//验证码key值
String captcha = redisCache.getCacheObject(verifyKey); //拿到缓存的数据
redisCache.deleteObject(verifyKey);//删除该缓存
if (captcha == null) {
//异步记录登录信息sys_logininfor
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
throw new CaptchaExpireException(); //抛出一个验证码过期异常
}
if (!code.equalsIgnoreCase(captcha)) {
//异步记录登录信息sys_logininfor
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException(); //抛出一个验证码错误的异常
}
}
6.验证码发送
发送用户短信的方法,就不提供了,这里就是发送验证码,并存起来。
@GetMapping("/appCaptcha")
public AjaxResult getAppCode(Long deptId,String phone) throws Exception {
//创建一个返回对象
AjaxResult ajax = AjaxResult.success();
// 保存验证码信息
String uuid = IdUtils.simpleUUID(); //生成一个uuid
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
// 生成四位数验证码
String code = RandomUtil.getFourBitRandom();
// 查询短信参数信息
ShortMessage shortMessage = shortMessageService.selectShortMessageByDeptId(deptId);
// 通过阿里云短信发送短信
boolean sent = ShortMessageUtils.sent(shortMessage, phone, code);
//把验证码答应存入缓存,10分钟的时间
redisCache.setCacheObject(verifyKey, code, Constants.CODE_EXPIRATION, TimeUnit.MINUTES);
//把信息封装返回
ajax.put("uuid", uuid);