由于最近在集成这玩意儿的时候实在是报错报麻了,所以写一下笔记记录一下,如果有人跟我一样,集成这个东西的时候各种报错,可以往下翻一翻找一下有没有你遇到的情况。
首先准备东西:插件集成 | Ruoyi
这里边的集成 aj-captcha
实现滑块验证码部分里第五步有一个百度网盘链接,先下载以后有用。
aj-captcha源码下载:AJ-Captcha: 行为验证码(滑动拼图、点选文字),前后端(java)交互,包含vue/h5/Android/IOS/flutter/uni-app/react/php/go/微信小程序的源码和实现
步骤参考:05-若依使用AJ-captcha验证_刀不封的博客-CSDN博客
但是我觉得这文章还缺点东西,所以我写这篇再给他封装一层。
一、后端整合步骤
1.添加依赖
找到 ruoyi-framework\pom.xm l文件,在
<!-- anji滑块验证码 -->
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>spring-boot-starter-captcha</artifactId>
<version>1.3.0</version>
</dependency>
原来那个验证码的依赖kaptcha可以删掉不要了
添加好依赖之后记得更新一下maven
2.修改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的文档:
3.添加实现类指向设置
找到ruoyi-admin\src\main\resources\META-INF\services下创建com.anji.captcha.service.CaptchaCacheService文件(不需要后缀名)同时设置文件内容为:
com.ruoyi.framework.web.service.CaptchaRedisService
这里的地址要跟你下一步写CaptchaRedisService的路径一致,不然扫不到包。
4.设置aj-captcha匿名访问权限
在 ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java 文件中configure方法下的httpSecurity添加如下语句:
.antMatchers("/login", "/captcha/get", "/captcha/check").permitAll()
这步保证这仨方法能够不登陆就访问,不然你登录的时候要求你有权限才能登录,就尬住了。
“我要登录。”
"对不起先生,您没有权限,不能登录。"
“我怎么才能有权限?”
"您需要先登录才有权限。"
“我要登录。”
"对不起先生,您没有权限,不能登录。"
5.修改代码
若依官方文档让修改 ruoyi-admin\com\ruoyi\web\controller\system\SysLoginController.java,但是其实改不改都行,区别就是改之前四个参数,改之后三个参数,多的那个参数uuid在换成滑动验证码之后其实用不到,所以传不传都行。
但是得保证修改之后,SysLoginController里的调用和SysLoginService里的定义是一致的,不管三个还是四个参数,得对应起来,不然就会报错。
我参考那个文章里有一句提醒:
大家在修改代码时,切记不要按照文档官方文档直接覆盖,建议使用文本比较工具将代码进行比对后只更新相关内容,这也是开发人员的良好习惯!!!!
这个其实很重要,但是翻了翻一些文档对比工具,好多都要钱,而且如果是在公司电脑上开发,有可能还不让你下载,所以我用的这个http://www.jsons.cn/txtdiff/
免费的,虽然界面可能low了点,但是挺好使。
并且记得边修改边思考,因为自身水平有限,所以我经常性进行递归式学习,看到这句代码,不知道干嘛的,点进去看一看,百度搜一搜,然后看的过程中又发现了不懂的,又往里进一层,继续点进去看继续搜。然后一层一层越进越深。但是学的东西也越来越多。
盗梦空间了属于是。
主要是这是个学习的过程,不是只为了使用而去使用。学习嘛,不寒碜。
(1)修改SysLoginService.java
修改 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
修了就好,可喜可贺。
(2)新增 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";
}
}
(3)删除不要的类
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的时候重名了显示已存在。
6、导入资源
将aj-captcha 官方提供的demo中的images包整体引入到resources包下。
文章的最上边提供了下载地址,可自行下载。
至此,后端全部修改完毕。
二、前端修改
其实前端改的比较简单,不容易出什么错,就不分步骤了,简单一说:
下载官方提供的前端修改代码,就我一开始说要下载的那个
链接: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ 提取码: y9jt
在ruoyi-ui目录下的文件不要全部替换到项目中,建议比较一下改动内容。
-
ruoyi-ui\package.json
dependencies 增加"crypto-js": "^4.1.1",
-
ruoyi-ui\src\views\login.vue
删除原来的验证码部分,增加滑块验证码相关代码<template> <div class="login"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"> <h3 class="title">若依后台管理系统</h3> <el-form-item prop="username"> <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号" > <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /> </el-input> </el-form-item> <el-form-item prop="password"> <el-input v-model="loginForm.password" type="password" auto-complete="off" placeholder="密码" @keyup.enter.native="handleLogin" > <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /> </el-input> </el-form-item> <Verify @success="capctchaCheckSuccess" :mode="'pop'" :captchaType="'blockPuzzle'" :imgSize="{ width: '330px', height: '155px' }" ref="verify" ></Verify> <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> <el-form-item style="width:100%;"> <el-button :loading="loading" size="medium" type="primary" style="width:100%;" @click.native.prevent="handleLogin" > <span v-if="!loading">登 录</span> <span v-else>登 录 中...</span> </el-button> <div style="float: right;" v-if="register"> <router-link class="link-type" :to="'/register'">立即注册</router-link> </div> </el-form-item> </el-form> <!-- 底部 --> <div class="el-login-footer"> <span>Copyright © 2018-2022 ruoyi.vip All Rights Reserved.</span> </div> </div> </template> <script> import Cookies from "js-cookie"; import { encrypt, decrypt } from '@/utils/jsencrypt' import Verify from "@/components/Verifition/Verify"; export default { components: { Verify }, name: "Login", data() { return { loginForm: { username: "admin", password: "admin123", rememberMe: false, code: "", }, loginRules: { username: [ { required: true, trigger: "blur", message: "请输入您的账号" } ], password: [ { required: true, trigger: "blur", message: "请输入您的密码" } ], }, loading: false, // 注册开关 register: false, redirect: undefined }; }, watch: { $route: { handler: function(route) { this.redirect = route.query && route.query.redirect; }, immediate: true } }, created() { this.getCookie(); }, methods: { getCookie() { const username = Cookies.get("username"); const password = Cookies.get("password"); const rememberMe = Cookies.get('rememberMe') this.loginForm = { username: username === undefined ? this.loginForm.username : username, password: password === undefined ? this.loginForm.password : decrypt(password), rememberMe: rememberMe === undefined ? false : Boolean(rememberMe) }; }, capctchaCheckSuccess(params) { this.loginForm.code = params.captchaVerification; this.loading = true; if (this.loginForm.rememberMe) { Cookies.set("username", this.loginForm.username, { expires: 30 }); Cookies.set("password", encrypt(this.loginForm.password), { expires: 30, }); Cookies.set("rememberMe", this.loginForm.rememberMe, { expires: 30 }); } else { Cookies.remove("username"); Cookies.remove("password"); Cookies.remove("rememberMe"); } this.$store.dispatch("Login", this.loginForm).then(() => { this.$router.push({ path: this.redirect || "/" }).catch(() => {}); }).catch(() => { this.loading = false; }); }, handleLogin() { this.$refs.loginForm.validate((valid) => { if (valid) { this.$refs.verify.show(); } }); } } }; </script> <style rel="stylesheet/scss" lang="scss"> .login { display: flex; justify-content: center; align-items: center; height: 100%; background-image: url("../assets/images/login-background.jpg"); background-size: cover; } .title { margin: 0px auto 30px auto; text-align: center; color: #707070; } .login-form { border-radius: 6px; background: #ffffff; width: 400px; padding: 25px 25px 5px 25px; .el-input { height: 38px; input { height: 38px; } } .input-icon { height: 39px; width: 14px; margin-left: 2px; } } .login-tip { font-size: 13px; text-align: center; color: #bfbfbf; } .login-code { width: 33%; height: 38px; float: right; img { cursor: pointer; vertical-align: middle; } } .el-login-footer { height: 40px; line-height: 40px; position: fixed; bottom: 0; width: 100%; text-align: center; color: #fff; font-family: Arial; font-size: 12px; letter-spacing: 1px; } .login-code-img { height: 38px; } </style>
- 其他资源
将 “集成aj-captcha实现滑块验证码\ruoyi-ui\src\“ 目录下assets、components两个目录直接复制到项目中。
- 其他资源
前端更新crypto-js (npm install --save crypto-js)
api文件夹里的东西可以不改,看一眼就知道,api里边没新增东西,只删了一些原来的验证码用的,现在用不到了,不删也能运行。
然后运行应该就能正常用了
标签:username,滑块,aj,Ruoyi,ruoyi,captcha,import,password,com From: https://www.cnblogs.com/RioTian/p/17230872.html