我有虚拟服务器怎么快速做网站,专业建设方案,如何建立内部网站,wordpress百度云插件下载目录 0. 前言0.1 说明 1. 后端部分1.1 添加依赖1.2. 修改 application.yml1.3. 新增 CaptchaRedisService 类1.4. 添加必须文件1.5. 移除不需要的类1.6. 修改登录方法1.7. 新增验证码开关获取接口1.8. 允许匿名访问 2. 前端部分#xff08;Vue3#xff09;2.1. 新增依赖 cryp… 目录 0. 前言0.1 说明 1. 后端部分1.1 添加依赖1.2. 修改 application.yml1.3. 新增 CaptchaRedisService 类1.4. 添加必须文件1.5. 移除不需要的类1.6. 修改登录方法1.7. 新增验证码开关获取接口1.8. 允许匿名访问 2. 前端部分Vue32.1. 新增依赖 crypto-js2.2. 新增 Verifition 组件2.3. 修改login.js2.4. 修改 user.js2.5. 修改login.vue2.6. 切换文字点选或滑块验证码2.6.1 后端修改2.6.2 前端修改 2.7. 成果展示 0. 前言
其实若依的官方文档中有集成aj-captcha实现滑块验证码的部分但是一直给的前端示例代码中都是Vue2的版本而且后端部分也一直未保持更新。再比如官方文档在集成aj-captcha后并未实现验证码开关的功能。
然后我最近正好在用若依的Vue3版本做东西正好记录一下。
0.1 说明 以官方文档为模板写的这篇文章所以中间会穿插官方文档中的一些文字。 文章中所涉及的截图、代码由于我已经使用 若依框架包名修改器 修改过了所以包名、模块名前缀会和原版有出入但仅限于包名和模块名。请注意甄别。 本文基于后端RuoYi-Vue 3.8.7 和 前端 RuoYi-Vue3 3.8.7 官方文档在集成后并没有实现验证码开关功能本文会进行实现。 集成以AJ-Captcha文字点选验证码为例不需要键盘手动输入极大优化了传统验证码用户体验不佳的问题。目前对外提供两种类型的验证码其中包含滑动拼图、文字点选。 1. 后端部分
1.1 添加依赖
在 ruoyi-framework 模块中的 pom.xml 添加以下依赖
!-- 滑块验证码 --
dependencygroupIdcom.github.anji-plus/groupIdartifactIdcaptcha-spring-boot-starter/artifactIdversion1.2.7/version
/dependency删除原本的 kaptcha 验证码依赖
!-- 验证码 --
dependencygroupIdpro.fessional/groupIdartifactIdkaptcha/artifactIdexclusionsexclusionartifactIdservlet-api/artifactIdgroupIdjavax.servlet/groupId/exclusion/exclusions
/dependency最终 pom.xml 截图 1.2. 修改 application.yml
修改application.yml加入aj-captcha相关配置 我的项目使用的是文字点选如需要使用滑块type 设置为 blockPuzzle 即可
# 滑块验证码
aj:captcha:# 缓存类型cache-type: redis# blockPuzzle 滑块 clickWord 文字点选 default默认两者都实例化type: clickWord# 右下角显示字water-mark: B站、抖音同名搜索七维大脑# 校验滑动拼图允许误差偏移量(默认5像素)slip-offset: 5# aes加密坐标开启或者禁用(true|false)aes-status: true# 滑动干扰项(0/1/2)interference-options: 21.3. 新增 CaptchaRedisService 类
在 ruoyi-framework 模块下com.ruoyi.framework.web.service 包下创建CaptchaRedisService.java 类内容如下
(请复制粘贴后注意修改包路径为自己项目真实路径)
package xyz.ytxy.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
{Autowiredprivate StringRedisTemplate stringRedisTemplate;Overridepublic void set(String key, String value, long expiresInSeconds){stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS);}Overridepublic boolean exists(String key){return Boolean.TRUE.equals(stringRedisTemplate.hasKey(key));}Overridepublic void delete(String key){stringRedisTemplate.delete(key);}Overridepublic String get(String key){return stringRedisTemplate.opsForValue().get(key);}Overridepublic Long increment(String key, long val){return stringRedisTemplate.opsForValue().increment(key, val);}Overridepublic String type(){return redis;}
} 1.4. 添加必须文件
在ruoyi-admin 模块下找到 resources 目录在 resources 目录找到 META-INF 目录在 META-INF 目录中新建 services 文件夹在 services 文件夹中新建 com.anji.captcha.service.CaptchaCacheService 文件注意是文件在 com.anji.captcha.service.CaptchaCacheService 文件中输入 xxx.xxx.framework.web.service.CaptchaRedisService 也就是刚刚创建的CaptchaRedisService类的真实路径
1.5. 移除不需要的类
ruoyi-admin 模块下 com.ruoyi.web.controller.common.CaptchaController.javaruoyi-framework 模块下 com.ruoyi.framework.config.CaptchaConfig.javaruoyi-framework 模块下 com.ruoyi.framework.config.KaptchaTextCreator.java
1.6. 修改登录方法
修改 ruoyi-admin 模块下 com.ruoyi.web.controller.system.SysLoginController.java 类中的 login 方法
/*** 登录方法** param loginBody 登录信息* return 结果*/PostMapping(/login)public AjaxResult login(RequestBody LoginBody loginBody){AjaxResult ajax AjaxResult.success();// 生成令牌String token loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode());ajax.put(Constants.TOKEN, token);return ajax;}修改后生成令牌这一步比原版少了 loginBody.getUuid() 参数。
修改 ruoyi-framework 模块下的com.ruoyi.framework.web.service.SysLoginService.java类
package xyz.ytxy.framework.web.service;import javax.annotation.Resource;import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
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 xyz.ytxy.common.constant.CacheConstants;
import xyz.ytxy.common.constant.Constants;
import xyz.ytxy.common.constant.UserConstants;
import xyz.ytxy.common.core.domain.entity.SysUser;
import xyz.ytxy.common.core.domain.model.LoginUser;
import xyz.ytxy.common.core.redis.RedisCache;
import xyz.ytxy.common.exception.ServiceException;
import xyz.ytxy.common.exception.user.BlackListException;
import xyz.ytxy.common.exception.user.CaptchaException;
import xyz.ytxy.common.exception.user.CaptchaExpireException;
import xyz.ytxy.common.exception.user.UserNotExistsException;
import xyz.ytxy.common.exception.user.UserPasswordNotMatchException;
import xyz.ytxy.common.utils.DateUtils;
import xyz.ytxy.common.utils.MessageUtils;
import xyz.ytxy.common.utils.StringUtils;
import xyz.ytxy.common.utils.ip.IpUtils;
import xyz.ytxy.framework.manager.AsyncManager;
import xyz.ytxy.framework.manager.factory.AsyncFactory;
import xyz.ytxy.framework.security.context.AuthenticationContextHolder;
import xyz.ytxy.system.service.ISysConfigService;
import xyz.ytxy.system.service.ISysUserService;/*** 登录校验方法** author ruoyi*/
Component
public class SysLoginService
{Autowiredprivate TokenService tokenService;Resourceprivate AuthenticationManager authenticationManager;Autowiredprivate RedisCache redisCache;Autowiredprivate ISysUserService userService;Autowiredprivate ISysConfigService configService;AutowiredLazyprivate CaptchaService captchaService;/*** 登录验证** param username 用户名* param password 密码* param code 验证码* return 结果*/public String login(String username, String password, String code){// 验证码校验validateCaptcha(username, code);// 登录前置校验loginPreCheck(username, password);// 用户验证Authentication authentication null;try{UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(username, password);AuthenticationContextHolder.setContext(authenticationToken);// 该方法会去调用UserDetailsServiceImpl.loadUserByUsernameauthentication 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());// 生成tokenreturn tokenService.createToken(loginUser);}/*** 校验验证码** param username 用户名* param code 验证码* return 结果*/public void validateCaptcha(String username, String code){boolean captchaEnabled configService.selectCaptchaEnabled();if (captchaEnabled){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();}}}/*** 登录前置校验* param username 用户名* param password 用户密码*/public void loginPreCheck(String username, String password){// 用户名或密码为空 错误if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message(not.null)));throw new UserNotExistsException();}// 密码如果不在指定范围内 错误if (password.length() UserConstants.PASSWORD_MIN_LENGTH|| password.length() UserConstants.PASSWORD_MAX_LENGTH){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message(user.password.not.match)));throw new UserPasswordNotMatchException();}// 用户名不在指定范围内 错误if (username.length() UserConstants.USERNAME_MIN_LENGTH|| username.length() UserConstants.USERNAME_MAX_LENGTH){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message(user.password.not.match)));throw new UserPasswordNotMatchException();}// IP黑名单校验String blackStr configService.selectConfigByKey(sys.login.blackIPList);if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message(login.blocked)));throw new BlackListException();}}/*** 记录登录信息** param userId 用户ID*/public void recordLoginInfo(Long userId){SysUser sysUser new SysUser();sysUser.setUserId(userId);sysUser.setLoginIp(IpUtils.getIpAddr());sysUser.setLoginDate(DateUtils.getNowDate());userService.updateUserProfile(sysUser);}
}
login 方法比原版少了 uuid 的参数validateCaptcha 方法比原版少了 uuid 的参数方法内容更改为aj-captcha的验证方式其他内容未更改
这地方如果直接替换官方文档中的代码会造成部分新功能缺失。所以这里直接替换我提供的代码即可。注意替换后将包名改为你实际的包名
1.7. 新增验证码开关获取接口
在 ruoyi-admin 模块下的 com.ruoyi.web.controller.common 包新增 CaptchaEnabledController.java
注意将包名改为你实际的包名
package xyz.ytxy.web.controller.common;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import xyz.ytxy.common.core.domain.AjaxResult;
import xyz.ytxy.system.service.ISysConfigService;/*** 验证码操作处理** author B站、抖音搜索七维大脑 点个关注呗*/
RestController
public class CaptchaEnabledController {Autowiredprivate ISysConfigService configService;/*** 获取验证码开关*/GetMapping(/captchaEnabled)public AjaxResult captchaEnabled() {AjaxResult ajax AjaxResult.success();boolean captchaEnabled configService.selectCaptchaEnabled();ajax.put(captchaEnabled, captchaEnabled);return ajax;}
}
1.8. 允许匿名访问
在ruoyi-framework模块下的 com.ruoyi.framework.config 包下找到 SecurityConfig.java 类修改以下内容
原版
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers(/login, /register, /captchaImage).permitAll()修改为
// 对于登录login 注册register 滑块验证码/captcha/get /captcha/check 获取验证码开关 /captchaEnabled 允许匿名访问
.antMatchers(/login, /register, /captcha/get, /captcha/check, /captchaEnabled).permitAll()2. 前端部分Vue3
2.1. 新增依赖 crypto-js
在 package.json 的 dependencies 中新增 crypto-js: 4.1.1 新增后重新 install比如我用的pnpm直接执行pnpm install --registryhttps://registry.npmmirror.com
2.2. 新增 Verifition 组件
此部分代码我放到了阿里云盘https://www.alipan.com/s/4hEbavUC4Np
下载后粘贴到 src/components 目录下 2.3. 修改login.js
import request from /utils/request// 登录方法
export function login(username, password, code) {const data {username,password,code}return request({url: /login,headers: {isToken: false,repeatSubmit: false},method: post,data: data})
}// 注册方法
export function register(data) {return request({url: /register,headers: {isToken: false},method: post,data: data})
}// 获取用户详细信息
export function getInfo() {return request({url: /getInfo,method: get})
}// 退出方法
export function logout() {return request({url: /logout,method: post})
}// 获取验证码开关
export function isCaptchaEnabled() {return request({url: /captchaEnabled,method: get})
}修改了 login 函数去掉了 uuid 参数删除了获取验证码函数 getCodeImg新增了获取验证码开关函数 isCaptchaEnabled
2.4. 修改 user.js
删除 uuid 参数
// 登录login(userInfo) {const username userInfo.username.trim()const password userInfo.passwordconst code userInfo.codereturn new Promise((resolve, reject) {login(username, password, code).then(res {setToken(res.token)this.token res.tokenresolve()}).catch(error {reject(error)})})},2.5. 修改login.vue
修改内容较多建议直接替换再修改
templatediv classloginel-form refloginRef :modelloginForm :rulesloginRules classlogin-formh3 classtitle若依后台管理系统/h3el-form-item propusernameel-inputv-modelloginForm.usernametypetextsizelargeauto-completeoffplaceholder账号template #prefixsvg-icon icon-classuser classel-input__icon input-icon//template/el-input/el-form-itemel-form-item proppasswordel-inputv-modelloginForm.passwordtypepasswordsizelargeauto-completeoffplaceholder密码keyup.enterhandleLogintemplate #prefixsvg-icon icon-classpassword classel-input__icon input-icon//template/el-input/el-form-itemVerifysuccesscapctchaCheckSuccess:modepop:captchaTypeclickWord:imgSize{ width: 330px, height: 155px }refverifyv-ifcaptchaEnabled/Verifyel-checkbox v-modelloginForm.rememberMe stylemargin:0px 0px 25px 0px;记住密码/el-checkboxel-form-item stylewidth:100%;el-button:loadingloadingsizelargetypeprimarystylewidth:100%;click.preventhandleLoginspan v-if!loading登 录/spanspan v-else登 录 中.../span/el-buttondiv stylefloat: right; v-ifregisterrouter-link classlink-type :to/register立即注册/router-link/div/el-form-item/el-form!-- 底部 --div classel-login-footerspanCopyright © 2018-2023 ruoyi.vip All Rights Reserved./span/div/div
/templatescript setup
import Cookies from js-cookie;
import {encrypt, decrypt} from /utils/jsencrypt;
import useUserStore from /store/modules/user
import Verify from /components/Verifition/Verify;
import {isCaptchaEnabled} from /api/login;const userStore useUserStore()
const route useRoute();
const router useRouter();
const {proxy} getCurrentInstance();const loginForm ref({username: admin,password: admin123,rememberMe: false,code:
});const loginRules {username: [{required: true, trigger: blur, message: 请输入您的账号}],password: [{required: true, trigger: blur, message: 请输入您的密码}]
};const loading ref(false);
// 验证码开关
const captchaEnabled ref(true);
// 注册开关
const register ref(false);
const redirect ref(undefined);watch(route, (newRoute) {redirect.value newRoute.query newRoute.query.redirect;
}, {immediate: true});function userRouteLogin() {// 调用action的登录方法userStore.login(loginForm.value).then(() {const query route.query;const otherQueryParams Object.keys(query).reduce((acc, cur) {if (cur ! redirect) {acc[cur] query[cur];}return acc;}, {});router.push({path: redirect.value || /, query: otherQueryParams});}).catch(() {loading.value false;});
}function handleLogin() {proxy.$refs.loginRef.validate(valid {if (valid captchaEnabled.value) {proxy.$refs.verify.show();} else if (valid !captchaEnabled.value) {userRouteLogin();}});
}function getCookie() {const username Cookies.get(username);const password Cookies.get(password);const rememberMe Cookies.get(rememberMe);loginForm.value {username: username undefined ? loginForm.value.username : username,password: password undefined ? loginForm.value.password : decrypt(password),rememberMe: rememberMe undefined ? false : Boolean(rememberMe)};
}function capctchaCheckSuccess(params) {loginForm.value.code params.captchaVerification;loading.value true;// 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码if (loginForm.value.rememberMe) {Cookies.set(username, loginForm.value.username, {expires: 30});Cookies.set(password, encrypt(loginForm.value.password), {expires: 30,});Cookies.set(rememberMe, loginForm.value.rememberMe, {expires: 30});} else {// 否则移除Cookies.remove(username);Cookies.remove(password);Cookies.remove(rememberMe);}userRouteLogin();
}// 获取验证码开关
function getCaptchaEnabled() {isCaptchaEnabled().then(res {captchaEnabled.value res.captchaEnabled undefined ? true : res.captchaEnabled;});
}getCookie();
getCaptchaEnabled();
/scriptstyle langscss scoped
.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: 40px;input {height: 40px;}}.input-icon {height: 39px;width: 14px;margin-left: 0px;}
}.login-tip {font-size: 13px;text-align: center;color: #bfbfbf;
}.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;
}
/style2.6. 切换文字点选或滑块验证码
有两种类型一种是文字点选一种是滑块验证那如何切换呢
2.6.1 后端修改
修改fcat-admin模块下 application.yml 中的 aj — type
填写blockPuzzle 为滑块填写 clickWord 为文字点选 2.6.2 前端修改
修改 login.vue
VerifysuccesscapctchaCheckSuccess:modepop:captchaTypeclickWord:imgSize{ width: 330px, height: 155px }refverifyv-ifcaptchaEnabled
/Verify修改上述代码中的 captchaType
填写blockPuzzle 为滑块填写 clickWord 为文字点选 2.7. 成果展示 默认底图展示用于接口异常等情况 滑块验证码正常显示截图 文字点选验证码正常显示截图