由于最近在集成这玩意儿的时候实在是报错报麻了,所以写一下笔记记录一下,如果有人跟我一样,集成这个东西的时候各种报错,可以往下翻一翻找一下有没有你遇到的情况。
首先准备东西:
插件集成 | RuoYi
这里边的集成aj-captcha实现滑块验证码部分里第五步有一个百度网盘链接,先下载以后有用。
aj-captcha源码下载:AJ-Captcha: 行为验证码(滑动拼图、点选文字),前后端(java)交互,包含vue/h5/Android/IOS/flutter/uni-app/react/php/go/微信小程序的源码和实现
步骤参考:05-若依使用AJ-captcha验证_刀不封的博客-CSDN博客
但是我觉得这文章还缺点东西,所以我写这篇再给他封装一层。
找到ruoyi-framework\pom.xml文件,在
com.anji-plus
spring-boot-starter-captcha
1.3.0
原来那个验证码的依赖kaptcha可以删掉不要了
添加好依赖之后记得更新一下maven
application.yml
,加入aj-captcha
配置# 滑块验证码
aj:
captcha:
# 缓存类型
cache-type: redis
# blockPuzzle 滑块 clickWord 文字点选 default默认两者都实例化
type: blockPuzzle
# 右下角显示字
water-mark: ruoyi.vip
# 校验滑动拼图允许误差偏移量(默认5像素)
slip-offset: 5
# aes加密坐标开启或者禁用(true|false)
aes-status: true
# 滑动干扰项(0/1/2)
interference-options: 2
这上边都是最基本的设置,想要其他配置效果可以参考AJ-Captcha的文档:
AJ-Captcha: 行为验证码(滑动拼图、点选文字),前后端(java)交互,包含vue/h5/Android/IOS/flutter/uni-app/react/php/go/微信小程序的源码和实现
找到ruoyi-admin\src\main\resources\META-INF\services下创建com.anji.captcha.service.CaptchaCacheService文件(不需要后缀名)同时设置文件内容为:
com.ruoyi.framework.web.service.CaptchaRedisService
这里的地址要跟你下一步写CaptchaRedisService的路径一致,不然扫不到包。
在 ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java 文件中configure方法下的httpSecurity添加如下语句:
.antMatchers("/login", "/captcha/get", "/captcha/check").permitAll()
这步保证这仨方法能够不登陆就访问,不然你登录的时候要求你有权限才能登录,就尬住了。
“我要登录。”
"对不起先生,您没有权限,不能登录。"
“我怎么才能有权限?”
"您需要先登录才有权限。"
“我要登录。”
"对不起先生,您没有权限,不能登录。"
我们要的是20岁拥有30年工作经验的人才.java
若依官方文档让修改 ruoyi-admin\com\ruoyi\web\controller\system\SysLoginController.java,但是其实改不改都行,区别就是改之前四个参数,改之后三个参数,多的那个参数uuid在换成滑动验证码之后其实用不到,所以传不传都行。
但是得保证修改之后,SysLoginController里的调用和SysLoginService里的定义是一致的,不管三个还是四个参数,得对应起来,不然就会报错。
我参考那个文章里有一句提醒:
大家在修改代码时,切记不要按照文档官方文档直接覆盖,建议使用文本比较工具将代码进行比对后只更新相关内容,这也是开发人员的良好习惯!!!!
这个其实很重要,但是翻了翻一些文档对比工具,好多都要钱,而且如果是在公司电脑上开发,有可能还不让你下载,所以我用的这个http://www.jsons.cn/txtdiff/
免费的,虽然界面可能low了点,但是挺好使。
并且记得边修改边思考,因为自身水平有限,所以我经常性进行递归式学习,看到这句代码,不知道干嘛的,点进去看一看,百度搜一搜,然后看的过程中又发现了不懂的,又往里进一层,继续点进去看继续搜。然后一层一层越进越深。但是学的东西也越来越多。
盗梦空间了属于是。
主要是这是个学习的过程,不是只为了使用而去使用。学习嘛,不寒碜。
修改com/ruoyi/framework/web/service/SysLoginService.java的内容,这个文件建议不要看北京时间2022年10月9日21:00之前的官方文档,那个版本官方文档里缺两句东西,用了会报错。
package com.ruoyi.framework.web.service;
import javax.annotation.Resource;
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.system.service.ISysUserService;
/**
* 登录校验方法
*
* @author ruoyi
*/
@Component
public class SysLoginService
{
@Autowired
private TokenService tokenService;
@Resource
private AuthenticationManager authenticationManager;
@Autowired
private ISysUserService userService;
@Autowired
@Lazy
private CaptchaService captchaService;
/**
* 登录验证
*
* @param username 用户名
* @param password 密码
* @param code 验证码
* @return 结果
*/
public String login(String username, String password, String code)
{
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(code);
ResponseModel response = captchaService.verification(captchaVO);
if (!response.isSuccess())
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
}
// 用户验证
Authentication authentication = null;
try
{
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
UsernamePasswordAuthenticationToken temp = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(temp);
authentication = authenticationManager.authenticate(temp);
}
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());
}
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 生成token
return tokenService.createToken(loginUser);
}
/**
* 记录登录信息
*
* @param userId 用户ID
*/
public void recordLoginInfo(Long userId)
{
SysUser sysUser = new SysUser();
sysUser.setUserId(userId);
sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
sysUser.setLoginDate(DateUtils.getNowDate());
userService.updateUserProfile(sysUser);
}
}
就是说不要无脑复制,最好对比一下看一看思考一下跟以前哪里不一样。
这里跟官方文档有区别的地方是这里:
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
UsernamePasswordAuthenticationToken temp = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(temp);
authentication = authenticationManager.authenticate(temp);
官方旧版:
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
就差一句setContext。可能是某个接口单独起了线程,所以要加一句setContext进行Holder的绑定,不然取不出来东西。
有关这个SecurityContext的相关知识详见:https://www.jianshu.com/p/a951859333a4,供递归式学习使用。
关于空指针报错的排查:SecurityContextHolder.getContext().getAuthentication()报空指针异常,已解决。_qq_48697226的博客-CSDN博客
这个issue在我写这篇文章的11天前就被提出(正好国庆放假前一天),然后在写这篇文章的18小时之前官方文档修复了,而昨天一整天我都被这个莫名其妙的空指针报错折磨着。
issue地址:
更换滑块验证码的BUG · Issue #I5TUBG · 若依/RuoYi - Gitee.com
修了就好,可喜可贺。
CaptchaRedisService.java
在ruoyi-framework\com\ruoyi\framework\web\service\目录下新建CaptchaRedisService.java
package com.ruoyi.framework.web.service;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import com.anji.captcha.service.CaptchaCacheService;
/**
* 自定义redis验证码缓存实现类
*
* @author ruoyi
*/
public class CaptchaRedisService implements CaptchaCacheService
{
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public void set(String key, String value, long expiresInSeconds)
{
stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS);
}
@Override
public boolean exists(String key)
{
return stringRedisTemplate.hasKey(key);
}
@Override
public void delete(String key)
{
stringRedisTemplate.delete(key);
}
@Override
public String get(String key)
{
return stringRedisTemplate.opsForValue().get(key);
}
@Override
public Long increment(String key, long val)
{
return stringRedisTemplate.opsForValue().increment(key, val);
}
@Override
public String type()
{
return "redis";
}
}
ruoyi-admin\com\ruoyi\web\controller\common\CaptchaController.java
ruoyi-framework\com\ruoyi\framework\config\CaptchaConfig.java
ruoyi-framework\com\ruoyi\framework\config\KaptchaTextCreator.java
不删直接运行会报错,如果运行的时候报错org.springframework.beans.factory.BeanDefinitionStoreException
八成是CaptchaController没删,导的那个依赖里边也有一个同名文件,所以创建Bean的时候重名了显示已存在。
将aj-captcha 官方提供的demo中的images包整体引入到resources包下。
文章的最上边提供了下载地址,可自行下载。
至此,后端全部修改完毕。
其实前端改的比较简单,不容易出什么错,就不分步骤了,简单一说:
下载官方提供的前端修改代码,就我一开始说要下载的那个
链接: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ 提取码: y9jt
在ruoyi-ui目录下的文件不要全部替换到项目中,建议比较一下改动内容。
"crypto-js": "^4.1.1",
若依后台管理系统
记住密码
登 录
登 录 中...
立即注册
前端更新crypto-js (npm install --save crypto-js)
api文件夹里的东西可以不改,看一眼就知道,api里边没新增东西,只删了一些原来的验证码用的,现在用不到了,不删也能运行。
然后运行应该就能正常用了