@ -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'
|
||||
|
||||
// 登录方法
|
||||
export function login(username, password, code, uuid) {
|
||||
const data = {
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
uuid
|
||||
}
|
||||
return request({
|
||||
url: '/system/auth/login',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
'method': 'post',
|
||||
'data': data
|
||||
})
|
||||
export function login(username, password, captchaVerification) {
|
||||
const data = {
|
||||
username,
|
||||
password,
|
||||
captchaVerification
|
||||
}
|
||||
return request({
|
||||
url: '/system/auth/login',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
'method': 'POST',
|
||||
'data': data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
return request({
|
||||
url: '/system/auth/get-permission-info',
|
||||
'method': 'get'
|
||||
})
|
||||
return request({
|
||||
url: '/system/auth/get-permission-info',
|
||||
'method': 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出方法
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/system/auth/logout',
|
||||
'method': 'post'
|
||||
})
|
||||
return request({
|
||||
url: '/system/auth/logout',
|
||||
'method': 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
export function getCodeImg() {
|
||||
return request({
|
||||
url: '/system/captcha/get-image',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'get',
|
||||
timeout: 20000
|
||||
})
|
||||
export function getCaptcha(data) {
|
||||
return request({
|
||||
url: '/captcha/get',
|
||||
headers: {
|
||||
isToken: false,
|
||||
isTenant: false
|
||||
},
|
||||
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 }
|
||||