若依 ruoyi-vue3 集成aj-captcha实现滑块、文字点选验证码

目录

  • 0. 前言
    • 0.1 说明
  • 1. 后端部分
    • 1.1 添加依赖
    • 1.2. 修改 application.yml
    • 1.3. 新增 CaptchaRedisService 类
    • 1.4. 添加必须文件
    • 1.5. 移除不需要的类
    • 1.6. 修改登录方法
    • 1.7. 新增验证码开关获取接口
    • 1.8. 允许匿名访问
  • 2. 前端部分(Vue3)
    • 2.1. 新增依赖 crypto-js
    • 2.2. 新增 Verifition 组件
    • 2.3. 修改login.js
    • 2.4. 修改 user.js
    • 2.5. 修改login.vue
    • 2.6. 切换文字点选或滑块验证码
      • 2.6.1 后端修改
      • 2.6.2 前端修改
    • 2.7. 成果展示:


0. 前言

其实若依的官方文档中有集成aj-captcha实现滑块验证码的部分,但是一直给的前端示例代码中都是Vue2的版本,而且后端部分也一直未保持更新。再比如官方文档在集成aj-captcha后并未实现验证码开关的功能。

然后我最近正好在用若依的Vue3版本做东西,正好记录一下。

0.1 说明

  1. 以官方文档为模板写的这篇文章,所以中间会穿插官方文档中的一些文字。

  2. 文章中所涉及的截图、代码,由于我已经使用 若依框架包名修改器 修改过了,所以包名、模块名前缀会和原版有出入,但仅限于包名和模块名。请注意甄别。

  3. 本文基于后端RuoYi-Vue 3.8.7 和 前端 RuoYi-Vue3 3.8.7

  4. 官方文档在集成后并没有实现验证码开关功能,本文会进行实现。

集成以AJ-Captcha文字点选验证码为例,不需要键盘手动输入,极大优化了传统验证码用户体验不佳的问题。目前对外提供两种类型的验证码,其中包含滑动拼图、文字点选。

1. 后端部分

1.1 添加依赖

ruoyi-framework 模块中的 pom.xml 添加以下依赖:

<!-- 滑块验证码  -->
<dependency><groupId>com.github.anji-plus</groupId><artifactId>captcha-spring-boot-starter</artifactId><version>1.2.7</version>
</dependency>

删除原本的 kaptcha 验证码依赖:

<!-- 验证码 -->
<dependency><groupId>pro.fessional</groupId><artifactId>kaptcha</artifactId><exclusions><exclusion><artifactId>servlet-api</artifactId><groupId>javax.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: 2

1.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. 添加必须文件

  1. ruoyi-admin 模块下,找到 resources 目录
  2. resources 目录找到 META-INF 目录
  3. META-INF 目录中新建 services 文件夹
  4. services 文件夹中新建 com.anji.captcha.service.CaptchaCacheService 文件(注意是文件)
  5. com.anji.captcha.service.CaptchaCacheService 文件中输入 xxx.xxx.framework.web.service.CaptchaRedisService (也就是刚刚创建的CaptchaRedisService类的真实路径)
    在这里插入图片描述

1.5. 移除不需要的类

  1. ruoyi-admin 模块下 com.ruoyi.web.controller.common.CaptchaController.java
  2. ruoyi-framework 模块下 com.ruoyi.framework.config.CaptchaConfig.java
  3. ruoyi-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;@Autowired@Lazyprivate 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 --registry=https://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

修改内容较多,建议直接替换再修改:

<template><div class="login"><el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form"><h3 class="title">若依后台管理系统</h3><el-form-item prop="username"><el-inputv-model="loginForm.username"type="text"size="large"auto-complete="off"placeholder="账号"><template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon"/></template></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="loginForm.password"type="password"size="large"auto-complete="off"placeholder="密码"@keyup.enter="handleLogin"><template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon"/></template></el-input></el-form-item><Verify@success="capctchaCheckSuccess":mode="'pop'":captchaType="'clickWord'":imgSize="{ width: '330px', height: '155px' }"ref="verify"v-if="captchaEnabled"></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="large"type="primary"style="width:100%;"@click.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-2023 ruoyi.vip All Rights Reserved.</span></div></div>
</template><script 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();
</script><style lang='scss' 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;
}
</style>

2.6. 切换文字点选或滑块验证码

有两种类型,一种是文字点选,一种是滑块验证,那如何切换呢?

2.6.1 后端修改

修改fcat-admin模块下 application.yml 中的 aj — type

  • 填写blockPuzzle 为滑块
  • 填写 clickWord 为文字点选

在这里插入图片描述

2.6.2 前端修改

修改 login.vue

<Verify@success="capctchaCheckSuccess":mode="'pop'":captchaType="'clickWord'":imgSize="{ width: '330px', height: '155px' }"ref="verify"v-if="captchaEnabled"
></Verify>

修改上述代码中的 captchaType

  • 填写blockPuzzle 为滑块
  • 填写 clickWord 为文字点选

在这里插入图片描述

2.7. 成果展示:

  • 默认底图展示,用于接口异常等情况:

    在这里插入图片描述

  • 滑块验证码正常显示截图:
    在这里插入图片描述

  • 文字点选验证码正常显示截图:
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/223418.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“一键调整尺寸,轻松完成视频批量剪辑:批量放大视频尺寸“

你是否曾经遇到过需要批量调整视频尺寸的情况&#xff1f;无论是为了适应不同的播放平台&#xff0c;还是为了满足客户的特定需求&#xff0c;批量调整视频尺寸都是一项繁琐而耗时的工作。但是&#xff0c;现在有一种方法可以让你轻松完成这项任务&#xff0c;那就是使用我们的…

Excel高效办公:文秘与行政办公的智能化革新

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f91f; 代理 IP 推荐&#xff1a;&#x1f449;品易 HTTP 代理 IP &#x1f485; 想寻找共同学习交流的小伙伴&#xff0c…

超声波清洗机怎么买不会踩坑?减少踩坑机会超声波清洗机推荐

在当今快节奏的生活中&#xff0c;超声波清洗机已经成为了我们生活中不可或缺的助手。无论是清洗餐具、首饰&#xff0c;还是清洗医疗设备、实验室仪器&#xff0c;超声波清洗机都能以其高效、便捷的特性&#xff0c;帮助我们解决各种清洁难题。然而&#xff0c;面对市场上琳琅…

ZeroSSL-ip证书配置

1.申请证书 Free SSL Certificates and SSL Tools - ZeroSSL 2.填入公网 IP 地址 3.选择90天免费 SSL 4.自动生成CSR 5.选择文件验证方式 使用80端口,建立对应的文件并进行访问测试 6. 进行认证 7.下载证书并进行配置 8.合并ssl证书 对于 Nginx 服务器,需要将 ca_bundle.crt…

ARM KEIL 安装

根据设备类型安装开发工具及环境 Arm,Cortex ----> MDK-Arm 8051 ----> C51 80251 ----> C251 C166,XC166,XC2000 MCU设备 ----> C155 填写信息提交后下载 点击MDK539.EXE下载 : MDK539.EXE 双击MDK539安装 点击Next 默认安装路径,点击Ne…

MySQL运维5-Mycat配置

一、schema.xml 1.1 简介 schema.xml作为Mycat中最重要的配置文件之一&#xff0c;涵盖了Mycat的逻辑库、逻辑表、分片规则、分片节点即数据源的配置。主要包括一下三组标签 schema标签 datanode标签 datahost标签 1.2 schema标签 用于定于Mycat实例中的逻辑库&#xff0c;一个…

FPGA - 1、Simulink HDL coder模型例化到FPGA

Simulink HDL coder模型例化到FPGA 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右…

匿名内部类与Lambda表达式

深入了解Java的匿名内部类 Java作为一种面向对象的编程语言&#xff0c;提供了许多灵活的特性&#xff0c;其中之一就是匿名内部类。匿名内部类是一种没有名字的局部内部类&#xff0c;通常用于创建只需在一个地方使用的类的实例。 什么是匿名内部类&#xff1f; 匿名内部类是…

Android12编译kernel4.9解决:scripts/gcc-wrapper.py, line 79, in run_gcc(一百六十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

CLIP 改进工作串讲(上)【论文精读·42】

大家好&#xff0c;今天这个串讲其实是主要想说一下在过去一年中&#xff0c;大家是怎么把 CLIP 这个模型或者 CLIP 这个思想应用到其他领域中去的。本来是想先做一下这个多模态的串讲的&#xff0c;但是就在前几天看到了 CLICK puzzle 这篇论文&#xff0c;获得了这个今年 c G…

Java 反射:探索运行时行为的强大工具

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、浅层理解 1.1 定义 1.2 我的理解 二、反射基础与应用 2.1 反射基础 2.2 反射的高级应用 三、一些反射的例子 3.1 获取类…

常见Web开发安全漏洞的防御手段

一、Web开发安全漏洞的防御手段方案 输入验证和过滤&#xff1a;对用户输入进行严格的验证和过滤&#xff0c;确保输入的数据符合预期的格式和类型&#xff0c;防止恶意输入或注入攻击。参数化查询&#xff1a;使用预编译的SQL语句和参数化查询接口&#xff0c;避免将用户输入…

Notion开源平替知识库软件AFFiNE本地部署与公网访问远程协作

文章目录 前言1. 使用Docker安装AFFINE2. 安装cpolar内网穿透工具3. 配置AFFINE公网访问地址4. 实现公网远程访问AFFINE5. 结语 前言 本篇文章讲解Notion开源平替全能知识库工具AFFINE如何本地部署&#xff0c;并实现公网远程访问。AFFiNE 是一个全新的开源项目&#xff0c;旨…

分析若依的文件上传处理逻辑

分析若依的文件上传处理逻辑 注&#xff1a;已经从若依框架完成拆分&#xff0c;此处单独分析一下人家精彩的封装&#xff0c;也来理解一下怎么做一个通用的上传接口&#xff01;如有分析的&#xff0c;理解的不透彻的地方&#xff0c;大家多多包含&#xff0c;欢迎批评指正&am…

LabVIEW在高铁温度与振动监测中的应用

​LabVIEW在高铁温度与振动监测中的应用 高速铁路的可靠性和安全性是现代铁路运输系统设计和运营的重中之重。LabVIEW软件作为一个多功能、可扩展的图形编程环境&#xff0c;提供了一个理想的平台&#xff0c;用于开发高铁监测系统&#xff0c;不仅监测实时数据&#xff0c;也…

C# WPF上位机开发(属性页面的设计)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在软件开发中&#xff0c;属性或者参数设置是很重要的一个部分。这个时候如果不想通过动态添加控件的方法来处理的话&#xff0c;那么可以通过tab控…

jrebel debug 启动不起来

idea更新之后jrebel debug模式启动不起来。 将下面的设置取消之后就可以了&#xff0c;希望能帮到你们… 被卡了两天… jrebel信息。 idea IntelliJ IDEA 2023.3.1 (Ultimate Edition) Build #IU-233.11799.300, built on December 12, 2023 Licensed to Alexandra Martin…

GitHub Universe 2023 Watch Party in Shanghai:在开源世界中找到真我

文章目录 ⭐ 前言⭐ “我”的开源之旅⭐ 为什么要做开源⭐ 要如何做好开源⭐ 开源的深度影响⭐ 小结 ⭐ 前言 周末有幸参加了在上海举行的 GitHub Universe 2023 Watch Party&#xff0c;这是一个充满激情和活力的开源开发者日。我有幸聆听了一场特别令人印象深刻的演讲&#…

Python接口测试框架选择之pytest+yaml+Allure!

一、为什么选择pytest&#xff1f; pytest完全兼容python自带的unittest pytest让单元测试更简单&#xff0c;能很好的管理测试用例。 对于实现接口测试的复杂场景&#xff0c;pytest的fixture、PDB等高阶用法都能实现需求。 入门简单&#xff0c;对于代码基础薄弱的团队人员…

Mysql - 常用插入数据的三种方法详解及练习

目录 &#x1f959;8.1.1 mysql中常用的三种插入数据的语句 1. insert into - 插入数据 2. replace into - 插入替换数据 3. insert ignore - 如果已存在&#xff0c;忽略当前新数据 &#x1f959;8.1.2 以上三种方法的练习及区分 &#x1f959;8.1.3 说明 &#x1f959…