test
This commit is contained in:
parent
d7c391f644
commit
127c7ed8ab
@ -84,6 +84,26 @@
|
||||
<artifactId>dingtalk</artifactId>
|
||||
<version>2.0.20</version>
|
||||
</dependency>
|
||||
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.34.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>1.34.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.dtflys.forest</groupId>
|
||||
<artifactId>forest-spring-boot-starter</artifactId>
|
||||
<version>1.5.26</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package top.baogutang.admin.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: developer
|
||||
* @date: 2022/06/10 : 12:08
|
||||
*/
|
||||
@Configuration
|
||||
public class EncoderConfig {
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package top.baogutang.admin.config;
|
||||
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class SaTokenConfigure {
|
||||
/**
|
||||
* 重写 Sa-Token 框架内部算法策略
|
||||
*/
|
||||
@Autowired
|
||||
public void rewriteSaStrategy() {
|
||||
// 重写 Token 生成策略
|
||||
SaStrategy.me.createToken = (loginId, loginType) -> {
|
||||
return SaFoxUtil.getRandomString(60);
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package top.baogutang.admin.config;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import top.baogutang.common.domain.Results;
|
||||
|
||||
/**
|
||||
* @description: 全局异常处理
|
||||
* @author: nikooh
|
||||
* @date: 2023/06/15 : 12:23
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class SysGlobalExceptionHandler {
|
||||
|
||||
|
||||
public SysGlobalExceptionHandler() {
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler({NotLoginException.class})
|
||||
public Results<Object> businessNotLoginException(NotLoginException e) {
|
||||
log.error("请求发生错误,code:{},message:{}", e.getCode(), e.getMessage());
|
||||
return Results.failed(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package top.baogutang.admin.contants;
|
||||
|
||||
/**
|
||||
* Jwt相关常量类
|
||||
*
|
||||
* @author
|
||||
*/
|
||||
public class JwtConstant {
|
||||
|
||||
/**
|
||||
* token名称
|
||||
*/
|
||||
public static final String TOKEN_JwtConstantNAME = "authorization";
|
||||
|
||||
/**
|
||||
* 加密方式
|
||||
*/
|
||||
public static final String ALGORITHM_FAMILY_NAME = "RSA";
|
||||
|
||||
/**
|
||||
* 私钥(加密用)
|
||||
*/
|
||||
public static final String PRIVATE_KEY =
|
||||
"MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDM9s3eJdYkcmKyW6mOZcLaME/tf5cnbsIuW/t1j9jsIsol9owmGn2ZI+pVgjXZH3slOjst9w+FA83sReFmIIfbgpHNbiS0ooWw6eSR1/3mK/U2GYeAxRy3z0MvOqYfifSg/I/hWn/gQkKdXX5XKKBbpbWC5/xiSdDz2XxchxTEy8NNSQZTXQ5haVQ83iRgQVl1sLkg8qRbSoMaB5DIYnpYd+GjJ+YqPMbBsZcjul9Y+bF95PDPmFVpMUu61Exr6qCn+vvk0RqMOO7Fu5ZtFYIUjegtpM4x8jQQYk0EUcEnHQup99vCF17SPngAuU72bYfTiSh+e/niRTinnLbEFkWzAgMBAAECggEBAK9zRV64PMsOL3ZGCKqgaV+ko4bGBXn30bkle+dyr1nTnf9JQUcMvh7tI5b202l9DUkcw7PhHSPb0dZDK2LkzecVqgGUG0+VH6QFU2eU4P66+jjJObj9AkxF21j+d13gFcwI5pEdiwFJNwTGF3Q8jqhk/S+FrgHk9j0HGvGJoDmkOiTsdQm1A87itRUFODMKm2naQ15w4px/wBhBCF/gdY2uusKtSyuHVB00M9ooINaVJ/+S5PjtCEbIDc8ZLlropg6Uqs2DLsILAcVceeXc8V4jmZZaVOFVdokn47Q8NTeWgRyqbAJ+JjIIMIUkj8JHfbvD3Eyyh2eNN9BYhWBl+cECgYEA8+4Hioj7+K/nTTGTylxo4MOihSqMQxRyJJ8hKTGfNKTDC0PoGUnv7xaThmG9MFg/iIwNf3nEIaw5aaXSL3HTItFaKJ2a5/S6c7Gg6/vlyQ9MD5g+18okzOeYATfWqSP+4cADjWqGnEx0zxO4z7T4Th1io9G21ExstJVQhc1s1SsCgYEA1xstlCYJeym2I+v50DPpWCqXZ/ZhaQvXIAlAPl6FZ+qzph9mSaaXNRE0qHLcn+d+BbspCBta4uoIowV6vc4D2MdpM6UKvFesULFvkH5oLuKXEjMkYe+mTi1XqZ6FFwJaO0bBW8WtO0jPufyDHL+32WlVhQ4/Qn0g0cSTBhUvHZkCgYEAup5BwLgaZeFVyVYDpo7aYhLqFI0/r9ZFmUTxHs3q6mCfI9A5Epfha35PMUE3d3Qcb0AO15b1+XkEl5IYYtnnWTzniClDqPAvcXHVFpWp8A+29jtY4MizLNyGC3CH1vFF/7piPV/hSlSoDQEepDHkwgZzP7ei83rgs6uEZkmKWO8CgYEAnnwZwtObnLEIz8KnVm1I+Xq3/xMahikBIUtvmARQSlY0cqsj1BP3yFOa9plaUD5hLZvOCXkOJ63DG0mIO5w0XV3e9vwcwtPd075HsrBP1muAXxprLfVCFMt3kTOIX4GONapWAGXO0qU8141ilKX0sSzYo6Xxme1TZvpsUMj+49ECgYA5E5A1sNpqnEg/umbWBExkYuubqAzSQpf5X1lCtKDoveHrTmnv2IfccGY9o03tOfP0sutspC2epdHR6n5nUg/0eohtHMmE037yTJPv+Wk8uzuo4/1NYQljGK+rf1NpF5bLU0MJJDT+s5RRkwP2E8ZPKNFoPHgNuj4omt8Y+HP6RQ==";
|
||||
|
||||
/**
|
||||
* 公钥(解密用)
|
||||
*/
|
||||
public static final String PUBLIC_KEY =
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzPbN3iXWJHJislupjmXC2jBP7X+XJ27CLlv7dY/Y7CLKJfaMJhp9mSPqVYI12R97JTo7LfcPhQPN7EXhZiCH24KRzW4ktKKFsOnkkdf95iv1NhmHgMUct89DLzqmH4n0oPyP4Vp/4EJCnV1+VyigW6W1guf8YknQ89l8XIcUxMvDTUkGU10OYWlUPN4kYEFZdbC5IPKkW0qDGgeQyGJ6WHfhoyfmKjzGwbGXI7pfWPmxfeTwz5hVaTFLutRMa+qgp/r75NEajDjuxbuWbRWCFI3oLaTOMfI0EGJNBFHBJx0Lqffbwhde0j54ALlO9m2H04kofnv54kU4p5y2xBZFswIDAQAB";
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package top.baogutang.admin.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import top.baogutang.admin.services.ICommonService;
|
||||
import top.baogutang.common.domain.Results;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @description: 通用能力控制器
|
||||
* @author: nikooh
|
||||
* @date: 2023/06/19 : 12:17
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/common")
|
||||
public class CommonController {
|
||||
|
||||
@Resource
|
||||
private ICommonService commonService;
|
||||
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*
|
||||
* @param key 验证码key
|
||||
* @return 验证码
|
||||
*/
|
||||
@GetMapping("/randomImage/{key}")
|
||||
public Results<String> randomImage(@PathVariable("key") String key) {
|
||||
return Results.ok(commonService.randomImage(key));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package top.baogutang.admin.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.hutool.core.lang.PatternPool;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import top.baogutang.admin.domain.req.SysUserLoginReq;
|
||||
import top.baogutang.admin.domain.req.SysUserRegisterReq;
|
||||
import top.baogutang.admin.services.ISysUserService;
|
||||
import top.baogutang.common.domain.Results;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @description: 后台用户控制器
|
||||
* @author: nikooh
|
||||
* @date: 2023/06/19 : 11:57
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("api/v1/sysUser")
|
||||
public class SysUserController {
|
||||
|
||||
@Resource
|
||||
private ISysUserService sysUserService;
|
||||
|
||||
/**
|
||||
* 后台系统用户注册
|
||||
*
|
||||
* @param sysUserRegisterReq 注册请求参数
|
||||
* @return 注册结果
|
||||
*/
|
||||
@PostMapping("register")
|
||||
public Results<Void> register(@Validated @RequestBody SysUserRegisterReq sysUserRegisterReq) {
|
||||
if (!Validator.isMatchRegex(PatternPool.EMAIL, sysUserRegisterReq.getEmail())) {
|
||||
return Results.failed("邮箱格式不正确!");
|
||||
}
|
||||
if (!Validator.isMatchRegex(PatternPool.MOBILE, sysUserRegisterReq.getMobile())) {
|
||||
return Results.failed("手机号格式不正确!");
|
||||
}
|
||||
sysUserService.register(sysUserRegisterReq);
|
||||
return Results.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 登陆接口
|
||||
*
|
||||
* @param sysUserLoginReq 登陆请求参数
|
||||
* @return 登陆用户信息
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public Results<SaTokenInfo> login(@RequestBody SysUserLoginReq sysUserLoginReq) {
|
||||
return Results.ok(sysUserService.login(sysUserLoginReq));
|
||||
}
|
||||
|
||||
/**
|
||||
* 登出接口
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
@SaCheckLogin
|
||||
public Results<Void> logout() {
|
||||
sysUserService.logout();
|
||||
return Results.ok();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package top.baogutang.admin.dao.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author developer
|
||||
* @version 1.0
|
||||
* @description: 用户实体
|
||||
* @date 2021/11/9 14:33
|
||||
*/
|
||||
@Data
|
||||
@TableName("t_sys_user")
|
||||
public class SysUserEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 390648011771249275L;
|
||||
|
||||
|
||||
@TableId(value = "id", type = IdType.INPUT)
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "创建人")
|
||||
private Long creator;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
@ApiModelProperty(value = "更新人")
|
||||
private Long updater;
|
||||
|
||||
@ApiModelProperty(value = "更新时间")
|
||||
private Date updateTime;
|
||||
|
||||
@ApiModelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 删除标记,0是正常,1逻辑删除
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* email
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* mobile
|
||||
*/
|
||||
private String mobile;
|
||||
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 是否启用(0:启用,1:禁用)
|
||||
*/
|
||||
private Integer enableFlag;
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package top.baogutang.admin.dao.mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import top.baogutang.admin.dao.entity.SysUserEntity;
|
||||
|
||||
/**
|
||||
* @author developer
|
||||
* @version 1.0
|
||||
* @description: Mapper映射接口
|
||||
* @date 2021/11/9 14:34
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysUserMapper extends BaseMapper<SysUserEntity> {
|
||||
|
||||
|
||||
/**
|
||||
* 根据邮箱或手机号查询
|
||||
*
|
||||
* @param email 邮箱
|
||||
* @param mobile 手机号
|
||||
* @return 用户信息
|
||||
*/
|
||||
SysUserEntity selectByEmailOrMobile(@Param("email") String email, @Param("mobile") String mobile);
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package top.baogutang.admin.domain.req;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author n
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("用户登录req")
|
||||
public class SysUserLoginReq implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8305880247606732166L;
|
||||
|
||||
@ApiModelProperty(value = "邮箱")
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
private String email;
|
||||
|
||||
@ApiModelProperty(value = "密码")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package top.baogutang.admin.domain.req;
|
||||
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @description: 注册请求参数
|
||||
* @author: nikooh
|
||||
* @date: 2023/06/19 : 12:11
|
||||
*/
|
||||
@Data
|
||||
public class SysUserRegisterReq implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8279662824540042891L;
|
||||
|
||||
/**
|
||||
* 注册邮箱
|
||||
*/
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 注册手机号
|
||||
*/
|
||||
@NotBlank(message = "手机不能为空")
|
||||
private String mobile;
|
||||
|
||||
/**
|
||||
* 账户密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Length(min = 6, max = 10, message = "密码长度请设置在6~10位之间")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
private String captcha;
|
||||
|
||||
/**
|
||||
* 验证码key
|
||||
*/
|
||||
@NotBlank(message = "验证码key不能为空")
|
||||
private String checkKey;
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package top.baogutang.admin.domain.res;
|
||||
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author n
|
||||
* @date 2021/11/21
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("用户token信息")
|
||||
public class SysUserInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2577609323497270414L;
|
||||
|
||||
@ApiModelProperty(value = "token(加在请求头里)")
|
||||
private String accessToken;
|
||||
|
||||
private String tokenType;
|
||||
|
||||
@ApiModelProperty(value = "token失效后根据refresh_token刷新")
|
||||
private String refreshToken;
|
||||
|
||||
@ApiModelProperty(value = "用户ID")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "用户邮箱")
|
||||
private String email;
|
||||
|
||||
@ApiModelProperty(value = "用户手机号")
|
||||
private String mobile;
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package top.baogutang.admin.services;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2023/06/19 : 12:20
|
||||
*/
|
||||
public interface ICommonService {
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*
|
||||
* @param key 验证码key
|
||||
* @return 验证码
|
||||
*/
|
||||
String randomImage(String key);
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package top.baogutang.admin.services;
|
||||
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import top.baogutang.admin.domain.req.SysUserLoginReq;
|
||||
import top.baogutang.admin.domain.req.SysUserRegisterReq;
|
||||
import top.baogutang.admin.domain.res.SysUserInfo;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2023/06/19 : 12:13
|
||||
*/
|
||||
public interface ISysUserService {
|
||||
|
||||
/**
|
||||
* 后台系统用户注册
|
||||
*
|
||||
* @param sysUserRegisterReq 注册请求参数
|
||||
*/
|
||||
void register(SysUserRegisterReq sysUserRegisterReq);
|
||||
|
||||
/**
|
||||
* 登陆接口
|
||||
*
|
||||
* @param sysUserLoginReq 登陆请求参数
|
||||
* @return 登陆用户信息
|
||||
*/
|
||||
SaTokenInfo login(SysUserLoginReq sysUserLoginReq);
|
||||
|
||||
/**
|
||||
* 登出接口
|
||||
*/
|
||||
void logout();
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package top.baogutang.admin.services.impl;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.baogutang.admin.services.ICommonService;
|
||||
import top.baogutang.common.constants.ErrorCodeEnum;
|
||||
import top.baogutang.common.exceptions.BusinessException;
|
||||
import top.baogutang.common.utils.RandImageUtil;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static top.baogutang.common.constants.CacheConstant.RANDOM_IMAGE_CACHE_KEY;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2023/06/19 : 12:20
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class CommonServiceImpl implements ICommonService {
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private static final String BASE_CHECK_CODES = "qwertyuipkjhgfdsazxcvbnmQWERTYUPKJHGFDSAZXCVBNM23456789";
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Override
|
||||
public String randomImage(String key) {
|
||||
String captcha = RandomUtil.randomString(BASE_CHECK_CODES, 4);
|
||||
String cacheKey = String.format(RANDOM_IMAGE_CACHE_KEY, key);
|
||||
redisTemplate.opsForValue().set(cacheKey, captcha, 5, TimeUnit.MINUTES);
|
||||
try {
|
||||
return RandImageUtil.generate(captcha);
|
||||
} catch (IOException e) {
|
||||
log.error(">>>>>>>>>>获取图片验证码失败:{}<<<<<<<<<<", e.getMessage(), e);
|
||||
throw new BusinessException(ErrorCodeEnum.E_BIZ_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package top.baogutang.admin.services.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.baogutang.admin.dao.entity.SysUserEntity;
|
||||
import top.baogutang.admin.dao.mapper.SysUserMapper;
|
||||
import top.baogutang.admin.domain.req.SysUserLoginReq;
|
||||
import top.baogutang.admin.domain.req.SysUserRegisterReq;
|
||||
import top.baogutang.admin.services.ISysUserService;
|
||||
import top.baogutang.common.annotation.RequestLimit;
|
||||
import top.baogutang.common.constants.ErrorCodeEnum;
|
||||
import top.baogutang.common.exceptions.BusinessException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static top.baogutang.common.constants.CacheConstant.ENABLE;
|
||||
import static top.baogutang.common.constants.CacheConstant.RANDOM_IMAGE_CACHE_KEY;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2023/06/19 : 12:14
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserEntity> implements ISysUserService {
|
||||
|
||||
@Resource
|
||||
private SysUserMapper sysUserMapper;
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Resource
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
|
||||
@Override
|
||||
@RequestLimit
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void register(SysUserRegisterReq sysUserRegisterReq) {
|
||||
String cacheKey = String.format(RANDOM_IMAGE_CACHE_KEY, sysUserRegisterReq.getCheckKey());
|
||||
Object cacheCaptcha = redisTemplate.opsForValue().get(cacheKey);
|
||||
if (Objects.isNull(cacheCaptcha)) {
|
||||
throw new BusinessException(ErrorCodeEnum.CAPTCHA_NOT_EXISTS);
|
||||
}
|
||||
if (!sysUserRegisterReq.getCaptcha().equalsIgnoreCase((String) cacheCaptcha)) {
|
||||
throw new BusinessException(ErrorCodeEnum.CAPTCHA_INCORRECT);
|
||||
}
|
||||
// 查询邮箱手机号是否已占用
|
||||
SysUserEntity sysUser = baseMapper.selectByEmailOrMobile(sysUserRegisterReq.getEmail(), sysUserRegisterReq.getMobile());
|
||||
if (Objects.nonNull(sysUser)) {
|
||||
throw new BusinessException(ErrorCodeEnum.CURRENT_USER_EXISTS);
|
||||
}
|
||||
// 可以进行注册
|
||||
SysUserEntity userEntity = new SysUserEntity();
|
||||
userEntity.setId(IdWorker.getId());
|
||||
userEntity.setUsername(sysUserRegisterReq.getEmail());
|
||||
userEntity.setEmail(sysUserRegisterReq.getEmail());
|
||||
userEntity.setMobile(sysUserRegisterReq.getMobile());
|
||||
userEntity.setPassword(passwordEncoder.encode(sysUserRegisterReq.getPassword()));
|
||||
saveOrUpdate(userEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaTokenInfo login(SysUserLoginReq sysUserLoginReq) {
|
||||
String password = sysUserLoginReq.getPassword();
|
||||
SysUserEntity userEntity = this.findByEmail(sysUserLoginReq.getEmail());
|
||||
if (userEntity == null) {
|
||||
throw new BusinessException(ErrorCodeEnum.E_BIZ_DIGITAL_ART_USER_NOT_EXISTS);
|
||||
}
|
||||
boolean userVerify = passwordEncoder.matches(password, userEntity.getPassword());
|
||||
if (!userVerify) {
|
||||
throw new BusinessException(ErrorCodeEnum.PASSWORD_ERROR);
|
||||
}
|
||||
// Sa-Token
|
||||
StpUtil.login(userEntity.getId());
|
||||
return StpUtil.getTokenInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout() {
|
||||
StpUtil.logout();
|
||||
}
|
||||
|
||||
|
||||
private SysUserEntity findByEmail(String email) {
|
||||
Wrapper<SysUserEntity> wrapper = Wrappers.<SysUserEntity>query()
|
||||
.lambda()
|
||||
.eq(SysUserEntity::getEmail, email)
|
||||
.eq(SysUserEntity::getEnableFlag, ENABLE);
|
||||
return this.getOne(wrapper);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="top.baogutang.admin.dao.mapper.SysUserMapper">
|
||||
|
||||
<select id="selectByEmailOrMobile" resultType="top.baogutang.admin.dao.entity.SysUserEntity">
|
||||
SELECT id, email, mobile
|
||||
FROM t_sys_user
|
||||
WHERE 1 = 1
|
||||
AND (email = #{email,jdbcType=VARCHAR}
|
||||
OR mobile = #{mobile,jdbcType=VARCHAR})
|
||||
|
||||
|
||||
</select>
|
||||
</mapper>
|
||||
@ -134,6 +134,16 @@
|
||||
<artifactId>wxpusher-java-sdk</artifactId>
|
||||
<version>2.1.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.7.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -18,4 +18,14 @@ public class CacheConstant {
|
||||
* 微信消息推送获取临时二维码缓存key
|
||||
*/
|
||||
public static final String WX_MSG_PUSH_QR_CODE_PREFIX = "top:baogutang:wx:msg_push:%s:";
|
||||
|
||||
/**
|
||||
* 随机验证码图片key
|
||||
*/
|
||||
public static final String RANDOM_IMAGE_CACHE_KEY = "top:baogutang:random_image:%s:";
|
||||
|
||||
public static final String SYS_TOKEN = "top:baogutang:sys:user:token:";
|
||||
|
||||
|
||||
public static final Integer ENABLE = 0;
|
||||
}
|
||||
|
||||
@ -60,7 +60,15 @@ public enum ErrorCodeEnum {
|
||||
FORBIDDEN(403, "没有相关权限"),
|
||||
AUTHORIZATION_HEADER_IS_EMPTY(600, "请求头中的token为空"),
|
||||
|
||||
DATA_IS_NULL(81011033, "数据为空");;
|
||||
DATA_IS_NULL(81011033, "数据为空"),
|
||||
|
||||
CAPTCHA_NOT_EXISTS(81011034, "验证码不存在"),
|
||||
|
||||
CAPTCHA_INCORRECT(81011035, "验证码错误"),
|
||||
|
||||
CURRENT_USER_EXISTS(81011036, "当前用户已存在"),
|
||||
|
||||
;
|
||||
|
||||
private final int code;
|
||||
private final String msg;
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
package top.baogutang.common.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* @author developer
|
||||
* 采用MD5加密
|
||||
*/
|
||||
public class MD5Utils {
|
||||
/***
|
||||
* MD5加密 生成32位md5码
|
||||
* @param inStr 待加密字符串
|
||||
* @return 返回32位md5码
|
||||
*/
|
||||
public static String md5Encode(String inStr) {
|
||||
MessageDigest md5;
|
||||
try {
|
||||
md5 = MessageDigest.getInstance("MD5");
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
|
||||
byte[] byteArray = inStr.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] md5Bytes = md5.digest(byteArray);
|
||||
StringBuilder hexValue = new StringBuilder();
|
||||
for (byte md5Byte : md5Bytes) {
|
||||
int val = (md5Byte) & 0xff;
|
||||
if (val < 16) {
|
||||
hexValue.append("0");
|
||||
}
|
||||
hexValue.append(Integer.toHexString(val));
|
||||
}
|
||||
return hexValue.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
package top.baogutang.common.utils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 登录验证码工具类
|
||||
*/
|
||||
public class RandImageUtil {
|
||||
|
||||
/**
|
||||
* 定义图形大小
|
||||
*/
|
||||
private static final int width = 105;
|
||||
/**
|
||||
* 定义图形大小
|
||||
*/
|
||||
private static final int height = 35;
|
||||
|
||||
/**
|
||||
* 定义干扰线数量
|
||||
*/
|
||||
private static final int count = 200;
|
||||
|
||||
/**
|
||||
* 干扰线的长度=1.414*lineWidth
|
||||
*/
|
||||
private static final int lineWidth = 2;
|
||||
|
||||
/**
|
||||
* 图片格式
|
||||
*/
|
||||
private static final String IMG_FORMAT = "JPEG";
|
||||
|
||||
/**
|
||||
* base64 图片前缀
|
||||
*/
|
||||
private static final String BASE64_PRE = "data:image/jpg;base64,";
|
||||
|
||||
/**
|
||||
* 直接通过response 返回图片
|
||||
* @param response
|
||||
* @param resultCode
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void generate(HttpServletResponse response, String resultCode) throws IOException {
|
||||
BufferedImage image = getImageBuffer(resultCode);
|
||||
// 输出图象到页面
|
||||
ImageIO.write(image, IMG_FORMAT, response.getOutputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成base64字符串
|
||||
* @param resultCode
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String generate(String resultCode) throws IOException {
|
||||
BufferedImage image = getImageBuffer(resultCode);
|
||||
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
//写入流中
|
||||
ImageIO.write(image, IMG_FORMAT, byteStream);
|
||||
//转换成字节
|
||||
byte[] bytes = byteStream.toByteArray();
|
||||
//转换成base64串
|
||||
String base64 = Base64.getEncoder().encodeToString(bytes).trim();
|
||||
//删除 \r\n
|
||||
base64 = base64.replaceAll("\n", "").replaceAll("\r", "");
|
||||
|
||||
return BASE64_PRE+base64;
|
||||
}
|
||||
|
||||
private static BufferedImage getImageBuffer(String resultCode){
|
||||
// 在内存中创建图象
|
||||
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
// 获取图形上下文
|
||||
final Graphics2D graphics = (Graphics2D) image.getGraphics();
|
||||
// 设定背景颜色
|
||||
// ---1
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fillRect(0, 0, width, height);
|
||||
// 设定边框颜色
|
||||
// graphics.setColor(getRandColor(100, 200)); // ---2
|
||||
graphics.drawRect(0, 0, width - 1, height - 1);
|
||||
|
||||
final Random random = new Random();
|
||||
// 随机产生干扰线,使图象中的认证码不易被其它程序探测到
|
||||
for (int i = 0; i < count; i++) {
|
||||
// ---3
|
||||
graphics.setColor(getRandColor(150, 200));
|
||||
// 保证画在边框之内
|
||||
final int x = random.nextInt(width - lineWidth - 1) + 1;
|
||||
final int y = random.nextInt(height - lineWidth - 1) + 1;
|
||||
final int xl = random.nextInt(lineWidth);
|
||||
final int yl = random.nextInt(lineWidth);
|
||||
graphics.drawLine(x, y, x + xl, y + yl);
|
||||
}
|
||||
// 取随机产生的认证码
|
||||
for (int i = 0; i < resultCode.length(); i++) {
|
||||
// 将认证码显示到图象中,调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
|
||||
// graphics.setColor(new Color(20 + random.nextInt(130), 20 + random
|
||||
// .nextInt(130), 20 + random.nextInt(130)));
|
||||
// 设置字体颜色
|
||||
graphics.setColor(Color.BLACK);
|
||||
// 设置字体样式
|
||||
// graphics.setFont(new Font("Arial Black", Font.ITALIC, 18));
|
||||
graphics.setFont(new Font("Times New Roman", Font.BOLD, 24));
|
||||
// 设置字符,字符间距,上边距
|
||||
graphics.drawString(String.valueOf(resultCode.charAt(i)), (23 * i) + 8, 26);
|
||||
}
|
||||
// 图象生效
|
||||
graphics.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
private static Color getRandColor(int fc, int bc) {
|
||||
final Random random = new Random();
|
||||
if (fc > 255) {
|
||||
fc = 255;
|
||||
}
|
||||
if (bc > 255) {
|
||||
bc = 255;
|
||||
}
|
||||
|
||||
final int r = fc + random.nextInt(bc - fc);
|
||||
final int g = fc + random.nextInt(bc - fc);
|
||||
final int b = fc + random.nextInt(bc - fc);
|
||||
|
||||
return new Color(r, g, b);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user