@ -0,0 +1,25 @@
|
|||||||
|
package cn.iocoder.yudao.framework.captcha.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ClassUtil;
|
||||||
|
import cn.iocoder.yudao.framework.captcha.core.enums.CaptchaRedisKeyConstants;
|
||||||
|
import cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl;
|
||||||
|
import com.anji.captcha.service.CaptchaCacheService;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class YudaoCaptchaConfiguration {
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到
|
||||||
|
// 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举
|
||||||
|
ClassUtil.loadClass(CaptchaRedisKeyConstants.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CaptchaCacheService captchaCacheService(StringRedisTemplate stringRedisTemplate) {
|
||||||
|
return new RedisCaptchaServiceImpl(stringRedisTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package cn.iocoder.yudao.framework.captcha.core.service;
|
||||||
|
|
||||||
|
import com.anji.captcha.service.CaptchaCacheService;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于 Redis 实现验证码的存储
|
||||||
|
*
|
||||||
|
* @author 星语
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor // 保证 aj-captcha 的 SPI 创建
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class RedisCaptchaServiceImpl implements CaptchaCacheService {
|
||||||
|
|
||||||
|
@Resource // 保证 aj-captcha 的 SPI 创建时的注入
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String type() {
|
||||||
|
return "redis";
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 Boolean.TRUE.equals(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
cn.iocoder.yudao.framework.captcha.config.YudaoCaptchaConfiguration
|
||||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 29 KiB |
@ -1,3 +0,0 @@
|
|||||||
### 请求 /captcha/get-image 接口 => 成功
|
|
||||||
GET {{baseUrl}}/system/captcha/get-image
|
|
||||||
tenant-id: {{adminTenentId}}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.system.controller.admin.common;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
|
|
||||||
import cn.iocoder.yudao.module.system.service.common.CaptchaService;
|
|
||||||
import io.swagger.annotations.Api;
|
|
||||||
import io.swagger.annotations.ApiOperation;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.annotation.security.PermitAll;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
|
||||||
|
|
||||||
@Api(tags = "管理后台 - 验证码")
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/system/captcha")
|
|
||||||
public class CaptchaController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CaptchaService captchaService;
|
|
||||||
|
|
||||||
@GetMapping("/get-image")
|
|
||||||
@PermitAll
|
|
||||||
@ApiOperation("生成图片验证码")
|
|
||||||
public CommonResult<CaptchaImageRespVO> getCaptchaImage() {
|
|
||||||
return success(captchaService.getCaptchaImage());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.system.convert.common;
|
|
||||||
|
|
||||||
import cn.hutool.captcha.AbstractCaptcha;
|
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
|
|
||||||
import org.mapstruct.Mapper;
|
|
||||||
import org.mapstruct.factory.Mappers;
|
|
||||||
|
|
||||||
@Mapper
|
|
||||||
public interface CaptchaConvert {
|
|
||||||
|
|
||||||
CaptchaConvert INSTANCE = Mappers.getMapper(CaptchaConvert.class);
|
|
||||||
|
|
||||||
default CaptchaImageRespVO convert(String uuid, AbstractCaptcha captcha) {
|
|
||||||
return CaptchaImageRespVO.builder().uuid(uuid).img(captcha.getImageBase64()).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.system.framework.captcha.config;
|
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableConfigurationProperties(CaptchaProperties.class)
|
|
||||||
public class CaptchaConfig {
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.common;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证码 Service 接口
|
|
||||||
*/
|
|
||||||
public interface CaptchaService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得验证码图片
|
|
||||||
*
|
|
||||||
* @return 验证码图片
|
|
||||||
*/
|
|
||||||
CaptchaImageRespVO getCaptchaImage();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否开启图片验证码
|
|
||||||
*
|
|
||||||
* @return 是否
|
|
||||||
*/
|
|
||||||
Boolean isCaptchaEnable();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得 uuid 对应的验证码
|
|
||||||
*
|
|
||||||
* @param uuid 验证码编号
|
|
||||||
* @return 验证码
|
|
||||||
*/
|
|
||||||
String getCaptchaCode(String uuid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除 uuid 对应的验证码
|
|
||||||
*
|
|
||||||
* @param uuid 验证码编号
|
|
||||||
*/
|
|
||||||
void deleteCaptchaCode(String uuid);
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.common;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
|
|
||||||
import cn.iocoder.yudao.module.system.dal.redis.common.CaptchaRedisDAO;
|
|
||||||
import cn.iocoder.yudao.module.system.framework.captcha.config.CaptchaProperties;
|
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseRedisUnitTest;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
@Import({CaptchaServiceImpl.class, CaptchaProperties.class, CaptchaRedisDAO.class})
|
|
||||||
public class CaptchaServiceTest extends BaseRedisUnitTest {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CaptchaServiceImpl captchaService;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private CaptchaRedisDAO captchaRedisDAO;
|
|
||||||
@Resource
|
|
||||||
private CaptchaProperties captchaProperties;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetCaptchaImage() {
|
|
||||||
// 调用
|
|
||||||
CaptchaImageRespVO respVO = captchaService.getCaptchaImage();
|
|
||||||
// 断言
|
|
||||||
assertNotNull(respVO.getUuid());
|
|
||||||
assertNotNull(respVO.getImg());
|
|
||||||
String captchaCode = captchaRedisDAO.get(respVO.getUuid());
|
|
||||||
assertNotNull(captchaCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetCaptchaCode() {
|
|
||||||
// 准备参数
|
|
||||||
String uuid = randomString();
|
|
||||||
String code = randomString();
|
|
||||||
// mock 数据
|
|
||||||
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
String resultCode = captchaService.getCaptchaCode(uuid);
|
|
||||||
// 断言
|
|
||||||
assertEquals(code, resultCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeleteCaptchaCode() {
|
|
||||||
// 准备参数
|
|
||||||
String uuid = randomString();
|
|
||||||
String code = randomString();
|
|
||||||
// mock 数据
|
|
||||||
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
captchaService.deleteCaptchaCode(uuid);
|
|
||||||
// 断言
|
|
||||||
assertNull(captchaRedisDAO.get(uuid));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,47 +1,60 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 登录方法
|
// 登录方法
|
||||||
export function login(username, password, code, uuid) {
|
export function login(username, password, captchaVerification) {
|
||||||
const data = {
|
const data = {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
code,
|
captchaVerification
|
||||||
uuid
|
}
|
||||||
}
|
return request({
|
||||||
return request({
|
url: '/system/auth/login',
|
||||||
url: '/system/auth/login',
|
headers: {
|
||||||
headers: {
|
isToken: false
|
||||||
isToken: false
|
},
|
||||||
},
|
'method': 'POST',
|
||||||
'method': 'post',
|
'data': data
|
||||||
'data': data
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户详细信息
|
// 获取用户详细信息
|
||||||
export function getInfo() {
|
export function getInfo() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/auth/get-permission-info',
|
url: '/system/auth/get-permission-info',
|
||||||
'method': 'get'
|
'method': 'GET'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 退出方法
|
// 退出方法
|
||||||
export function logout() {
|
export function logout() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/auth/logout',
|
url: '/system/auth/logout',
|
||||||
'method': 'post'
|
'method': 'POST'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取验证码
|
// 获取验证码
|
||||||
export function getCodeImg() {
|
export function getCaptcha(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/captcha/get-image',
|
url: '/captcha/get',
|
||||||
headers: {
|
headers: {
|
||||||
isToken: false
|
isToken: false,
|
||||||
},
|
isTenant: false
|
||||||
method: 'get',
|
},
|
||||||
timeout: 20000
|
method: 'POST',
|
||||||
})
|
'data': data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证验证码
|
||||||
|
export function checkCaptcha(data) {
|
||||||
|
return request({
|
||||||
|
url: '/captcha/check',
|
||||||
|
headers: {
|
||||||
|
isToken: false,
|
||||||
|
isTenant: false
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
'data': data
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
import CryptoJS from 'crypto-js'
|
||||||
|
/**
|
||||||
|
* @word 要加密的内容
|
||||||
|
* @keyWord String 服务器随机返回的关键字
|
||||||
|
* */
|
||||||
|
export function aesEncrypt(word, keyWord = "XwKsGlMcdPMEhR1B") {
|
||||||
|
var key = CryptoJS.enc.Utf8.parse(keyWord);
|
||||||
|
var srcs = CryptoJS.enc.Utf8.parse(word);
|
||||||
|
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
|
||||||
|
mode: CryptoJS.mode.ECB,
|
||||||
|
padding: CryptoJS.pad.Pkcs7
|
||||||
|
});
|
||||||
|
return encrypted.toString();
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import config from '@/config'
|
||||||
|
const baseUrl = config.baseUrl
|
||||||
|
export const myRequest = (option = {}) => {
|
||||||
|
return new Promise((reslove, reject) => {
|
||||||
|
uni.request({
|
||||||
|
url: baseUrl + option.url,
|
||||||
|
data: option.data,
|
||||||
|
method: option.method || "GET",
|
||||||
|
success: (result) => {
|
||||||
|
reslove(result)
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"crypto-js": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 20 KiB |
@ -0,0 +1,3 @@
|
|||||||
|
import Verify from './src/Verify.vue'
|
||||||
|
|
||||||
|
export { Verify }
|
||||||