This commit is contained in:
JiyangTang 2023-06-21 14:19:18 +08:00
parent d7c391f644
commit 127c7ed8ab
22 changed files with 867 additions and 1 deletions

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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);
};
}
}

View File

@ -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());
}
}

View File

@ -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";
}

View File

@ -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));
}
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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 = "密码长度请设置在610位之间")
private String password;
/**
* 验证码
*/
@NotBlank(message = "验证码不能为空")
private String captcha;
/**
* 验证码key
*/
@NotBlank(message = "验证码key不能为空")
private String checkKey;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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);
}
}