diff --git a/README.md b/README.md
index 42285985..bc384c71 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
### 简介
-ContiNew-Admin (incubating) 中后台管理框架,Continue New Admin,持续以最新流行技术栈构建。当前阶段采用的技术栈:Spring Boot、Undertow、Redis、Redisson、Hutool 等。
+ContiNew-Admin (incubating) 中后台管理框架,Continue New Admin,持续以最新流行技术栈构建。当前阶段采用的技术栈:Spring Boot、Undertow、Sa-Token、JWT、Redis、Redisson、Hutool 等。
### 开始
@@ -40,8 +40,9 @@ git clone https://github.com/Charles7c/continew-admin.git
| :----------------------------------------------------------- | :----------- | :----------------------------------------------------------- |
| [Spring Boot](https://spring.io/projects/spring-boot) | 2.7.6 | 简化新 Spring 应用的初始搭建以及开发过程。 |
| [Undertow](https://undertow.io/) | 2.2.20.Final | 采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。 |
+| [Sa-Token + JWT](https://sa-token.dev33.cn/) | 1.33.0 | 轻量级 Java 权限认证框架,让鉴权变得简单、优雅。 |
| [Redis](https://redis.io/) | 6.2.7 | 高性能的 key-value 数据库。 |
-| [Redisson](https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D) | 3.18.1 | 不仅仅是一个 Redis Java 客户端,同其他 Redis Java 客户端有着很大的区别,相比之下其他客户端提供的功能还仅仅停留在作为数据库驱动层面上,比如仅针对 Redis 提供连接方式,发送命令和处理返回结果等。而 Redisson 充分的利用了 Redis 键值数据库提供的一系列优势,基于 Java 实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。 |
+| [Redisson](https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D) | 3.19.0 | 不仅仅是一个 Redis Java 客户端,同其他 Redis Java 客户端有着很大的区别,相比之下其他客户端提供的功能还仅仅停留在作为数据库驱动层面上,比如仅针对 Redis 提供连接方式,发送命令和处理返回结果等。而 Redisson 充分的利用了 Redis 键值数据库提供的一系列优势,基于 Java 实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。 |
| Easy Captcha | 1.6.2 | Java 图形验证码,支持 gif、中文、算术等类型,可用于 Java Web、JavaSE 等项目。 |
| [Knife4j](https://doc.xiaominfo.com/) | 4.0.0 | 前身是 swagger-bootstrap-ui,集 Swagger2 和 OpenAPI3 为一体的增强解决方案。本项目使用的是 [knife4j-openapi3-spring-boot-starter](https://gitee.com/xiaoym/swagger-bootstrap-ui-demo/tree/master/knife4j-springdoc-openapi-demo) 基于 OpenAPI3 规范,在 Spring Boot < 3.0.0-M1 的单体架构下可以直接引用此 starter,该模块包含了 UI 部分,底层基于 springdoc-openapi 项目。 |
| [Hutool](https://www.hutool.cn/) | 5.8.10 | 小而全的 Java 工具类库,通过静态方法封装,降低相关 API 的学习成本,提高工作效率,使 Java 拥有函数式语言般的优雅,让 Java 语言也可以“甜甜的”。 |
@@ -54,46 +55,55 @@ git clone https://github.com/Charles7c/continew-admin.git
> 下方项目目录结构是按照模块的层次顺序进行介绍的,实际 IDE 中 `continew-admin-common` 模块会因为字母排序原因排在上方。
>
-```
-continew-admin 全局通用项目配置及依赖版本管理
- ├─continew-admin-webapi API 模块(存放 Controller 层代码,打包部署的模块)
- │ ├─src
- │ │ ├─main
- │ │ │ ├─java 工程源文件代码目录
- │ │ │ │ └─top
- │ │ │ │ └─charles7c
- │ │ │ │ └─cnadmin
- │ │ │ │ └─webapi
- │ │ │ │ └─controller
- │ │ │ │ └─auth 认证相关 API
- │ │ │ │ └─ContinewAdminApplication.java 启动入口
- │ │ │ ├─resources 工程配置目录
- ├─continew-admin-system 系统管理模块(存放系统管理模块相关功能,例如:部门管理、角色管理、用户管理等)
- │ ├─src
- │ │ ├─main
- │ │ │ ├─java 工程源文件代码目录
- │ │ │ │ └─top
- │ │ │ │ └─charles7c
- │ │ │ │ └─cnadmin
- │ │ │ │ └─auth 认证相关业务及配置
- │ │ │ │ └─config 认证相关配置
- │ │ │ │ └─properties 认证相关配置属性
- │ │ │ │ └─model 认证相关模型
- │ │ │ │ └─vo 认证相关 VO(View Object)
- ├─continew-admin-common 公共模块(存放公共工具类,公共配置等)
- │ ├─src
- │ │ ├─main
- │ │ │ ├─java 工程源文件代码目录
- │ │ │ │ └─top
- │ │ │ │ └─charles7c
- │ │ │ │ └─cnadmin
- │ │ │ │ └─common
- │ │ │ │ └─config 公共配置
- │ │ │ │ └─jackson Jackson 配置
- │ │ │ │ └─properties 公共配置属性
- │ │ │ │ └─model 公共模型
- │ │ │ │ └─vo 公共 VO(View Object)
- │ │ │ │ └─util 公共工具类
+```bash
+continew-admin # 全局通用项目配置及依赖版本管理
+ ├─ continew-admin-webapi # API 模块(存放 Controller 层代码,打包部署的模块)
+ │ ├─ src
+ │ │ ├─ main
+ │ │ │ ├─ java # 工程源文件代码目录
+ │ │ │ │ └─ top
+ │ │ │ │ └─ charles7c
+ │ │ │ │ ├─ cnadmin
+ │ │ │ │ │ └─ webapi
+ │ │ │ │ │ └─ controller
+ │ │ │ │ │ └─ auth # 认证相关 API
+ │ │ │ │ └─ ContinewAdminApplication.java # 启动入口
+ │ │ │ ├─ resources # 工程配置目录
+ ├─ continew-admin-system # 系统管理模块(存放系统管理模块相关功能,例如:部门管理、角色管理、用户管理等)
+ │ ├─ src
+ │ │ ├─ main
+ │ │ │ ├─ java # 工程源文件代码目录
+ │ │ │ │ └─ top
+ │ │ │ │ └─ charles7c
+ │ │ │ │ └─ cnadmin
+ │ │ │ │ └─ auth # 认证相关业务及配置
+ │ │ │ │ ├─ config # 认证相关配置
+ │ │ │ │ │ ├─ satoken # Sa-Token 配置
+ │ │ │ │ │ └─ properties # 认证相关配置属性
+ │ │ │ │ ├─ model # 认证相关模型
+ │ │ │ │ │ ├─ entity # 认证相关实体对象
+ │ │ │ │ │ ├─ request # 认证相关请求对象
+ │ │ │ │ │ └─ vo # 认证相关 VO(View Object)
+ │ │ │ │ └─ service # 认证相关业务
+ │ │ │ │ └─ impl # 认证相关业务实现
+ ├─ continew-admin-common # 公共模块(存放公共工具类,公共配置等)
+ │ ├─ src
+ │ │ ├─ main
+ │ │ │ ├─ java # 工程源文件代码目录
+ │ │ │ │ └─ top
+ │ │ │ │ └─ charles7c
+ │ │ │ │ └─ cnadmin
+ │ │ │ │ └─ common
+ │ │ │ │ ├─ config # 公共配置
+ │ │ │ │ │ ├─ jackson # Jackson 配置
+ │ │ │ │ │ └─ properties # 公共配置属性
+ │ │ │ │ ├─ consts # 公共常量
+ │ │ │ │ ├─ exception # 公共异常
+ │ │ │ │ ├─ handler # 公共处理器
+ │ │ │ │ ├─ model # 公共模型
+ │ │ │ │ │ ├─ entity # 公共实体对象
+ │ │ │ │ │ └─ vo # 公共 VO(View Object)
+ │ │ │ │ └─ util # 公共工具类
```
### License
diff --git a/continew-admin-common/pom.xml b/continew-admin-common/pom.xml
index 1e7adb43..4725f56d 100644
--- a/continew-admin-common/pom.xml
+++ b/continew-admin-common/pom.xml
@@ -59,6 +59,31 @@ limitations under the License.
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+
+ cn.dev33
+ sa-token-spring-boot-starter
+
+
+
+
+ cn.dev33
+ sa-token-jwt
+
+
+
+
+ cn.dev33
+ sa-token-dao-redis-jackson
+
+
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/RsaProperties.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/RsaProperties.java
new file mode 100644
index 00000000..6be2ae49
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/RsaProperties.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.common.config.properties;
+
+import lombok.Data;
+
+import org.springframework.stereotype.Component;
+
+import cn.hutool.extra.spring.SpringUtil;
+
+/**
+ * RSA 配置属性
+ *
+ * @author Charles7c
+ * @since 2022/12/21 20:21
+ */
+@Data
+@Component
+public class RsaProperties {
+
+ /** 私钥 */
+ public static final String PRIVATE_KEY;
+
+ static {
+ PRIVATE_KEY = SpringUtil.getProperty("rsa.privateKey");
+ }
+}
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/consts/CommonConstants.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/consts/CommonConstants.java
new file mode 100644
index 00000000..41cc519e
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/consts/CommonConstants.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.common.consts;
+
+/**
+ * 公共常量
+ *
+ * @author Charles7c
+ * @since 2022/12/22 19:30
+ */
+public interface CommonConstants {
+
+ /**
+ * 状态-启用
+ */
+ Integer STATUS_ENABLE = 1;
+
+ /**
+ * 状态-禁用
+ */
+ Integer STATUS_DISABLE = 2;
+
+}
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/exception/BadRequestException.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/exception/BadRequestException.java
new file mode 100644
index 00000000..56a82faa
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/exception/BadRequestException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.common.exception;
+
+import lombok.NoArgsConstructor;
+
+/**
+ * 自定义验证异常-错误请求
+ *
+ * @author Charles7c
+ * @since 2022/12/21 20:59
+ */
+@NoArgsConstructor
+public class BadRequestException extends RuntimeException {
+
+ public BadRequestException(String message) {
+ super(message);
+ }
+
+}
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java
new file mode 100644
index 00000000..2b8666ea
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.common.handler;
+
+import java.util.Objects;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolationException;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.hutool.core.util.StrUtil;
+
+import top.charles7c.cnadmin.common.exception.BadRequestException;
+import top.charles7c.cnadmin.common.model.vo.R;
+
+/**
+ * 全局异常处理器
+ *
+ * @author Charles7c
+ * @since 2022/12/21 21:01
+ */
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ /**
+ * 拦截未知的系统异常
+ */
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ @ExceptionHandler(Exception.class)
+ public R handleException(Exception e, HttpServletRequest request) {
+ log.error("请求地址'{}',发生未知异常", request.getRequestURI(), e);
+ return R.fail(e.getMessage());
+ }
+
+ /**
+ * 拦截未知的运行时异常
+ */
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ @ExceptionHandler(RuntimeException.class)
+ public R handleRuntimeException(RuntimeException e, HttpServletRequest request) {
+ log.error("请求地址'{}',发生系统异常", request.getRequestURI(), e);
+ return R.fail(e.getMessage());
+ }
+
+ /**
+ * 拦截自定义验证异常-错误请求
+ */
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(BadRequestException.class)
+ public R handleBadRequestException(BadRequestException e, HttpServletRequest request) {
+ log.error("请求地址'{}',自定义验证失败", request.getRequestURI(), e);
+ return R.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage());
+ }
+
+ /**
+ * 拦截校验异常-绑定异常
+ */
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(BindException.class)
+ public R handleBindException(BindException e, HttpServletRequest request) {
+ log.error("请求地址'{}',参数验证失败", request.getRequestURI(), e);
+ return R.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage());
+ }
+
+ /**
+ * 拦截校验异常-违反约束异常
+ */
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(ConstraintViolationException.class)
+ public R constraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
+ log.error("请求地址'{}',参数验证失败", request.getRequestURI(), e);
+ return R.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage());
+ }
+
+ /**
+ * 拦截校验异常-方法参数无效异常
+ */
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
+ log.error("请求地址'{}',参数验证失败", request.getRequestURI(), e);
+ return R.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage());
+ }
+
+ /**
+ * 拦截校验异常-方法参数类型不匹配异常
+ */
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(MethodArgumentTypeMismatchException.class)
+ public R handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e,
+ HttpServletRequest request) {
+ String subMsg = StrUtil.format("参数名:'{}',期望参数类型:'{}'", e.getName(), e.getParameter().getParameterType());
+ log.error("请求地址'{}',参数转换失败。方法:'{}',{}", request.getRequestURI(),
+ Objects.requireNonNull(e.getParameter().getMethod()).getName(), subMsg, e);
+ return R.fail(HttpStatus.BAD_REQUEST.value(), subMsg);
+ }
+
+ /**
+ * 拦截校验异常-请求方式不支持异常
+ */
+ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
+ @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+ public R handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
+ log.error("请求地址'{}',不支持'{}'请求", request.getRequestURI(), e.getMethod());
+ return R.fail(HttpStatus.METHOD_NOT_ALLOWED.value(), e.getMessage());
+ }
+
+ /**
+ * 拦截认证异常-未登录异常
+ */
+ @ResponseStatus(HttpStatus.UNAUTHORIZED)
+ @ExceptionHandler(NotLoginException.class)
+ public R handleNotLoginException(NotLoginException e, HttpServletRequest request) {
+ log.error("请求地址'{}',认证失败'{}',无法访问系统资源", request.getRequestURI(), e.getMessage());
+ return R.fail(HttpStatus.UNAUTHORIZED.value(), "认证失败,无法访问系统资源");
+ }
+}
\ No newline at end of file
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/entity/BaseEntity.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/entity/BaseEntity.java
new file mode 100644
index 00000000..669d76f5
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/entity/BaseEntity.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.common.model.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import lombok.Data;
+
+/**
+ * 实体类基类
+ *
+ * @author Charles7c
+ * @since 2022/12/12 23:02
+ */
+@Data
+public class BaseEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 创建人
+ */
+ private Long createUser;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 修改人
+ */
+ private Long updateUser;
+
+ /**
+ * 修改时间
+ */
+ private Date updateTime;
+}
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/CheckUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/CheckUtils.java
new file mode 100644
index 00000000..1f4fe40f
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/CheckUtils.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.common.util;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import top.charles7c.cnadmin.common.exception.BadRequestException;
+
+/**
+ * 检查工具类
+ *
+ * @author Charles7c
+ * @since 2022/12/21 20:56
+ */
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class CheckUtils {
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param obj
+ * 被检测的对象
+ * @param message
+ * 错误信息
+ */
+ public static void exIfNull(Object obj, String message) {
+ if (obj == null) {
+ log.error(message);
+ throw new BadRequestException(message);
+ }
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param str
+ * 被检测的字符串
+ * @param message
+ * 错误信息
+ */
+ public static void exIfBlank(CharSequence str, String message) {
+ if (StrUtil.isBlank(str)) {
+ log.error(message);
+ throw new BadRequestException(message);
+ }
+ }
+
+ /**
+ * 如果相同,抛出异常
+ *
+ * @param obj1
+ * 要比较的对象1
+ * @param obj2
+ * 要比较的对象2
+ * @param message
+ * 错误信息
+ */
+ public static void exIfEqual(Object obj1, Object obj2, String message) {
+ if (ObjectUtil.equals(obj1, obj2)) {
+ log.error(message);
+ throw new BadRequestException(message);
+ }
+ }
+
+ /**
+ * 如果不相同,抛出异常
+ *
+ * @param obj1
+ * 要比较的对象1
+ * @param obj2
+ * 要比较的对象2
+ * @param message
+ * 错误信息
+ */
+ public static void exIfNotEqual(Object obj1, Object obj2, String message) {
+ if (ObjectUtil.notEqual(obj1, obj2)) {
+ log.error(message);
+ throw new BadRequestException(message);
+ }
+ }
+
+ /**
+ * 如果条件成立,抛出异常
+ *
+ * @param conditionSupplier
+ * 条件
+ * @param message
+ * 错误信息
+ */
+ public static void exIfCondition(java.util.function.BooleanSupplier conditionSupplier, String message) {
+ if (conditionSupplier != null && conditionSupplier.getAsBoolean()) {
+ log.error(message);
+ throw new BadRequestException(message);
+ }
+ }
+}
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExceptionUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExceptionUtils.java
new file mode 100644
index 00000000..90f2e684
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExceptionUtils.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.common.util;
+
+import java.util.function.Consumer;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 异常工具类
+ *
+ * @author Charles7c
+ * @since 2022/12/21 20:56
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class ExceptionUtils {
+
+ /**
+ * 如果有异常,返回 null
+ *
+ * @param exSupplier
+ * 可能会出现异常的方法执行
+ * @param
+ * /
+ * @return /
+ */
+ public static T exToNull(ExSupplier exSupplier) {
+ return exToDefault(exSupplier, null);
+ }
+
+ /**
+ * 如果有异常,执行异常处理
+ *
+ * @param supplier
+ * 可能会出现异常的方法执行
+ * @param exConsumer
+ * 异常处理
+ * @param
+ * /
+ * @return /
+ */
+ public static T exToNull(ExSupplier supplier, Consumer exConsumer) {
+ return exToDefault(supplier, null, exConsumer);
+ }
+
+ /**
+ * 如果有异常,返回默认值
+ *
+ * @param exSupplier
+ * 可能会出现异常的方法执行
+ * @param defaultValue
+ * 默认值
+ * @param
+ * /
+ * @return /
+ */
+ public static T exToDefault(ExSupplier exSupplier, T defaultValue) {
+ return exToDefault(exSupplier, defaultValue, null);
+ }
+
+ /**
+ * 如果有异常,执行异常处理,返回默认值
+ *
+ * @param exSupplier
+ * 可能会出现异常的方法执行
+ * @param defaultValue
+ * 默认值
+ * @param exConsumer
+ * 异常处理
+ * @param
+ * /
+ * @return /
+ */
+ public static T exToDefault(ExSupplier exSupplier, T defaultValue, Consumer exConsumer) {
+ try {
+ return exSupplier.get();
+ } catch (Exception ex) {
+ if (exConsumer != null) {
+ exConsumer.accept(ex);
+ }
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 异常提供者
+ *
+ * @param
+ * /
+ */
+ public interface ExSupplier {
+ /**
+ * 获取返回值
+ *
+ * @return /
+ * @throws Exception
+ * /
+ */
+ T get() throws Exception;
+ }
+
+}
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/SecureUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/SecureUtils.java
new file mode 100644
index 00000000..e38f9605
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/SecureUtils.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.common.util;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+
+/**
+ * 加密/解密工具类
+ *
+ * @author Charles7c
+ * @since 2022/12/21 21:41
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class SecureUtils {
+
+ /**
+ * 公钥加密
+ *
+ * @param data
+ * 要加密的内容
+ * @param publicKey
+ * 公钥
+ * @return 公钥加密并 Base64 加密后的内容
+ */
+ public static String encryptByRsaPublicKey(String data, String publicKey) {
+ return Base64.encode(SecureUtil.rsa(null, publicKey).encrypt(data, KeyType.PublicKey));
+ }
+
+ /**
+ * 私钥解密
+ *
+ * @param data
+ * 要解密的内容(Base64 加密过)
+ * @param privateKey
+ * 私钥
+ * @return 解密后的内容
+ */
+ public static String decryptByRsaPrivateKey(String data, String privateKey) {
+ return new String(SecureUtil.rsa(privateKey, null).decrypt(Base64.decode(data), KeyType.PrivateKey));
+ }
+
+ /**
+ * MD5 加密
+ *
+ * @param data
+ * 要加密的内容
+ * @param salt
+ * 盐
+ * @return 加密后的内容
+ */
+ public static String md5Salt(String data, String salt) {
+ return SecureUtil.md5(SecureUtil.md5(data) + salt);
+ }
+}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/config/satoken/SaTokenConfiguration.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/config/satoken/SaTokenConfiguration.java
new file mode 100644
index 00000000..601cdda9
--- /dev/null
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/config/satoken/SaTokenConfiguration.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.auth.config.satoken;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+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;
+
+import cn.dev33.satoken.interceptor.SaInterceptor;
+import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
+import cn.dev33.satoken.stp.StpLogic;
+import cn.dev33.satoken.stp.StpUtil;
+
+/**
+ * Sa-Token 配置
+ *
+ * @author Charles7c
+ * @since 2022/12/19 22:13
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+public class SaTokenConfiguration implements WebMvcConfigurer {
+
+ private final SecurityProperties securityProperties;
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验
+ registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin())).addPathPatterns("/**")
+ .excludePathPatterns(securityProperties.getExcludes());
+ }
+
+ /**
+ * Sa-Token 整合 JWT(简单模式)
+ */
+ @Bean
+ public StpLogic getStpLogicJwt() {
+ return new StpLogicJwtForSimple();
+ }
+}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/config/satoken/SecurityProperties.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/config/satoken/SecurityProperties.java
new file mode 100644
index 00000000..5f163118
--- /dev/null
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/config/satoken/SecurityProperties.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.auth.config.satoken;
+
+import lombok.Data;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Sa-Token 安全配置属性
+ *
+ * @author Charles7c
+ * @since 2022/12/19 22:14
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "security")
+public class SecurityProperties {
+
+ /**
+ * 排除路径配置
+ */
+ private String[] excludes = new String[0];
+}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/entity/SysUser.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/entity/SysUser.java
new file mode 100644
index 00000000..9980ad52
--- /dev/null
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/entity/SysUser.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.auth.model.entity;
+
+import lombok.Data;
+
+import top.charles7c.cnadmin.common.model.entity.BaseEntity;
+
+/**
+ * 用户实体
+ *
+ * @author Charles7c
+ * @since 2022/12/21 20:42
+ */
+@Data
+public class SysUser extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户 ID
+ */
+ private Long userId;
+
+ /**
+ * 用户名
+ */
+ private String username;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * 昵称
+ */
+ private String nickname;
+
+ /**
+ * 性别(0未知 1男 2女)
+ */
+ private Integer gender;
+
+ /**
+ * 头像
+ */
+ private String avatar;
+
+ /**
+ * 状态(1启用 2禁用)
+ */
+ private Integer status;
+}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/request/LoginRequest.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/request/LoginRequest.java
new file mode 100644
index 00000000..8633bebf
--- /dev/null
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/request/LoginRequest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.auth.model.request;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotBlank;
+
+import lombok.Data;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * 登录信息
+ *
+ * @author Charles7c
+ * @since 2022/12/21 20:43
+ */
+@Data
+@Schema(description = "登录信息")
+public class LoginRequest implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户名
+ */
+ @Schema(description = "用户名")
+ @NotBlank(message = "用户名不能为空")
+ private String username;
+
+ /**
+ * 密码
+ */
+ @Schema(description = "密码")
+ @NotBlank(message = "密码不能为空")
+ private String password;
+
+ /**
+ * 验证码
+ */
+ @Schema(description = "验证码")
+ @NotBlank(message = "验证码不能为空")
+ private String captcha;
+
+ /**
+ * 验证码标识
+ */
+ @Schema(description = "验证码标识")
+ @NotBlank(message = "验证码标识不能为空")
+ private String uuid;
+}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/CaptchaVO.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/CaptchaVO.java
index 98884b86..ac406707 100644
--- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/CaptchaVO.java
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/CaptchaVO.java
@@ -37,9 +37,9 @@ public class CaptchaVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
- * 验证码唯一标识
+ * 验证码标识
*/
- @Schema(description = "验证码唯一标识")
+ @Schema(description = "验证码标识")
private String uuid;
/**
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/LoginVO.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/LoginVO.java
new file mode 100644
index 00000000..08903d8b
--- /dev/null
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/LoginVO.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.auth.model.vo;
+
+import java.io.Serializable;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * 令牌信息
+ *
+ * @author Charles7c
+ * @since 2022/12/21 20:42
+ */
+@Data
+@Accessors(chain = true)
+@Schema(description = "令牌信息")
+public class LoginVO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 令牌
+ */
+ @Schema(description = "令牌")
+ private String token;
+}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/LoginService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/LoginService.java
new file mode 100644
index 00000000..8818ca3b
--- /dev/null
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/LoginService.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.auth.service;
+
+/**
+ * 登录业务接口
+ *
+ * @author Charles7c
+ * @since 2022/12/21 21:48
+ */
+public interface LoginService {
+
+ /**
+ * 用户登录
+ *
+ * @param username
+ * 用户名
+ * @param password
+ * 密码
+ * @return 令牌
+ */
+ String login(String username, String password);
+
+}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/UserService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/UserService.java
new file mode 100644
index 00000000..85e77ee3
--- /dev/null
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/UserService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.auth.service;
+
+import top.charles7c.cnadmin.auth.model.entity.SysUser;
+
+/**
+ * 用户业务接口
+ *
+ * @author Charles7c
+ * @since 2022/12/21 21:48
+ */
+public interface UserService {
+
+ /**
+ * 根据用户名查询
+ *
+ * @param username
+ * 用户名
+ * @return 用户信息
+ */
+ SysUser getByUsername(String username);
+}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java
new file mode 100644
index 00000000..16b6c8a7
--- /dev/null
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.auth.service.impl;
+
+import lombok.RequiredArgsConstructor;
+
+import org.springframework.stereotype.Service;
+
+import cn.dev33.satoken.stp.StpUtil;
+
+import top.charles7c.cnadmin.auth.model.entity.SysUser;
+import top.charles7c.cnadmin.auth.service.LoginService;
+import top.charles7c.cnadmin.auth.service.UserService;
+import top.charles7c.cnadmin.common.consts.CommonConstants;
+import top.charles7c.cnadmin.common.util.CheckUtils;
+import top.charles7c.cnadmin.common.util.SecureUtils;
+
+/**
+ * 登录业务实现类
+ *
+ * @author Charles7c
+ * @since 2022/12/21 21:49
+ */
+@Service
+@RequiredArgsConstructor
+public class LoginServiceImpl implements LoginService {
+
+ private final UserService userService;
+
+ @Override
+ public String login(String username, String password) {
+ // 查询用户
+ SysUser sysUser = userService.getByUsername(username);
+
+ // 校验
+ CheckUtils.exIfNull(sysUser, "用户名或密码错误");
+ Long userId = sysUser.getUserId();
+ CheckUtils.exIfNotEqual(sysUser.getPassword(), SecureUtils.md5Salt(password, userId.toString()), "用户名或密码错误");
+ CheckUtils.exIfEqual(CommonConstants.STATUS_DISABLE, sysUser.getStatus(), "此账号已被禁用,如有疑问,请联系管理员");
+
+ // 登录
+ StpUtil.login(userId);
+
+ // 返回令牌
+ return StpUtil.getTokenValue();
+ }
+}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/UserServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/UserServiceImpl.java
new file mode 100644
index 00000000..eba6020d
--- /dev/null
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/UserServiceImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.auth.service.impl;
+
+import java.util.Date;
+
+import org.springframework.stereotype.Service;
+
+import top.charles7c.cnadmin.auth.model.entity.SysUser;
+import top.charles7c.cnadmin.auth.service.UserService;
+import top.charles7c.cnadmin.common.util.SecureUtils;
+
+/**
+ * 用户业务实现类
+ *
+ * @author Charles7c
+ * @since 2022/12/21 21:49
+ */
+@Service
+public class UserServiceImpl implements UserService {
+
+ @Override
+ public SysUser getByUsername(String username) {
+ if (!"admin".equals(username)) {
+ return null;
+ }
+ SysUser sysUser = new SysUser();
+ sysUser.setUserId(1L);
+ sysUser.setUsername("admin");
+ sysUser.setPassword(SecureUtils.md5Salt("123456", sysUser.getUserId().toString()));
+ sysUser.setNickname("超级管理员");
+ sysUser.setGender(1);
+ sysUser.setStatus(1);
+ sysUser.setCreateUser(1L);
+ sysUser.setCreateTime(new Date());
+ sysUser.setUpdateUser(1L);
+ sysUser.setUpdateTime(new Date());
+ return sysUser;
+ }
+}
diff --git a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/ContinewAdminApplication.java b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/ContinewAdminApplication.java
index 69cfa911..5dcaed21 100644
--- a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/ContinewAdminApplication.java
+++ b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/ContinewAdminApplication.java
@@ -33,6 +33,8 @@ import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
+import cn.dev33.satoken.annotation.SaIgnore;
+
import top.charles7c.cnadmin.common.config.properties.ContinewAdminProperties;
/**
@@ -62,6 +64,7 @@ public class ContinewAdminApplication implements ApplicationRunner {
* @return /
*/
@Hidden
+ @SaIgnore
@GetMapping("/")
public String index() {
return String.format("%s backend service started successfully.", properties.getName());
diff --git a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/auth/CaptchaController.java b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/auth/CaptchaController.java
index 8a4b27a9..1dba70af 100644
--- a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/auth/CaptchaController.java
+++ b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/auth/CaptchaController.java
@@ -30,6 +30,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.wf.captcha.base.Captcha;
+import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.IdUtil;
import top.charles7c.cnadmin.auth.config.properties.CaptchaProperties;
@@ -44,6 +45,7 @@ import top.charles7c.cnadmin.common.util.RedisUtils;
* @since 2022/12/11 14:00
*/
@Tag(name = "验证码 API")
+@SaIgnore
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/captcha", produces = MediaType.APPLICATION_JSON_VALUE)
diff --git a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/auth/LoginController.java b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/auth/LoginController.java
new file mode 100644
index 00000000..4f42c99e
--- /dev/null
+++ b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/auth/LoginController.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.charles7c.cnadmin.webapi.controller.auth;
+
+import lombok.RequiredArgsConstructor;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.dev33.satoken.stp.StpUtil;
+
+import top.charles7c.cnadmin.auth.config.properties.CaptchaProperties;
+import top.charles7c.cnadmin.auth.model.request.LoginRequest;
+import top.charles7c.cnadmin.auth.model.vo.LoginVO;
+import top.charles7c.cnadmin.auth.service.LoginService;
+import top.charles7c.cnadmin.common.config.properties.RsaProperties;
+import top.charles7c.cnadmin.common.model.vo.R;
+import top.charles7c.cnadmin.common.util.CheckUtils;
+import top.charles7c.cnadmin.common.util.ExceptionUtils;
+import top.charles7c.cnadmin.common.util.RedisUtils;
+import top.charles7c.cnadmin.common.util.SecureUtils;
+
+/**
+ * 登录 API
+ *
+ * @author Charles7c
+ * @since 2022/12/21 20:37
+ */
+@Tag(name = "登录 API")
+@RestController
+@RequiredArgsConstructor
+@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
+public class LoginController {
+
+ private final LoginService loginService;
+ private final CaptchaProperties captchaProperties;
+
+ @SaIgnore
+ @Operation(summary = "用户登录", description = "根据用户名和密码进行登录认证")
+ @PostMapping("/login")
+ public R login(@Validated @RequestBody LoginRequest loginRequest) {
+ // 校验验证码
+ String captchaKey = RedisUtils.formatKey(captchaProperties.getKeyPrefix(), loginRequest.getUuid());
+ String captcha = RedisUtils.getCacheObject(captchaKey);
+ CheckUtils.exIfBlank(captcha, "验证码已失效");
+ RedisUtils.deleteCacheObject(captchaKey);
+ CheckUtils.exIfCondition(() -> !captcha.equalsIgnoreCase(loginRequest.getCaptcha()), "验证码错误");
+
+ // 用户登录
+ String rawPassword = ExceptionUtils
+ .exToNull(() -> SecureUtils.decryptByRsaPrivateKey(loginRequest.getPassword(), RsaProperties.PRIVATE_KEY));
+ CheckUtils.exIfBlank(rawPassword, "密码解密失败");
+ String token = loginService.login(loginRequest.getUsername(), rawPassword);
+ return R.ok(new LoginVO().setToken(token));
+ }
+
+ @SaIgnore
+ @Operation(summary = "用户退出", description = "注销用户的当前登录")
+ @Parameter(name = "Authorization", description = "令牌", required = true, example = "Bearer xxxxxxxxx",
+ in = ParameterIn.HEADER)
+ @PostMapping("/logout")
+ public R logout() {
+ CheckUtils.exIfCondition(() -> !StpUtil.isLogin(), "Token 无效");
+ StpUtil.logout();
+ return R.ok();
+ }
+}
\ No newline at end of file
diff --git a/continew-admin-webapi/src/main/resources/application-dev.yml b/continew-admin-webapi/src/main/resources/application-dev.yml
index f2f3cda5..2863fc8d 100644
--- a/continew-admin-webapi/src/main/resources/application-dev.yml
+++ b/continew-admin-webapi/src/main/resources/application-dev.yml
@@ -19,6 +19,29 @@ spring:
# 是否开启 SSL
ssl: false
+--- ### 安全配置
+security:
+ # 排除路径配置
+ excludes:
+ # 静态资源
+ - /*.html
+ - /**/*.html
+ - /**/*.css
+ - /**/*.js
+ - /webSocket/**
+ # 接口文档相关资源
+ - /favicon.ico
+ - /doc.html
+ - /webjars/**
+ - /swagger-ui/**
+ - /swagger-resources/**
+ - /*/api-docs/**
+
+--- ### 非对称加密配置(例如:密码加密传输,前端公钥加密,后端私钥解密;在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair)
+rsa:
+ # 私钥
+ privateKey: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV
+
--- ### 验证码配置
captcha:
# 类型
diff --git a/continew-admin-webapi/src/main/resources/application-prod.yml b/continew-admin-webapi/src/main/resources/application-prod.yml
index 99cf3547..cd048e59 100644
--- a/continew-admin-webapi/src/main/resources/application-prod.yml
+++ b/continew-admin-webapi/src/main/resources/application-prod.yml
@@ -19,6 +19,22 @@ spring:
# 是否开启 SSL
ssl: false
+--- ### 安全配置
+security:
+ # 排除路径配置
+ excludes:
+ # 静态资源
+ - /*.html
+ - /**/*.html
+ - /**/*.css
+ - /**/*.js
+ - /webSocket/**
+
+--- ### 非对称加密配置(例如:密码加密传输,前端公钥加密,后端私钥解密;在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair)
+rsa:
+ # 私钥
+ privateKey: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV
+
--- ### 验证码配置
captcha:
# 类型
diff --git a/continew-admin-webapi/src/main/resources/application.yml b/continew-admin-webapi/src/main/resources/application.yml
index ad1084b6..551de150 100644
--- a/continew-admin-webapi/src/main/resources/application.yml
+++ b/continew-admin-webapi/src/main/resources/application.yml
@@ -3,13 +3,13 @@ continew-admin:
# 名称
name: ContiNew-Admin
# 应用名称
- appName: @project.parent.name@
+ appName: continew-admin
# 版本
- version: @project.version@
+ version: 0.0.1-SNAPSHOT
# 描述
- description: @project.parent.description@
+ description: ContiNew-Admin (incubating) 中后台管理框架,Continue New Admin,持续以最新流行技术栈构建。
# URL
- url: @project.parent.url@
+ url: https://github.com/Charles7c/continew-admin
## 作者信息配置
author:
name: Charles7c
@@ -54,6 +54,31 @@ knife4j:
# 自定义 footer 内容,支持 Markdown 语法
footer-custom-content: '[Apache-2.0](https://github.com/Charles7c/continew-admin/blob/dev/LICENSE) | Copyright © 2022-present [ContiNew-Admin](https://github.com/Charles7c/continew-admin)'
+--- ### Sa-Token 配置
+sa-token:
+ # token名称(同时也是 cookie 名称)
+ token-name: Authorization
+ # token前缀(例如填写 Bearer 实际传参 Authorization: Bearer xxxx-xxxx-xxxx-xxxx)
+ token-prefix: Bearer
+ # token有效期(单位:秒,默认 30 天,-1 代表永不过期)
+ timeout: 86400
+ # token临时有效期(单位:秒,指定时间内无操作就视为 token 过期)
+ activity-timeout: 1800
+ # 是否打开自动续签(如果此值为 true,框架会在每次直接或间接调用 getLoginId() 时进行一次过期检查与续签操作)
+ auto-renew: true
+ # 是否允许同一账号并发登录(为 true 时允许一起登录,为 false 时新登录挤掉旧登录)
+ is-concurrent: true
+ # 在多人登录同一账号时,是否共用一个 token(为 true 时所有登录共用一个 token,为 false 时每次登录新建一个 token)
+ is-share: false
+ # 是否尝试从 header 里读取 Token
+ is-read-header: true
+ # 是否尝试从 cookie 里读取 Token,此值为 false 后,StpUtil.login(id) 登录时也不会再往前端注入 Cookie,适合前后端分离模式
+ is-read-cookie: false
+ # 是否打印操作日志
+ is-log: false
+ # JWT秘钥
+ jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk
+
--- ### 服务器配置
server:
servlet:
diff --git a/pom.xml b/pom.xml
index f10b2a17..1574e3a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,9 +42,11 @@ limitations under the License.
+ 1.33.0
+
4.0.0
- 3.18.1
+ 3.19.0
1.6.2
5.8.10
@@ -60,6 +62,34 @@ limitations under the License.
+
+
+
+ cn.dev33
+ sa-token-spring-boot-starter
+ ${sa-token.version}
+
+
+
+
+ cn.dev33
+ sa-token-jwt
+ ${sa-token.version}
+
+
+ cn.hutool
+ hutool-all
+
+
+
+
+
+
+ cn.dev33
+ sa-token-dao-redis-jackson
+ ${sa-token.version}
+
+