This commit is contained in:
刘玉霞 2024-06-19 16:32:21 +08:00
commit 43c6a2c92f
31 changed files with 1023 additions and 763 deletions

0
Jenkinsfile vendored Normal file
View File

View File

@ -4,17 +4,16 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.mesmile</groupId>
<groupId>com.das</groupId>
<artifactId>das</artifactId>
<version>1.0.0-release</version>
<name>admin</name>
<description>base admin project for Spring Boot</description>
<name>das</name>
<description>base das project for Spring Boot</description>
<properties>
<admin.project.id>admin-boot</admin.project.id>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<java.version>17</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.plugin.version>3.8.1</maven.plugin.version>
@ -23,32 +22,17 @@
<bitwalker.version>1.21</bitwalker.version>
<kaptcha.version>2.3.2</kaptcha.version>
<fastjson.version>1.2.83</fastjson.version>
<oshi.version>6.1.2</oshi.version>
<jna.version>5.10.0</jna.version>
<commons.io.version>2.11.0</commons.io.version>
<commons.fileupload.version>1.4</commons.fileupload.version>
<commons.collections.version>3.2.2</commons.collections.version>
<poi.version>4.1.2</poi.version>
<jwt.version>0.9.1</jwt.version>
<spring.boot.version>2.7.6</spring.boot.version>
<mybatis.plus.spring.boot>3.5.2</mybatis.plus.spring.boot>
<hutool.version>5.8.4</hutool.version>
<hutool.version>5.8.18</hutool.version>
<!--注意这里 lombok 1.18.10 和 mapstruct 1.3.0.Final 匹配-->
<!--注意这里 lombok 1.18.20 和 mapstruct 1.5.2.Final 匹配-->
<org.mapstruct.version>1.5.2.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.20</org.projectlombok.version>
<swagger.knife4j.version>2.0.9</swagger.knife4j.version>
<google.guava.version>29.0-jre</google.guava.version>
<velocity.engine.core.version>2.3</velocity.engine.core.version>
<apache.commons.text>1.9</apache.commons.text>
<redisson.version>3.17.6</redisson.version>
<minio.verion>8.1.0</minio.verion>
<xxl-job.version>2.3.1</xxl-job.version>
<easyexcel.version>3.1.1</easyexcel.version>
<apm-toolkit-trace.version>8.11.0</apm-toolkit-trace.version>
<just-auth.version>1.16.3</just-auth.version>
<org.apache.httpclient>4.5.12</org.apache.httpclient>
<aliyun.dysmsapi.version>2.0.16</aliyun.dysmsapi.version>
<postgresql.version>42.2.24</postgresql.version>
<sa.version>1.34.0</sa.version>
</properties>
<!--锁定版本-->
@ -94,45 +78,25 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.24</version>
<version>${postgresql.version}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>1.34.0</version>
<version>${sa.version}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.34.0</version>
<version>${sa.version}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis</artifactId>
<version>1.34.0</version>
</dependency>
<!--第三方认证包-->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>${just-auth.version}</version>
<exclusions>
<exclusion>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
</exclusions>
</dependency>
<!--just-auth中的http工具-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${org.apache.httpclient}</version>
<version>${sa.version}</version>
</dependency>
<!-- 验证码 -->
@ -149,22 +113,6 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<!-- IntelliJ does not pick up the processor if it is not in the dependencies.
There is already an open issue for IntelliJ see https://youtrack.jetbrains.com/issue/IDEA-150621
-->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@ -192,13 +140,6 @@
<version>${druid.version}</version>
</dependency>
<!--AliSms-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>${aliyun.dysmsapi.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
@ -225,20 +166,6 @@
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--Swagger-->
<!-- <dependency>-->
<!-- <groupId>com.github.xiaoymin</groupId>-->
<!-- <artifactId>knife4j-micro-spring-boot-starter</artifactId>-->
<!-- <version>${swagger.knife4j.version}</version>-->
<!-- </dependency>-->
<!--swagger-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${swagger.knife4j.version}</version>
</dependency>
<!--google工具类-->
<dependency>
<groupId>com.google.guava</groupId>
@ -246,20 +173,6 @@
<version>${google.guava.version}</version>
</dependency>
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis.plus.spring.boot}</version>
</dependency>
<!--模板生成器 velocity-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.engine.core.version}</version>
</dependency>
<!--redis 操作-->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -271,62 +184,15 @@
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- redisson 实现分布式锁 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
<!--aop切面-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--minio 客户端-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.verion}</version>
</dependency>
<!--分布式定时任务-->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job.version}</version>
</dependency>
<!--easy-excel 表格导入导出-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
</dependency>
<!--添加水印-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>1.4</version>
</dependency>
<!--Skywalking工具类, 版本号与服务端一致-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>${apm-toolkit-trace.version}</version>
</dependency>
</dependencies>
<build>
<finalName>${admin.project.id}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>

View File

@ -1,6 +1,5 @@
package com.das.common.captcha;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -17,10 +16,8 @@ public class CaptchaVO {
this.img = img;
}
@ApiModelProperty("唯一值")
private String key;
@ApiModelProperty("图像的base64")
private String img;
}

View File

@ -1,6 +1,7 @@
package com.das.common.config;
import com.das.common.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@ -8,12 +9,16 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public TokenInterceptor getTokenInterceptor() {
return new TokenInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 将Token拦截器添加到注册表中
registry.addInterceptor(new TokenInterceptor())
registry.addInterceptor(getTokenInterceptor())
// 可以指定拦截哪些路径例如"/api/**"表示所有以/api开头的路径
// .addPathPatterns("/api/**")
// .addPathPatterns("/api/**");
// 排除不需要拦截的路径
.excludePathPatterns("/api/**");
}

View File

@ -14,7 +14,7 @@ import org.springframework.context.annotation.Configuration;
* @author chenhaojie
* @Description
*/
@MapperScan(basePackages = {"cn.mesmile.**.mapper"})
@MapperScan(basePackages = {"com.das.**.mapper"})
@Configuration
public class MybatisPlusConfig {

View File

@ -9,7 +9,7 @@ public interface AdminConstant {
/**
* 基础包路径
*/
String BASE_PACKAGE = "cn.mesmile";
String BASE_PACKAGE = "com.das";
/**
* redis分布式所的默认前缀

View File

@ -6,12 +6,12 @@ import com.baomidou.mybatisplus.annotation.TableLogic;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Date;
/**
* @author chenhaojie
@ -20,37 +20,34 @@ import java.time.LocalDateTime;
@Data
public class BaseEntity implements Serializable {
/**
* 创建人
*/
@JsonSerialize(using = ToStringSerializer.class)
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty("创建人")
private Long createBy;
@JsonSerialize(using = ToStringSerializer.class)
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty("创建部门")
private Long createDept;
private Long createdBy;
/**
* 创建时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty("创建时间")
private LocalDateTime createTime;
private Date createdTime;
/**
* 更新人
*/
@JsonSerialize(using = ToStringSerializer.class)
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty("更新人")
private Long updateBy;
private Long updatedBy;
/**
* 更新时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty("更新时间")
private LocalDateTime updateTime;
private Date updatedTime;
@ApiModelProperty("删除标志0代表存在 1代表删除")
@TableLogic
private String deleted;
@ApiModelProperty("业务状态0正常 1停用")
private String status;
}

View File

@ -1,26 +1,49 @@
package com.das.common.interceptor;
import com.das.common.utils.AESUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TokenInterceptor implements HandlerInterceptor {
@Resource
private AESUtil aesUtil;
private static String key;
@Value("${aesKey}")
public void setKey(String key){
TokenInterceptor.key = key;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从请求头中获取Token
String token = request.getHeader("Authorization");
// 验证Token有效性这里只是一个示例逻辑实际应根据你的认证方式实现
if (token != null && validateToken(token)) {
// Token有效继续处理请求
return true;
} else {
// Token无效可以设置响应状态并返回错误信息
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized");
return false; // 阻止请求继续
}
String param = request.getParameter("param");
// 从请求头中获取Token
// String token = request.getHeader("token");
// 从请求头中获取向量IV
String iv = request.getHeader("v");
// 先解密token
// AESUtil.decrypt(token, iv);
// 解密参数
aesUtil.decrypt(key, param, iv);
System.out.println(aesUtil.decrypt(key ,param, iv));
request.setAttribute("param", aesUtil.decrypt(key,param, iv));
return true;
// if (token != null && validateToken(token)) {
// // Token有效继续处理请求
// return true;
// } else {
// // Token无效可以设置响应状态并返回错误信息
// response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// response.getWriter().write("Unauthorized");
// return false; // 阻止请求继续
// }
}
// 示例Token验证方法需要根据实际情况实现

View File

@ -1,7 +1,5 @@
package com.das.common.result;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.lang.Nullable;
import java.io.Serializable;
@ -11,7 +9,6 @@ import java.util.Optional;
* @author chenhaojie
* @Description 公共返回信息
*/
@ApiModel("统一响应结果")
public class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
@ -19,25 +16,21 @@ public class R<T> implements Serializable {
/**
* 状态码
*/
@ApiModelProperty("状态码")
private int code;
/**
* 是否成功
*/
@ApiModelProperty("是否成功")
private boolean success;
/**
* 承载数据
*/
@ApiModelProperty("承载数据")
private T data;
/**
* 返回消息
*/
@ApiModelProperty("返回消息")
private String msg;
private R(IResultCode resultCode) {
@ -89,6 +82,10 @@ public class R<T> implements Serializable {
return new R(ResultCode.SUCCESS, msg);
}
public static <T> R<T> success(T data) {
return new R(ResultCode.SUCCESS, data,"操作成功");
}
public static <T> R<T> success(IResultCode resultCode) {
return new R(resultCode);
}

View File

@ -1,38 +0,0 @@
package com.das.common.support;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.stream.Collectors;
/**
* 返回视图层所需的字段
* @author chenhaojie
* @Description
* @param <E>
* @param <V>
*/
public abstract class BaseEntityWrapper<E, V> {
public BaseEntityWrapper() {
}
/**
* 将实体类中部门字段转换为视图层可看懂的字段例如 性别 0 -> 1 ->
* @param entity
* @return
*/
public abstract V entityVO(E entity);
public List<V> listVO(List<E> list) {
return (List)list.stream().map(this::entityVO).collect(Collectors.toList());
}
public IPage<V> pageVO(IPage<E> pages) {
List<V> records = this.listVO(pages.getRecords());
IPage<V> pageVo = new Page(pages.getCurrent(), pages.getSize(), pages.getTotal());
pageVo.setRecords(records);
return pageVo;
}
}

View File

@ -0,0 +1,39 @@
package com.das.common.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import java.nio.charset.Charset;
/**
* @author xxx
* @date 2020-09-16 11:17
**/
@Component
public class AESUtil {
/**
* AES加密
* @param content 待加密的内容
* @return 加密后的Base64字符串
*/
// public String encrypt(String content, String iv) {
// AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, key.getBytes(), iv.getBytes());
// return Base64.encode(aes.encrypt(content, Charset.forName("UTF-8")));
// }
/**
* AES解密
* @param encryptStr 已加密的Base64字符串
* @return 解密后的内容
*/
public static String decrypt(String key, String encryptStr, String iv) {
AES aes = new AES(Mode.CBC, Padding.ZeroPadding, key.getBytes(), iv.getBytes());
return aes.decryptStr(Base64.decode(encryptStr), Charset.forName("UTF-8"));
}
}

View File

@ -1,51 +0,0 @@
package com.das.common.utils;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* @author chenhaojie
* @Description 自定义工具类
*/
@Slf4j
public class FunctionUtil {
/**
* 将英文逗号分割的字符串转换成 List
* @param string 字符串
* @return 结果
*/
public static List<Long> strToLongList(String string) {
return strToList(string, StringPool.COMMA);
}
/**
* 将某个字符串分割的字符串转换成List
* @param string 字符串
* @param split 分隔符
* @return 结果
*/
public static List<Long> strToList(String string,String split){
if (string == null || split == null){
return new ArrayList<>();
}
String[] resultArray = string.split(split);
List<Long> result = new ArrayList<>(resultArray.length);
for (String str : resultArray) {
if (NumberUtil.isNumber(str)){
try {
long number = Long.parseLong(str);
result.add(number);
}catch (Exception e){
log.error("解析数据异常", e);
}
}
}
return result;
}
}

View File

@ -1,229 +0,0 @@
package com.das.common.utils;
import cn.hutool.crypto.digest.MD5;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @author chenhaojie
* @Description
*/
public class IpUtil {
/**
* 获取客户端IP
*
* @param request 请求对象
* @return IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String s = new MD5().digestHex("");
if (request == null) {
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
}
/**
* 检查是否为内部IP地址
*
* @param ip IP地址
* @return 结果
*/
public static boolean internalIp(String ip) {
byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip);
}
/**
* 检查是否为内部IP地址
*
* @param addr byte地址
* @return 结果
*/
private static boolean internalIp(byte[] addr) {
if (addr == null || addr.length < 2) {
return true;
}
final byte b0 = addr[0];
final byte b1 = addr[1];
// 10.x.x.x/8
final byte SECTION_1 = 0x0A;
// 172.16.x.x/12
final byte SECTION_2 = (byte) 0xAC;
final byte SECTION_3 = (byte) 0x10;
final byte SECTION_4 = (byte) 0x1F;
// 192.168.x.x/16
final byte SECTION_5 = (byte) 0xC0;
final byte SECTION_6 = (byte) 0xA8;
switch (b0) {
case SECTION_1:
return true;
case SECTION_2:
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
return true;
}
case SECTION_5:
switch (b1) {
case SECTION_6:
return true;
}
default:
return false;
}
}
/**
* 将IPv4地址转换成字节
*
* @param text IPv4地址
* @return byte 字节
*/
public static byte[] textToNumericFormatV4(String text) {
if (text.length() == 0) {
return null;
}
byte[] bytes = new byte[4];
String[] elements = text.split("\\.", -1);
try {
long l;
int i;
switch (elements.length) {
case 1:
l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L)) {
return null;
}
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 2:
l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L)) {
return null;
}
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 3:
for (i = 0; i < 2; ++i) {
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L)) {
return null;
}
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 4:
for (i = 0; i < 4; ++i) {
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
break;
default:
return null;
}
} catch (NumberFormatException e) {
return null;
}
return bytes;
}
/**
* 获取IP地址
*
* @return 本地IP地址
*/
public static String getHostIp() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
}
return "127.0.0.1";
}
/**
* 获取主机名
*
* @return 本地主机名
*/
public static String getHostName() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
}
return "未知";
}
/**
* 从多级反向代理中获得第一个非unknown IP地址
*
* @param ip 获得的IP地址
* @return 第一个非unknown IP地址
*/
public static String getMultistageReverseProxyIp(String ip) {
// 多级反向代理检测
if (ip != null && ip.indexOf(",") > 0) {
final String[] ips = ip.trim().split(",");
for (String subIp : ips) {
if (false == isUnknown(subIp)) {
ip = subIp;
break;
}
}
}
return ip;
}
/**
* 检测给定字符串是否为未知多用于检测HTTP请求相关
*
* @param checkString 被检测的字符串
* @return 是否未知
*/
public static boolean isUnknown(String checkString) {
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
}
}

View File

@ -0,0 +1,109 @@
package com.das.common.utils;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* JSON 工具类
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtils {
private static ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
public static ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}
public static String toJsonString(Object object) {
if (ObjectUtil.isNull(object)) {
return null;
}
try {
return OBJECT_MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
if (ArrayUtil.isEmpty(bytes)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(bytes, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Dict parseMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class));
} catch (MismatchedInputException e) {
// 类型不匹配说明不是json
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static List<Dict> parseArrayMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> List<T> parseArray(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return new ArrayList<>();
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,67 +0,0 @@
package com.das.common.utils;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.i18n.LocaleContextHolder;
/**
* @author chenhaojie
* @Description
*/
public class ResourceI18nUtil {
public static MessageSource getMessageSource() {
return SpringUtil.getBean(MessageSource.class);
}
/**
* 通过定义key 获取value
* @param key key
* @return value
*/
public static String getValueByKey(String key){
return getMessageSource().getMessage(key, null, LocaleContextHolder.getLocale());
}
/**
* 通过定义key 获取value
* @param key key
* @param args 填补参数
* @return value
*/
public static String getValueByKey(String key, Object... args){
return getMessageSource().getMessage(key, args, LocaleContextHolder.getLocale());
}
/**
* 通过定义key 获取value
* @param key key
* @param defaultMessage 默认消息
* @param args 填补参数
* @return value
*/
public static String getValueDefaultByKey(String key,String defaultMessage,Object... args){
return getMessageSource().getMessage(key, args, defaultMessage, LocaleContextHolder.getLocale());
}
/**
* 通过定义key 获取value
* @param key key
* @param defaultMessage 默认消息
* @return value
*/
public static String getValueDefaultByKey(String key,String defaultMessage){
return getMessageSource().getMessage(key, null, defaultMessage,LocaleContextHolder.getLocale());
}
/**
* 通过定义key 获取value
* @param resolvable resolvable
* @return value
*/
public static String getValueByResolvable(MessageSourceResolvable resolvable){
return getMessageSource().getMessage(resolvable, LocaleContextHolder.getLocale());
}
}

View File

@ -0,0 +1,69 @@
package com.das.common.utils;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
/**
* spring工具类
*/
public final class SpringUtils extends SpringUtil {
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype
* 如果与给定名字相应的bean定义没有被找到将会抛出一个异常NoSuchBeanDefinitionException
*
* @param name
* @return boolean
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名则返回这些别名
*
* @param name
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getAliases(name);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker) {
return (T) AopContext.currentProxy();
}
/**
* 获取spring上下文
*/
public static ApplicationContext context() {
return getApplicationContext();
}
}

View File

@ -0,0 +1,346 @@
package com.das.common.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.util.AntPathMatcher;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 字符串工具类
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StringUtils extends org.apache.commons.lang3.StringUtils {
public static final String SEPARATOR = ",";
/**
* 获取参数不为空值
*
* @param str defaultValue 要判断的value
* @return value 返回值
*/
public static String blankToDefault(String str, String defaultValue) {
return StrUtil.blankToDefault(str, defaultValue);
}
/**
* * 判断一个字符串是否为空串
*
* @param str String
* @return true为空 false非空
*/
public static boolean isEmpty(String str) {
return StrUtil.isEmpty(str);
}
/**
* * 判断一个字符串是否为非空串
*
* @param str String
* @return true非空串 false空串
*/
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
/**
* 去空格
*/
public static String trim(String str) {
return StrUtil.trim(str);
}
/**
* 截取字符串
*
* @param str 字符串
* @param start 开始
* @return 结果
*/
public static String substring(final String str, int start) {
return substring(str, start, str.length());
}
/**
* 截取字符串
*
* @param str 字符串
* @param start 开始
* @param end 结束
* @return 结果
*/
public static String substring(final String str, int start, int end) {
return StrUtil.sub(str, start, end);
}
/**
* 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
* 如果想输出 {} 使用 \\转义 { 即可如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
* <br>
* 通常使用format("this is {} for {}", "a", "b") -> this is a for b<br>
* 转义{} format("this is \\{} for {}", "a", "b") -> this is {} for a<br>
* 转义\ format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
*
* @param template 文本模板被替换的部分用 {} 表示
* @param params 参数值
* @return 格式化后的文本
*/
public static String format(String template, Object... params) {
return StrUtil.format(template, params);
}
/**
* 是否为http(s)://开头
*
* @param link 链接
* @return 结果
*/
public static boolean ishttp(String link) {
return Validator.isUrl(link);
}
/**
* 字符串转set
*
* @param str 字符串
* @param sep 分隔符
* @return set集合
*/
public static Set<String> str2Set(String str, String sep) {
return new HashSet<>(str2List(str, sep, true, false));
}
/**
* 字符串转list
*
* @param str 字符串
* @param sep 分隔符
* @param filterBlank 过滤纯空白
* @param trim 去掉首尾空白
* @return list集合
*/
public static List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
List<String> list = new ArrayList<>();
if (isEmpty(str)) {
return list;
}
// 过滤空白字符串
if (filterBlank && isBlank(str)) {
return list;
}
String[] split = str.split(sep);
for (String string : split) {
if (filterBlank && isBlank(string)) {
continue;
}
if (trim) {
string = trim(string);
}
list.add(string);
}
return list;
}
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
*
* @param cs 指定字符串
* @param searchCharSequences 需要检查的字符串数组
* @return 是否包含任意一个字符串
*/
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences);
}
/**
* 驼峰转下划线命名
*/
public static String toUnderScoreCase(String str) {
return StrUtil.toUnderlineCase(str);
}
/**
* 是否包含字符串
*
* @param str 验证字符串
* @param strs 字符串组
* @return 包含返回true
*/
public static boolean inStringIgnoreCase(String str, String... strs) {
return StrUtil.equalsAnyIgnoreCase(str, strs);
}
/**
* 将下划线大写方式命名的字符串转换为驼峰式如果转换前的下划线大写方式命名的字符串为空则返回空字符串 例如HELLO_WORLD->HelloWorld
*
* @param name 转换前的下划线大写方式命名的字符串
* @return 转换后的驼峰式命名的字符串
*/
public static String convertToCamelCase(String name) {
return StrUtil.upperFirst(StrUtil.toCamelCase(name));
}
/**
* 驼峰式命名法 例如user_name->userName
*/
public static String toCamelCase(String s) {
return StrUtil.toCamelCase(s);
}
/**
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
*
* @param str 指定字符串
* @param strs 需要检查的字符串数组
* @return 是否匹配
*/
public static boolean matches(String str, List<String> strs) {
if (isEmpty(str) || CollUtil.isEmpty(strs)) {
return false;
}
for (String pattern : strs) {
if (isMatch(pattern, str)) {
return true;
}
}
return false;
}
/**
* 判断url是否与规则配置:
* ? 表示单个字符;
* * 表示一层路径内的任意字符串不可跨层级;
* ** 表示任意层路径;
*
* @param pattern 匹配规则
* @param url 需要匹配的url
*/
public static boolean isMatch(String pattern, String url) {
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
}
/**
* 数字左边补齐0使之达到指定长度注意如果数字转换为字符串后长度大于size则只保留 最后size个字符
*
* @param num 数字对象
* @param size 字符串指定长度
* @return 返回数字的字符串格式该字符串为指定长度
*/
public static String padl(final Number num, final int size) {
return padl(num.toString(), size, '0');
}
/**
* 字符串左补齐如果原始字符串s长度大于size则只保留最后size个字符
*
* @param s 原始字符串
* @param size 字符串指定长度
* @param c 用于补齐的字符
* @return 返回指定长度的字符串由原字符串左补齐或截取得到
*/
public static String padl(final String s, final int size, final char c) {
final StringBuilder sb = new StringBuilder(size);
if (s != null) {
final int len = s.length();
if (s.length() <= size) {
for (int i = size - len; i > 0; i--) {
sb.append(c);
}
sb.append(s);
} else {
return s.substring(len - size, len);
}
} else {
for (int i = size; i > 0; i--) {
sb.append(c);
}
}
return sb.toString();
}
/**
* 切分字符串(分隔符默认逗号)
*
* @param str 被切分的字符串
* @return 分割后的数据列表
*/
public static List<String> splitList(String str) {
return splitTo(str, Convert::toStr);
}
/**
* 切分字符串
*
* @param str 被切分的字符串
* @param separator 分隔符
* @return 分割后的数据列表
*/
public static List<String> splitList(String str, String separator) {
return splitTo(str, separator, Convert::toStr);
}
/**
* 切分字符串自定义转换(分隔符默认逗号)
*
* @param str 被切分的字符串
* @param mapper 自定义转换
* @return 分割后的数据列表
*/
public static <T> List<T> splitTo(String str, Function<? super Object, T> mapper) {
return splitTo(str, SEPARATOR, mapper);
}
/**
* 切分字符串自定义转换
*
* @param str 被切分的字符串
* @param separator 分隔符
* @param mapper 自定义转换
* @return 分割后的数据列表
*/
public static <T> List<T> splitTo(String str, String separator, Function<? super Object, T> mapper) {
if (isBlank(str)) {
return new ArrayList<>(0);
}
return StrUtil.split(str, separator)
.stream()
.filter(Objects::nonNull)
.map(mapper)
.collect(Collectors.toList());
}
/*
* 首字母变大写
*
* */
public static String capitalizeFirstLetter(String str) {
char[] chars = str.toCharArray();
chars[0] = toUpperCase(chars[0]);
return String.valueOf(chars);
}
/*
* 字母变大写
*
* */
public static char toUpperCase(char c) {
if (97 <= c && c <= 122) {
c ^= 32;
}
return c;
}
}

View File

@ -0,0 +1,56 @@
package com.das.modules.auth.controller;
import cn.hutool.core.util.IdUtil;
import com.das.common.captcha.CaptchaProperties;
import com.das.common.captcha.CaptchaUtil;
import com.das.common.captcha.CaptchaVO;
import com.das.common.result.R;
import com.das.common.utils.AdminRedisTemplate;
import com.das.modules.auth.domain.LoginUserDetails;
import com.das.modules.auth.domain.vo.LoginVO;
import com.das.modules.auth.service.ILoginService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.code.kaptcha.Producer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
/**
* @author chenhaojie
* @Description 获取验证码
*/
@Slf4j
@RequestMapping("/api")
@RestController
public class CaptchaImageController {
@Resource
private Producer producer;
@Resource
private CaptchaProperties captchaProperties;
@Value("${aesKey}")
String key;
@Resource
private AdminRedisTemplate adminRedisTemplate;
/**
* 获取验证码
*/
@PostMapping("/captchaImage")
public R<CaptchaVO> getCaptcha() {
String uuid = IdUtil.fastSimpleUUID();
String imageBase64Str = CaptchaUtil.getImageBase64Str(producer, adminRedisTemplate, captchaProperties, uuid);
CaptchaVO captchaVO = new CaptchaVO(uuid, imageBase64Str);
return R.success(captchaVO);
}
}

View File

@ -1,56 +1,63 @@
package com.das.modules.auth.controller;
import cn.hutool.core.util.IdUtil;
import com.das.common.captcha.CaptchaProperties;
import com.das.common.captcha.CaptchaUtil;
import com.das.common.captcha.CaptchaVO;
import com.das.common.result.R;
import com.das.common.utils.AdminRedisTemplate;
import com.das.modules.auth.domain.request.LoginRequest;
import com.das.modules.auth.domain.LoginUserDetails;
import com.das.modules.auth.domain.vo.LoginVO;
import com.das.modules.auth.service.ILoginService;
import com.google.code.kaptcha.Producer;
import io.swagger.annotations.ApiOperation;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
/**
* @author chenhaojie
* @Description
* @Description 登录控制层
*/
@Slf4j
@RequestMapping("/api")
@RequestMapping("/api/auth")
@RestController
public class LoginController {
@Resource
private Producer producer;
@Resource
private CaptchaProperties captchaProperties;
@Resource
private ILoginService loginService;
@Value("${aesKey}")
String key;
@Resource
private AdminRedisTemplate adminRedisTemplate;
/**
* 限制频率在 5 秒钟 3次
* 登录接口
* @return 退出结果提示信息
*/
@ApiOperation("获取验证码")
@PostMapping("/captchaImage")
public R<CaptchaVO> getCaptcha() {
String uuid = IdUtil.fastSimpleUUID();
String imageBase64Str = CaptchaUtil.getImageBase64Str(producer, adminRedisTemplate, captchaProperties, uuid);
CaptchaVO captchaVO = new CaptchaVO(uuid, imageBase64Str);
return R.data(captchaVO);
@PostMapping("/login")
public R<LoginUserDetails> login(@RequestParam String param, HttpServletRequest request, HttpServletResponse response) throws JsonProcessingException {
LoginUserDetails loginInfo = loginService.login(param,request, response);
return R.success(loginInfo);
}
@PostMapping("/auth/login")
@ApiOperation("登录接口")
public R<?> login(@RequestBody @Validated LoginRequest loginRequest) {
String a = loginService.login(loginRequest);
return R.data(a);
/**
* 退出登录接口
* @return 退出结果提示信息
*/
@PostMapping("/logout")
public R<?> logout(HttpServletRequest request) {
if (loginService.logout(request)) {
return R.success("退出成功");
} else {
return R.fail("退出失败");
}
}
@PostMapping("/refreshToken")
public R<LoginVO> refreshToken(@NotEmpty(message = "刷新token不允许为空") @RequestParam("refreshToken")String refreshToken){
LoginVO loginVO = loginService.refreshToken(refreshToken);
return R.data(loginVO);
}

View File

@ -0,0 +1,27 @@
package com.das.modules.auth.domain;
import com.das.modules.auth.domain.vo.SysUserVo;
import lombok.Data;
import java.io.Serializable;
/**
* @author zb
* @Description
*/
@Data
public class LoginUserDetails implements Serializable {
private static final long serialVersionUID = 1L;
private SysUserVo sysUser;
private String token;
public LoginUserDetails() {
}
}

View File

@ -1,31 +0,0 @@
package com.das.modules.auth.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author chenhaojie
* @Description
*/
@Data
public class SysUserDTO {
@ApiModelProperty("用户账号")
private String username;
@ApiModelProperty("用户昵称")
private String nickName;
@ApiModelProperty("真实姓名")
private String realName;
@ApiModelProperty("用户类型00系统用户")
private String userType;
@ApiModelProperty("用户邮箱")
private String email;
@ApiModelProperty("手机号码")
private String mobile;
}

View File

@ -1,6 +1,5 @@
package com.das.modules.auth.domain.request;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@ -12,19 +11,35 @@ import javax.validation.constraints.NotBlank;
@Data
public class LoginRequest {
/**
* * 用户账号
*/
@NotBlank(message = "用户名不允许为空")
@ApiModelProperty("用户账号")
private String username;
/**
* 密码
*/
@NotBlank(message = "密码不允许为空")
@ApiModelProperty("密码")
private String password;
@NotBlank(message = "验证码不允许为空")
@ApiModelProperty("验证码")
private String captcha;
/**
* 是否记住
*/
@NotBlank(message = "是否记住")
private boolean keep;
/**
* 验证码
*/
@NotBlank(message = "验证码不允许为空")
private String code;
/**
* 验证码唯一标识
*/
@NotBlank(message = "验证码唯一标识不允许为空")
@ApiModelProperty("验证码唯一标识")
private String uuid;
}

View File

@ -1,6 +1,5 @@
package com.das.modules.auth.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@ -14,13 +13,19 @@ public class LoginVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("accessToken")
/**
*
*/
private String accessToken;
@ApiModelProperty("刷新token")
/**
*刷新token
*/
private String refreshToken;
@ApiModelProperty("过期时间,单位秒")
/**
*过期时间
*/
private Integer expire;
public LoginVO(){}

View File

@ -0,0 +1,47 @@
package com.das.modules.auth.domain.vo;
import lombok.Data;
import java.io.Serializable;
/**
* 账号信息
*
* @author guchengwei
*/
@Data
public class SysUserVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 账号ID
*/
private Long id;
/**
* 账号名
*/
private String account;
/**
* 账号本地名
*/
private String userName;
/**
* 手机号
*/
private String phone;
/**
* E-Mail地址
*/
private String email;
/**
* 所属机构
*/
private Long orgId;
}

View File

@ -0,0 +1,27 @@
package com.das.modules.auth.domain.vo;
import lombok.Data;
/**
* 令牌实体
* @author Administrator
*/
@Data
public class TokenVo {
/**
* 令牌
*/
private String token;
/**
* 令牌超时时间单位()
*/
public long tokenTimeout;
/**
* 令牌活动超时间隔单位()
*/
public long tokenActivityTimeout;
/**
* 账号ID
*/
public long accountId;
}

View File

@ -1,19 +1,15 @@
package com.das.modules.auth.entity;
import com.das.common.constant.BaseEntity;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.das.common.constant.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
import java.util.Date;
/**
* <p>
@ -24,54 +20,57 @@ import java.time.LocalDate;
*/
@Data
@TableName("sys_user")
@ApiModel(value = "SysUser对象", description = "用户信息")
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class SysUser extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("用户ID")
/**
* 用户ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@ApiModelProperty("用户账号")
private String username;
/**
* 登录账号
*/
private String account;
@ApiModelProperty("用户昵称")
private String nickName;
@ApiModelProperty("真实姓名")
private String realName;
@ApiModelProperty("用户类型00系统用户")
private String userType;
@ApiModelProperty("用户邮箱")
private String email;
@ApiModelProperty("手机号码")
private String mobile;
@ApiModelProperty("用户性别(1男 2女 3未知")
private String sex;
@ApiModelProperty("头像地址")
private String avatar;
// @JsonDeserialize(using = LocalDateDeserializer.class)
// @JsonSerialize(using = LocalDateSerializer.class)
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
@ApiModelProperty("出生年月")
private LocalDate birthday;
@JSONField(serialize = false)
@ApiModelProperty("密码")
/**
* 加密后的密码
*/
private String password;
@ApiModelProperty("备注")
private String remark;
/**
* 职员名称
*/
private String userName;
/**
* 职员邮箱
*/
private String email;
/**
* 职员联系电话
*/
private String phone;
/**
* 所属机构id
*/
private Long orgId;
/**
* 最近一次登录时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastLogin;
/**
* 乐观锁
*/
private int revision;
}

View File

@ -1,8 +1,13 @@
package com.das.modules.auth.service;
import com.das.modules.auth.domain.request.LoginRequest;
import com.das.common.result.R;
import com.das.modules.auth.domain.LoginUserDetails;
import com.das.modules.auth.domain.vo.LoginVO;
import com.fasterxml.jackson.core.JsonProcessingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author chenhaojie
@ -12,16 +17,17 @@ public interface ILoginService {
/**
* 登录接口
* @param loginRequest 登录请求
*
* @param data 登录请求
* @return 登录成功
*/
String login(LoginRequest loginRequest);
LoginUserDetails login(String data, HttpServletRequest request, HttpServletResponse response) throws JsonProcessingException;
/**
* 退出登录
* @return 是否退出成功
*/
boolean logout();
boolean logout(HttpServletRequest request);
/**
* 刷新token

View File

@ -1,68 +1,116 @@
package com.das.modules.auth.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.das.common.captcha.CaptchaUtil;
import com.das.common.exceptions.ServiceException;
import com.das.common.result.R;
import com.das.common.result.ResultCode;
import com.das.common.utils.AESUtil;
import com.das.common.utils.AdminRedisTemplate;
import com.das.modules.auth.domain.LoginUserDetails;
import com.das.modules.auth.domain.request.LoginRequest;
import com.das.modules.auth.domain.vo.SysUserVo;
import com.das.modules.auth.entity.SysUser;
import com.das.modules.auth.mapper.SysUserMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
//import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.das.modules.auth.domain.vo.LoginVO;
import com.das.modules.auth.service.ILoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author chenhaojie
* @Description
*/
@Service
public class LoginServiceImpl implements ILoginService {
@Resource
private SysUserMapper sysUserMapper;
@Autowired
private AdminRedisTemplate adminRedisTemplate;
@Resource
private AESUtil aesUtil;
@Value("${aesKey}")
private String key;
// @Override
// public String login(LoginRequest loginRequest) {
// String captcha = loginRequest.getUsername();
// String account = loginRequest.getUsername();
// String password = loginRequest.getPassword();
// String uuid = loginRequest.getPassword();
//
// // 验证码验证逻辑这里省略需根据实际情况实现
// if (CaptchaUtil.checkVerificationCode(uuid, captcha, adminRedisTemplate)) {
// return "验证码错误";
// }
// // 用户名密码验证
//// if (isValidUser(account, password)) {
// // 登录成功使用Sa-Token生成Token
// StpUtil.login(account);
// // 返回Token给客户端
// return StpUtil.getTokenValue();
//// } else {
//// return "用户名或密码错误";
//
// }
@Override
public String login(LoginRequest loginRequest) {
String captcha = loginRequest.getUsername();
String account = loginRequest.getUsername();
public LoginUserDetails login(String param, HttpServletRequest request, HttpServletResponse response) throws JsonProcessingException {
String iv = request.getHeader("v");
System.out.println("iv:" + iv);
param = aesUtil.decrypt(key, param, iv);
ObjectMapper objectMapper = new ObjectMapper();
LoginRequest loginRequest = objectMapper.readValue(param, LoginRequest.class);
String name = loginRequest.getUsername();
String password = loginRequest.getPassword();
String uuid = loginRequest.getPassword();
String code = loginRequest.getCode();
String uuid = loginRequest.getUuid();
// 验证码验证逻辑这里省略需根据实际情况实现
if (CaptchaUtil.checkVerificationCode(uuid, captcha, adminRedisTemplate)) {
return "验证码错误";
LambdaQueryWrapper<SysUser> wrapper = Wrappers.lambdaQuery();
wrapper.eq(SysUser::getAccount, loginRequest.getUsername());
SysUser sysUser = sysUserMapper.selectOne(wrapper);
if (sysUser == null) {
throw new ServiceException("无账号信息");
}
// 用户名密码验证
// if (isValidUser(account, password)) {
// 登录成功使用Sa-Token生成Token
StpUtil.login(account);
// 返回Token给客户端
return StpUtil.getTokenValue();
// } else {
// return "用户名或密码错误";
if (!CaptchaUtil.checkVerificationCode(uuid, code, adminRedisTemplate)) {
throw new ServiceException("验证码不正确");
}
StpUtil.login(sysUser.getId()); // 执行登录这里username为用户唯一标识
SysUserVo sysUserVo = new SysUserVo();
sysUserVo.setId(sysUser.getId());
sysUserVo.setAccount(sysUser.getAccount());
sysUserVo.setUserName(sysUser.getUserName());
sysUserVo.setPhone(sysUser.getPhone());
sysUserVo.setEmail(sysUser.getEmail());
sysUserVo.setOrgId(sysUser.getOrgId());
LoginUserDetails loginInfo = new LoginUserDetails();
loginInfo.setSysUser(sysUserVo); // 存储用户信息到会话
loginInfo.setToken(StpUtil.getTokenValue());
return loginInfo;
}
@Override
public boolean logout() {
// // 调用注销接口的时候需要携带token
// // SecurityContextHolder 请求中获取认证信息然后再获取username
// SecurityContext context = SecurityContextHolder.getContext();
// Authentication authentication = context.getAuthentication();
// if (authentication == null){
// return false;
// }
// // 认证之后 principal 里面是 UserDetails 的子类
// // 未认证的时候 principal 里面是 username (登录账号)
// Object principal = authentication.getPrincipal();
// // UserLogin 实现了 UserDetails 接口
// LoginUserDetails userLogin = (LoginUserDetails) principal;
// SysUser user = userLogin.getSysUser();
// String username = user.getUsername();
// String uuid = userLogin.getUuid();
// // 认证设置为空
// context.setAuthentication(null);
// // 删除redis中的token
// return adminRedisTemplate.del(username+":"+uuid);
public boolean logout(HttpServletRequest request) {
String iv = request.getHeader("v");
String token = request.getHeader("token");
System.out.println("iv:" + iv);
token = aesUtil.decrypt(key, token, iv);
StpUtil.logoutByTokenValue(token);
return true;
}
@ -72,11 +120,11 @@ public class LoginServiceImpl implements ILoginService {
// String uuid = (String) claim.get("uuid");
// String username = (String) claim.get("username");
// String refresh = (String) claim.get("refresh");
// if (StrUtil.isEmpty(refresh)){
// if (StrUtil.isEmpty(refresh)) {
// throw new BusinessException("非法token");
// }
// LoginUserDetails loginUserDetails = adminRedisTemplate.get(username + ":refresh:" + uuid);
// if (loginUserDetails == null){
// if (loginUserDetails == null) {
// throw new BusinessException("token过期请重新登录");
// }
// String token = tokenService.createToken(loginUserDetails);
@ -84,7 +132,7 @@ public class LoginServiceImpl implements ILoginService {
// String tokenPrefix = jwtProperties.getTokenPrefix();
// // 删除原有刷新token
// adminRedisTemplate.del(username + ":refresh:" + uuid);
// return new LoginVO(tokenPrefix + " " +token,newRefreshToken ,jwtProperties.getExpireTime());
// return new LoginVO(tokenPrefix + " " + token, newRefreshToken, jwtProperties.getExpireTime());
return new LoginVO();
}

View File

@ -34,3 +34,5 @@ spring:
# 日志配置文件位置
logging:
config: classpath:log/logback-spring-dev.xml
aesKey: b6967ee87b86d85a

View File

@ -23,19 +23,15 @@ sa-token:
# token前缀
token-prefix: Bearer
# token有效期单位秒
timeout: 3600
timeout: 7200
# 是否允许同一账号多终端登录默认为true
is-concurrent: true
# 系统信息
admin-boot:
version: 1.0.0
spring:
profiles:
active: dev
application:
name: admin
name: das
#json格式化全局配置,相当于@JsonFormat
jackson:
time-zone: GMT+8
@ -76,7 +72,7 @@ spring:
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
# 搜索指定包别名
typeAliasesPackage: cn.mesmile.**.entity
typeAliasesPackage: com.das.**.entity
global-config:
# 关闭MP3.0自带的banner
banner: false

View File

@ -5,34 +5,27 @@
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.das.modules.auth.entity.SysUser">
<id column="id" property="id"/>
<result column="status" property="status"/>
<result column="deleted" property="deleted"/>
<result column="create_time" property="createTime"/>
<result column="create_by" property="createBy"/>
<result column="update_time" property="updateTime"/>
<result column="update_by" property="updateBy"/>
<result column="created_time" property="createdTime"/>
<result column="created_by" property="createdBy"/>
<result column="updated_time" property="updatedTime"/>
<result column="updated_by" property="updatedBy"/>
<result column="account" property="account"/>
<result column="nick_name" property="nickName"/>
<result column="real_name" property="realName"/>
<result column="user_type" property="userType"/>
<result column="email" property="email"/>
<result column="mobile" property="mobile"/>
<result column="sex" property="sex"/>
<result column="avatar" property="avatar"/>
<result column="birthday" property="birthday"/>
<result column="password" property="password"/>
<result column="remark" property="remark"/>
<result column="user_name" property="userName"/>
<result column="email" property="email"/>
<result column="phone" property="phone"/>
<result column="org_id" property="orgId"/>
<result column="last_login" property="lastLogin"/>
<result column="revision" property="revision"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
status,
deleted,
create_time,
create_by,
update_time,
update_by,
id, account, nick_name, real_name, user_type, email, mobile, sex, avatar, birthday, password, remark
created_time,
created_by,
updated_time,
updated_by,
id, account, user_name, org_id, last_login, email, phone, revision, password
</sql>
</mapper>