Compare commits

...

2 Commits

Author SHA1 Message Date
Bunny 8035c8b56a 修改配置文件 2025-02-18 18:35:06 +08:00
Bunny a3b118aef8 版本v2 2025-02-18 18:32:06 +08:00
74 changed files with 732 additions and 701 deletions

View File

View File

@ -25,39 +25,9 @@ public class Knife4jConfig {
return new OpenAPI().info(info).externalDocs(new ExternalDocumentation());
}
// 管理员相关分类接口
@Bean
public GroupedOpenApi all() {
return GroupedOpenApi.builder().group("全部请求接口").pathsToMatch("/api/**").build();
}
@Bean
public GroupedOpenApi i18n() {
return GroupedOpenApi.builder().group("多语言").pathsToMatch("/api/i18n/**", "/api/i18nType/**").build();
}
@Bean
public GroupedOpenApi config() {
return GroupedOpenApi.builder().group("配置")
.pathsToMatch("/api/config/**", "/api/emailTemplate/**", "/api/emailUsers/**",
"/api/message/**", "/api/messageReceived/**", "/api/messageType/**",
"/api/menuIcon/**", "/api/schedulers/**", "/api/schedulersGroup/**"
)
.build();
}
@Bean
public GroupedOpenApi system() {
return GroupedOpenApi.builder().group("系统")
.pathsToMatch("/api/dept/**", "/api/files/**", "/api/power/**",
"/api/rolePower/**", "/api/role/**", "/api/router/**",
"/api/routerRole/**", "/api/user/**", "/api/userRole/**"
).build();
}
@Bean
public GroupedOpenApi log() {
return GroupedOpenApi.builder().group("日志")
.pathsToMatch("/api/userLoginLog/**", "/api/quartzExecuteLog/**"
).build();
public GroupedOpenApi groupedOpenAdminApi() {
return GroupedOpenApi.builder().group("默认请求接口").pathsToMatch("/admin/**").build();
}
}

View File

@ -15,7 +15,7 @@ public class AuthCustomerException extends RuntimeException {
Integer code;
// 描述信息
String message = "服务异常";
String message;
// 返回结果状态
ResultCodeEnum resultCodeEnum;

View File

@ -6,7 +6,6 @@ import cn.bunny.dao.vo.result.Result;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ -37,7 +36,6 @@ public class GlobalExceptionHandler {
@ResponseBody
public Result<Object> exceptionHandler(RuntimeException exception) {
String message = exception.getMessage();
message = StringUtils.hasText(message) ? message : "服务器异常";
// 解析异常
String jsonParseError = "JSON parse error (.*)";
@ -69,7 +67,7 @@ public class GlobalExceptionHandler {
log.error("GlobalExceptionHandler===>运行时异常信息:{}", message);
exception.printStackTrace();
return Result.error(null, 500, message);
return Result.error(null, 500, "服务器异常");
}
// 捕获系统异常

View File

@ -0,0 +1,206 @@
package cn.bunny.common.service.utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpUtil {
public static HttpResponse doGet(String host, String path, String method, Map<String, String> headers, Map<String, String> querys) throws Exception {
HttpClient httpClient = wrapClient(host);
HttpGet request = new HttpGet(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
return httpClient.execute(request);
}
public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, Map<String, String> bodys) throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (bodys != null) {
List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
for (String key : bodys.keySet()) {
nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
}
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
request.setEntity(formEntity);
}
return httpClient.execute(request);
}
public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, String body) throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (StringUtils.isNotBlank(body)) {
request.setEntity(new StringEntity(body, "utf-8"));
}
return httpClient.execute(request);
}
public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, byte[] body) throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (body != null) {
request.setEntity(new ByteArrayEntity(body));
}
return httpClient.execute(request);
}
public static HttpResponse doPut(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, String body) throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPut request = new HttpPut(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (StringUtils.isNotBlank(body)) {
request.setEntity(new StringEntity(body, "utf-8"));
}
return httpClient.execute(request);
}
public static HttpResponse doPut(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, byte[] body) throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPut request = new HttpPut(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (body != null) {
request.setEntity(new ByteArrayEntity(body));
}
return httpClient.execute(request);
}
public static HttpResponse doDelete(String host, String path, String method, Map<String, String> headers, Map<String, String> querys) throws Exception {
HttpClient httpClient = wrapClient(host);
HttpDelete request = new HttpDelete(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
return httpClient.execute(request);
}
private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
StringBuilder sbUrl = new StringBuilder();
sbUrl.append(host);
if (!StringUtils.isBlank(path)) {
sbUrl.append(path);
}
if (null != querys) {
StringBuilder sbQuery = new StringBuilder();
for (Map.Entry<String, String> query : querys.entrySet()) {
if (!sbQuery.isEmpty()) {
sbQuery.append("&");
}
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
sbQuery.append(query.getValue());
}
if (!StringUtils.isBlank(query.getKey())) {
sbQuery.append(query.getKey());
if (!StringUtils.isBlank(query.getValue())) {
sbQuery.append("=");
sbQuery.append(URLEncoder.encode(query.getValue(), StandardCharsets.UTF_8));
}
}
}
if (!sbQuery.isEmpty()) {
sbUrl.append("?").append(sbQuery);
}
}
return sbUrl.toString();
}
private static HttpClient wrapClient(String host) {
HttpClient httpClient = new DefaultHttpClient();
if (host.startsWith("https://")) {
sslClient(httpClient);
}
return httpClient;
}
private static void sslClient(HttpClient httpClient) {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] xcs, String str) {
}
public void checkServerTrusted(X509Certificate[] xcs, String str) {
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = httpClient.getConnectionManager();
SchemeRegistry registry = ccm.getSchemeRegistry();
registry.register(new Scheme("https", 443, ssf));
} catch (Exception ex) {
throw new RuntimeException();
}
}
}

View File

@ -11,13 +11,13 @@ import java.io.IOException;
public class ResponseUtil {
public static void out(HttpServletResponse response, Result<Object> result) {
try {
ObjectMapper mapper = new ObjectMapper();
ObjectMapper mapper = new ObjectMapper();
// 注册JavaTimeModule模块
mapper.registerModule(new JavaTimeModule());
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpStatus.OK.value());
// 注册JavaTimeModule模块
mapper.registerModule(new JavaTimeModule());
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpStatus.OK.value());
try {
mapper.writeValue(response.getWriter(), result);
} catch (IOException e) {
e.printStackTrace();

View File

@ -13,9 +13,6 @@ import java.util.Objects;
import java.util.Properties;
public class MailSenderUtil {
private MailSenderUtil() {
// 私有化构造器
}
/**
* 如果启用SSL需要配置以下
@ -26,8 +23,8 @@ public class MailSenderUtil {
Properties properties = new Properties();
// 开启认证
properties.setProperty("mail.smtp.auth", "true");
// 是否启用调试---会输出发送邮件调试内容
properties.setProperty("mail.debug", "false");
// 启用调试
properties.setProperty("mail.debug", "true");
// 设置链接超时
properties.setProperty("mail.smtp.timeout", "200000");
// 设置端口

View File

@ -23,11 +23,6 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- spring-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>

View File

@ -37,7 +37,7 @@ public class LoginDto {
@Schema(name = "type", title = "登录类型")
@NotBlank(message = "登录类型不能为空")
@NotNull(message = "登录类型能为空")
private String type = "default";
private String type;
@Schema(name = "readMeDay", title = "记住我的天数")
private Long readMeDay = 1L;

View File

@ -6,11 +6,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
/**
* <p>
@ -25,7 +20,7 @@ import java.util.List;
@Accessors(chain = true)
@TableName("sys_user")
@Schema(name = "AdminUser对象", title = "用户信息", description = "用户信息")
public class AdminUser extends BaseEntity implements UserDetails {
public class AdminUser extends BaseEntity {
@Schema(name = "username", title = "用户名")
private String username;
@ -60,9 +55,5 @@ public class AdminUser extends BaseEntity implements UserDetails {
@Schema(name = "status", title = "状态", description = "1:禁用 0:正常")
private Boolean status;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}
}

View File

@ -1,21 +0,0 @@
package cn.bunny.dao.enums;
import lombok.Getter;
@Getter
public enum LoginEnums {
// 邮箱登录请求
EMAIL_STRATEGY("email"),
// 默认登录请求
default_STRATEGY("default"),
// 登良路请求API
LOGIN_REQUEST_API("/api/login"),
;
private final String value;
LoginEnums(String value) {
this.value = value;
}
}

View File

@ -24,7 +24,7 @@ public enum ResultCodeEnum {
EMAIL_CODE_NOT_EMPTY(201, "邮箱验证码不能为空"),
SEND_EMAIL_CODE_NOT_EMPTY(201, "请先发送邮箱验证码"),
EMAIL_CODE_NOT_MATCHING(201, "邮箱验证码不匹配"),
LOGIN_ERROR(500, "账号或密码错误"),
LOGIN_ERROR(201, "账号或密码错误"),
LOGIN_ERROR_USERNAME_PASSWORD_NOT_EMPTY(201, "登录信息不能为空"),
GET_BUCKET_EXCEPTION(201, "获取文件信息失败"),
SEND_MAIL_CODE_ERROR(201, "邮件发送失败"),

View File

@ -1,68 +0,0 @@
name: auth-compose # 定义该配置的名称为 auth-dependence
services: # 定义服务列表
# 安装MySQL
mysql: # 定义 MySQL 服务
container_name: mysql_master # 容器名称为 mysql_master
image: mysql:8.0.33 # 使用 MySQL 8.0.33 版本的镜像
ports:
- "3306:3306" # 将宿主机的 3306 端口映射到容器的 3306 端口
environment:
- MYSQL_ROOT_PASSWORD=123456 # 设置 MySQL 的 root 用户密码为 123456
- TZ=Asia/Shanghai # 设置时区为亚洲/上海
volumes:
# - ~/docker/docker_data/mysql/mysql_master/etc/my.cnf:/etc/my.cnf # 如果需要创建配置文件
- ~/docker/docker_data/mysql/mysql_master/etc/mysql:/etc/mysql/conf.d # 挂载 MySQL 配置文件目录
- ~/docker/docker_data/mysql/mysql_master/data:/var/lib/mysql # 挂载 MySQL 数据目录
- ~/docker/docker_data/mysql/mysql_master/backup:/backup # 挂载备份目录
command:
- "--log-bin=mysql-bin" # 启用二进制日志
- "--server-id=1" # 设置服务器 ID 为 1
- "--collation-server=utf8mb4_unicode_ci" # 设置默认的排序规则为 utf8mb4_unicode_ci
- "--character-set-server=utf8mb4" # 设置默认的字符集为 utf8mb4
- "--lower-case-table-names=1" # 设置表名存储为小写
restart: always # 设置容器总是自动重启
privileged: true # 赋予容器特权模式
networks:
- auth # 将 MySQL 服务加入到 auth 网络
# 安装Redis
redis: # 定义 Redis 服务
container_name: redis_master # 容器名称为 redis_master
image: redis:7.0.10 # 使用 Redis 7.0.10 版本的镜像
ports:
- "6379:6379" # 将宿主机的 6379 端口映射到容器的 6379 端口
volumes:
# - ~/docker/docker_data/redis_master/redis.conf:/etc/redis/redis.conf # 需要创建配置文件
- ~/docker/docker_data/redis_master:/etc/redis # 挂载 Redis 配置文件目录
- ~/docker/docker_data/redis_master/data:/data # 挂载 Redis 数据目录
command:
- "--appendonly yes" # 启用 AOF 持久化
- "--daemonize no" # 不以守护进程方式运行
- "--requirepass 123456" # 设置 Redis 访问密码为 123456
- "--tcp-keepalive 300" # 设置 TCP keepalive 时间为 300 秒
restart: always # 设置容器总是自动重启
networks:
- auth # 将 MySQL 服务加入到 auth 网络
# 安装 Minio
minio: # 定义 Minio 服务
image: minio/minio # 使用 Minio 官方镜像
container_name: minio_master # 容器名称为 minio_master
ports:
- "9000:9000" # 将宿主机的 9000 端口映射到容器的 9000 端口
- "9090:9090" # 将宿主机的 9090 端口映射到容器的 9090 端口
volumes:
- ~/docker/docker_data/minio/data:/data # 挂载 Minio 数据目录
environment:
- MINIO_ROOT_USER=bunny # 设置 Minio 的 root 用户名为 bunny
- MINIO_ROOT_PASSWORD=12345678 # 设置 Minio 的 root 用户密码为 123456
command: "server /data --console-address :9090" # 启动 Minio 服务并指定控制台地址
restart: always # 设置容器总是自动重启
networks:
- auth # 将 MySQL 服务加入到 auth 网络
networks: # 定义网络
auth: # 定义名为 auth 的网络
name: auth # 网络名称为 auth
driver: bridge # 使用 bridge 驱动(默认)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
<version>3.2.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.bunny</groupId>

View File

@ -14,7 +14,7 @@ WORKDIR /home/server
# 复制jar包
COPY target/*.jar /home/server/app.jar
# 程序内部挂在目录看情况是否需要操作本地docker
# 程序内部挂在目录
VOLUME /usr/bin/docker
VOLUME ["/var/run/docker.sock"]
VOLUME /etc/docker/daemon.json

View File

@ -25,13 +25,6 @@
<artifactId>service-utils</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- spring-security -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -1,14 +1,12 @@
package cn.bunny.services;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@MapperScan("cn.bunny.services.mapper")
@ -16,14 +14,10 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableScheduling
@EnableCaching
@EnableTransactionManagement
@Slf4j
@SpringBootApplication
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -14,7 +14,7 @@ import reactor.core.publisher.Mono;
@Tag(name = "系统配置", description = "系统配置相关接口")
@RestController
@RequestMapping("/api/config")
@RequestMapping("/admin/config")
public class ConfigurationController {
@Autowired

View File

@ -30,7 +30,7 @@ import java.util.List;
*/
@Tag(name = "系统部门", description = "部门相关接口")
@RestController
@RequestMapping("/api/dept")
@RequestMapping("/admin/dept")
public class DeptController {
@Autowired

View File

@ -31,7 +31,7 @@ import java.util.Map;
*/
@Tag(name = "邮件模板", description = "邮件模板相关接口")
@RestController
@RequestMapping("api/emailTemplate")
@RequestMapping("admin/emailTemplate")
public class EmailTemplateController {
@Autowired

View File

@ -32,7 +32,7 @@ import java.util.Map;
*/
@Tag(name = "邮箱用户发送配置", description = "邮箱用户发送配置相关接口")
@RestController
@RequestMapping("api/emailUsers")
@RequestMapping("admin/emailUsers")
public class EmailUsersController {
@Autowired

View File

@ -36,7 +36,7 @@ import java.util.Set;
*/
@Tag(name = "系统文件表", description = "系统文件相关接口")
@RestController
@RequestMapping("api/files")
@RequestMapping("admin/files")
public class FilesController {
@Autowired

View File

@ -31,7 +31,7 @@ import java.util.Map;
*/
@Tag(name = "多语言", description = "多语言相关接口")
@RestController
@RequestMapping("api/i18n")
@RequestMapping("admin/i18n")
public class I18nController {
@Autowired

View File

@ -26,7 +26,7 @@ import java.util.List;
*/
@Tag(name = "多语言类型", description = "多语言类型相关接口")
@RestController
@RequestMapping("api/i18nType")
@RequestMapping("admin/i18nType")
public class I18nTypeController {
@Autowired

View File

@ -30,7 +30,7 @@ import java.util.List;
*/
@Tag(name = "系统菜单图标", description = "系统菜单图标相关接口")
@RestController
@RequestMapping("api/menuIcon")
@RequestMapping("admin/menuIcon")
public class MenuIconController {
@Autowired

View File

@ -32,7 +32,7 @@ import java.util.List;
*/
@Tag(name = "系统消息", description = "系统消息相关接口")
@RestController
@RequestMapping("api/message")
@RequestMapping("admin/message")
public class MessageController {
@Autowired

View File

@ -31,7 +31,7 @@ import java.util.List;
*/
@Tag(name = "消息接收(用户消息)", description = "消息接收(用户消息)相关接口")
@RestController
@RequestMapping("/api/messageReceived")
@RequestMapping("/admin/messageReceived")
public class MessageReceivedController {
@Autowired

View File

@ -30,7 +30,7 @@ import java.util.List;
*/
@Tag(name = "系统消息类型", description = "系统消息类型相关接口")
@RestController
@RequestMapping("api/messageType")
@RequestMapping("admin/messageType")
public class MessageTypeController {
@Autowired

View File

@ -31,7 +31,7 @@ import java.util.List;
*/
@Tag(name = "权限", description = "权限相关接口")
@RestController
@RequestMapping("api/power")
@RequestMapping("admin/power")
public class PowerController {
@Autowired

View File

@ -30,7 +30,7 @@ import java.util.List;
*/
@Tag(name = "角色", description = "角色相关接口")
@RestController
@RequestMapping("api/role")
@RequestMapping("admin/role")
public class RoleController {
@Autowired

View File

@ -22,7 +22,7 @@ import java.util.List;
*/
@Tag(name = "角色和权限", description = "角色和权限相关接口")
@RestController
@RequestMapping("api/rolePower")
@RequestMapping("admin/rolePower")
public class RolePowerController {
@Autowired

View File

@ -32,7 +32,7 @@ import java.util.List;
*/
@Tag(name = "系统路由", description = "系统路由相关接口")
@RestController
@RequestMapping("api/router")
@RequestMapping("admin/router")
public class RouterController {
@Autowired

View File

@ -21,7 +21,7 @@ import java.util.List;
*/
@Tag(name = "路由和角色", description = "路由和角色相关接口")
@RestController
@RequestMapping("api/routerRole")
@RequestMapping("admin/routerRole")
public class RouterRoleController {
@Autowired

View File

@ -27,7 +27,7 @@ import java.util.List;
*/
@Tag(name = "调度任务执行日志", description = "调度任务执行日志相关接口")
@RestController
@RequestMapping("api/quartzExecuteLog")
@RequestMapping("admin/quartzExecuteLog")
public class ScheduleExecuteLogController {
@Autowired

View File

@ -32,7 +32,7 @@ import java.util.Map;
*/
@Tag(name = "调度任务", description = "调度任务相关接口")
@RestController
@RequestMapping("api/schedulers")
@RequestMapping("admin/schedulers")
public class SchedulersController {
@Autowired

View File

@ -30,7 +30,7 @@ import java.util.List;
*/
@Tag(name = "任务调度分组", description = "任务调度分组相关接口")
@RestController
@RequestMapping("api/schedulersGroup")
@RequestMapping("admin/schedulersGroup")
public class SchedulersGroupController {
@Autowired

View File

@ -21,7 +21,7 @@ import java.util.List;
@Tag(name = "用户信息", description = "用户信息相关接口")
@RestController
@RequestMapping("/api/user")
@RequestMapping("/admin/user")
public class UserController {
@Autowired

View File

@ -28,7 +28,7 @@ import java.util.List;
*/
@Tag(name = "用户登录日志", description = "用户登录日志相关接口")
@RestController
@RequestMapping("api/userLoginLog")
@RequestMapping("admin/userLoginLog")
public class UserLoginLogController {
@Autowired

View File

@ -21,7 +21,7 @@ import java.util.List;
*/
@Tag(name = "用户和角色", description = "用户和角色相关接口")
@RestController
@RequestMapping("api/userRole")
@RequestMapping("admin/userRole")
public class UserRoleController {
@Autowired

View File

@ -1,4 +1,4 @@
package cn.bunny.services.utils.email;
package cn.bunny.services.factory;
import cn.bunny.common.service.exception.AuthCustomerException;
import cn.bunny.common.service.utils.mail.MailSenderUtil;
@ -7,6 +7,7 @@ import cn.bunny.dao.entity.system.EmailUsers;
import cn.bunny.dao.model.email.EmailSend;
import cn.bunny.dao.model.email.EmailSendInit;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.services.mapper.EmailTemplateMapper;
import cn.bunny.services.mapper.EmailUsersMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.mail.MessagingException;
@ -18,19 +19,19 @@ import java.util.HashMap;
import java.util.List;
@Component
public abstract class AbstractSenderEmailTemplate {
public class EmailFactory {
@Autowired
private EmailTemplateMapper emailTemplateMapper;
@Autowired
private EmailUsersMapper emailUsersMapper;
/**
* 基本邮件发送模板
*
* @param email 邮箱
* @param emailTemplate 邮箱模板
* @param params 传入的参数
* * 发送邮件模板
* 根据已存在的邮件模板发送邮件
*/
public void sendEmail(String email, EmailTemplate emailTemplate, HashMap<String, Object> params) {
public void sendEmailTemplate(String email, EmailTemplate emailTemplate, HashMap<String, Object> params) {
// 判断邮件模板是否为空
if (emailTemplate == null) throw new AuthCustomerException(ResultCodeEnum.EMAIL_TEMPLATE_IS_EMPTY);
@ -68,4 +69,27 @@ public abstract class AbstractSenderEmailTemplate {
throw new AuthCustomerException(ResultCodeEnum.SEND_MAIL_CODE_ERROR);
}
}
/**
* * 发送邮件模板
* 根据邮件模板发送邮件
*/
public void sendEmailTemplate(String email, Long emailTemplateId, HashMap<String, Object> params) {
EmailTemplate emailTemplate = emailTemplateMapper.selectOne(Wrappers.<EmailTemplate>lambdaQuery().eq(EmailTemplate::getId, emailTemplateId));
sendEmailTemplate(email, emailTemplate, params);
}
/**
* 判断邮箱是否添加
*
* @param isDefault 邮箱是否为默认
*/
public void updateEmailUserDefault(Boolean isDefault) {
EmailUsers emailUsers = new EmailUsers();
// 判断状态如果是默认将所有的内容都设为false
if (isDefault) {
emailUsers.setIsDefault(false);
emailUsersMapper.update(emailUsers, Wrappers.<EmailUsers>lambdaUpdate().eq(EmailUsers::getIsDefault, true));
}
}
}

View File

@ -0,0 +1,25 @@
package cn.bunny.services.factory;
import cn.bunny.dao.vo.system.rolePower.PowerVo;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class PowerFactory {
/**
* * 构建权限树形结构
*
* @param id 节点ID
* @param powerVoList 节点列表
* @return 树形列表
*/
public List<PowerVo> handlePowerVoChildren(Long id, List<PowerVo> powerVoList) {
return powerVoList.stream()
.filter(powerVo -> powerVo.getParentId().equals(id))
.peek(powerVo -> powerVo.setChildren(handlePowerVoChildren(powerVo.getId(), powerVoList)))
.toList();
}
}

View File

@ -0,0 +1,44 @@
package cn.bunny.services.factory;
import cn.bunny.dao.constant.RedisUserConstant;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.services.mapper.UserMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class RoleFactory {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private UserMapper userMapper;
@Autowired
private UserFactory userFactory;
/**
* 批量更新Redis中用户信息
*
* @param userIds 用户Id列表
*/
public void updateUserRedisInfo(List<Long> userIds) {
// 根据Id查找所有用户
List<AdminUser> adminUsers = userMapper.selectList(Wrappers.<AdminUser>lambdaQuery().in(AdminUser::getId, userIds));
// 用户为空时不更新Redis的key
if (adminUsers.isEmpty()) return;
// 更新Redis中用户信息
adminUsers.stream().filter(user -> {
String adminLoginInfoPrefix = RedisUserConstant.getAdminLoginInfoPrefix(user.getUsername());
Object object = redisTemplate.opsForValue().get(adminLoginInfoPrefix);
return object != null;
}).forEach(user -> userFactory.buildUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME));
}
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.utils;
package cn.bunny.services.factory;
import cn.bunny.dao.vo.system.router.UserRouterVo;
import org.jetbrains.annotations.NotNull;
@ -8,7 +8,7 @@ import java.util.ArrayList;
import java.util.List;
@Component
public class RouterServiceUtil {
public class RouterServiceFactory {
/**
* * 递归调用设置子路由

View File

@ -1,6 +1,5 @@
package cn.bunny.services.utils;
package cn.bunny.services.factory;
import cn.bunny.common.service.exception.AuthCustomerException;
import cn.bunny.common.service.utils.JwtHelper;
import cn.bunny.common.service.utils.ip.IpUtil;
import cn.bunny.common.service.utils.minio.MinioUtil;
@ -16,6 +15,7 @@ import cn.bunny.services.mapper.PowerMapper;
import cn.bunny.services.mapper.RoleMapper;
import cn.bunny.services.mapper.UserLoginLogMapper;
import cn.bunny.services.mapper.UserMapper;
import cn.bunny.services.security.custom.CustomCheckIsAdmin;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -35,7 +35,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
public class UserUtil {
public class UserFactory {
@Autowired
private PowerMapper powerMapper;
@ -54,14 +54,7 @@ public class UserUtil {
@Autowired
private MinioUtil minioUtil;
/**
* 构建登录用户返回对象
*
* @param user 用户
* @param readMeDay 保存登录信息时间
* @return 登录信息
*/
@Transactional(rollbackFor = AuthCustomerException.class)
@Transactional
public LoginVo buildLoginUserVo(AdminUser user, long readMeDay) {
Long userId = user.getId();
String email = user.getEmail();
@ -131,7 +124,7 @@ public class UserUtil {
List<String> permissions = new ArrayList<>();
// 判断是否是 admin 如果是admin 赋予所有权限
boolean isAdmin = RoleUtil.checkAdmin(roles, permissions, user);
boolean isAdmin = CustomCheckIsAdmin.checkAdmin(roles, permissions, user);
if (!isAdmin) {
permissions = powerMapper.selectListByUserId(userId).stream().map(Power::getPowerCode).toList();
}

View File

@ -1,24 +1,20 @@
package cn.bunny.services.security.config;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.services.mapper.UserMapper;
import cn.bunny.services.security.custom.CustomAuthorizationManagerServiceImpl;
import cn.bunny.services.security.custom.CustomPasswordEncoder;
import cn.bunny.services.security.filter.TokenLoginFilterService;
import cn.bunny.services.security.handelr.SecurityAccessDeniedHandler;
import cn.bunny.services.security.handelr.SecurityAuthenticationEntryPoint;
import cn.bunny.services.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import cn.bunny.services.security.service.CustomUserDetailsService;
import cn.bunny.services.security.service.impl.CustomAuthorizationManagerServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
@ -28,7 +24,6 @@ import org.springframework.security.web.util.matcher.RegexRequestMatcher;
@EnableMethodSecurity
public class WebSecurityConfig {
// 需要排出的无需验证的请求路径
public static String[] annotations = {
"/", "/ws/**",
"/*/*/noAuth/**", "/*/noAuth/**", "/noAuth/**",
@ -37,7 +32,13 @@ public class WebSecurityConfig {
};
@Autowired
private UserService userService;
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private CustomPasswordEncoder customPasswordEncoder;
@Autowired
private CustomAuthorizationManagerServiceImpl customAuthorizationManagerService;
@ -72,31 +73,10 @@ public class WebSecurityConfig {
exception.accessDeniedHandler(new SecurityAccessDeniedHandler());
})
// 登录验证过滤器
.addFilterBefore(new TokenLoginFilterService(authenticationConfiguration, userService), UsernamePasswordAuthenticationFilter.class);
.addFilterBefore(new TokenLoginFilterService(authenticationConfiguration, redisTemplate, customUserDetailsService), UsernamePasswordAuthenticationFilter.class)
// 自定义密码加密器和用户登录
.passwordManagement(customPasswordEncoder).userDetailsService(customUserDetailsService);
return httpSecurity.build();
}
/**
* 使用数据库方式
* 登录方式邮箱+用户名
*
* @param userMapper 获取用户数据
* @return 数据库的用户
*/
@Bean
public UserDetailsService userDetailsService(UserMapper userMapper) {
return username -> {
// 查询用户相关内容
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<AdminUser>()
.eq(AdminUser::getEmail, username)
.or()
.eq(AdminUser::getUsername, username);
// 根据邮箱查询用户名
AdminUser adminUser = userMapper.selectOne(queryWrapper);
if (adminUser == null) throw new UsernameNotFoundException(ResultCodeEnum.USER_IS_EMPTY.getMessage());
return adminUser;
};
}
}

View File

@ -13,11 +13,10 @@ import org.springframework.security.core.AuthenticationException;
@ToString
@Slf4j
public class CustomAuthenticationException extends AuthenticationException {
String message;
ResultCodeEnum resultCodeEnum;
public CustomAuthenticationException(String message) {
super(message);
this.message = message;
public CustomAuthenticationException(ResultCodeEnum codeEnum) {
super(codeEnum.getMessage());
this.resultCodeEnum = codeEnum;
}
}

View File

@ -0,0 +1,51 @@
package cn.bunny.services.security.custom;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.dao.entity.system.AdminUser;
import java.util.List;
/**
* 检查是否是管理员
*/
public class CustomCheckIsAdmin {
/**
* 判断是否是管理员
*
* @param roleList 角色代码列表
* @return 是否是管理员
*/
public static boolean checkAdmin(List<String> roleList) {
// 判断是否是超级管理员
if (BaseContext.getUserId().equals(1L)) return true;
// 判断是否是 admin
return roleList.stream().anyMatch(role -> role.equals("admin"));
}
/**
* 判断是否是管理员
*
* @param roleList 角色代码列表
* @param permissions 权限列表
* @param adminUser 用户信息
* @return 是否是管理员
*/
public static boolean checkAdmin(List<String> roleList, List<String> permissions, AdminUser adminUser) {
// 判断是否是超级管理员
boolean isIdAdmin = adminUser.getId().equals(1L);
boolean isAdmin = roleList.stream().anyMatch(role -> role.equals("admin"));
// 判断是否是 admin
if (isIdAdmin || isAdmin) {
roleList.add("admin");
permissions.add("*");
permissions.add("*::*");
permissions.add("*::*::*");
return true;
}
return false;
}
}

View File

@ -0,0 +1,28 @@
package cn.bunny.services.security.custom;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.PasswordManagementConfigurer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.DigestUtils;
/**
* 自定义密码加密比对
*/
@Configuration
public class CustomPasswordEncoder implements PasswordEncoder, Customizer<PasswordManagementConfigurer<HttpSecurity>> {
@Override
public String encode(CharSequence rawPassword) {
return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.matches(DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes()));
}
@Override
public void customize(PasswordManagementConfigurer<HttpSecurity> httpSecurityPasswordManagementConfigurer) {
}
}

View File

@ -0,0 +1,23 @@
package cn.bunny.services.security.custom;
import cn.bunny.dao.entity.system.AdminUser;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
/**
* 重写自带的User
*/
@Getter
@Setter
public class CustomUser extends User {
private AdminUser user;
public CustomUser(AdminUser user, Collection<? extends GrantedAuthority> authorities) {
super(user.getUsername(), user.getPassword(), authorities);
this.user = user;
}
}

View File

@ -1,44 +1,52 @@
package cn.bunny.services.security.filter;
import cn.bunny.common.service.utils.ResponseUtil;
import cn.bunny.dao.constant.RedisUserConstant;
import cn.bunny.dao.dto.system.user.LoginDto;
import cn.bunny.dao.enums.LoginEnums;
import cn.bunny.dao.vo.result.Result;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.user.LoginVo;
import cn.bunny.services.security.handelr.SecurityAuthenticationFailureHandler;
import cn.bunny.services.security.handelr.SecurityAuthenticationSuccessHandler;
import cn.bunny.services.service.UserService;
import cn.bunny.services.security.service.CustomUserDetailsService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import java.io.IOException;
import static cn.bunny.common.service.utils.ResponseUtil.out;
/**
* 自定义登录
* 自定义登录路径和自定义登录返回数据
* * UsernamePasswordAuthenticationFilter
* * 也可以在这里添加验证码短信等的验证
* 由于SpringSecurity的登录只能是表单形式 并且用户名密码需要时usernamepassword,可以通过继承 UsernamePasswordAuthenticationFilter 获取登录请求的参数
* 再去设置到 UsernamePasswordAuthenticationToken 来改变请求传参方式参数名等 或者也可以在登录的时候加入其他参数等等
*/
public class TokenLoginFilterService extends UsernamePasswordAuthenticationFilter {
private final UserService userService;
LoginDto loginDto;
public TokenLoginFilterService(AuthenticationConfiguration authenticationConfiguration, UserService customUserDetailsService) throws Exception {
private final RedisTemplate<String, Object> redisTemplate;
private final CustomUserDetailsService customUserDetailsService;
private LoginDto loginDto;
public TokenLoginFilterService(AuthenticationConfiguration authenticationConfiguration, RedisTemplate<String, Object> redisTemplate, CustomUserDetailsService customUserDetailsService) throws Exception {
this.setAuthenticationSuccessHandler(new SecurityAuthenticationSuccessHandler());
this.setAuthenticationFailureHandler(new SecurityAuthenticationFailureHandler());
this.setPostOnly(false);
// 自定义登录路径自定义登录请求类型
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(LoginEnums.LOGIN_REQUEST_API.getValue(), HttpMethod.POST.name()));
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/login", HttpMethod.POST.name()));
this.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager());
this.userService = customUserDetailsService;
this.redisTemplate = redisTemplate;
this.customUserDetailsService = customUserDetailsService;
}
/**
@ -50,22 +58,61 @@ public class TokenLoginFilterService extends UsernamePasswordAuthenticationFilte
ObjectMapper objectMapper = new ObjectMapper();
try {
loginDto = objectMapper.readValue(request.getInputStream(), LoginDto.class);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());
return getAuthenticationManager().authenticate(authentication);
} catch (Exception exception) {
throw new UsernameNotFoundException(exception.getMessage());
// type不能为空
String type = loginDto.getType();
if (!StringUtils.hasText(type)) {
out(response, Result.error(ResultCodeEnum.REQUEST_IS_EMPTY));
return null;
}
String emailCode = loginDto.getEmailCode();
String username = loginDto.getUsername();
String password = loginDto.getPassword();
// 如果有邮箱验证码表示是邮箱登录
if (type.equals("email")) {
emailCode = emailCode.toLowerCase();
Object redisEmailCode = redisTemplate.opsForValue().get(RedisUserConstant.getAdminUserEmailCodePrefix(username));
// --------TODO 线上取消注释这个
// ----测试
// --------线上取消注释这个
// if (redisEmailCode == null) {
// out(response, Result.error(ResultCodeEnum.EMAIL_CODE_EMPTY));
// return null;
// }
//
// // 判断用户邮箱验证码是否和Redis中发送的验证码
// if (!emailCode.equals(redisEmailCode.toString().toLowerCase())) {
// out(response, Result.error(ResultCodeEnum.EMAIL_CODE_NOT_MATCHING));
// return null;
// }
}
Authentication authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
return getAuthenticationManager().authenticate(authenticationToken);
} catch (IOException e) {
out(response, Result.error(ResultCodeEnum.ILLEGAL_DATA_REQUEST));
return null;
}
}
/**
* 验证成功
* 在这里用户的账号和密码是对的
* 此时不要再去解析/转换 request.getInputStream() 因为流已经关闭
*/
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) {
LoginVo loginVo = userService.login(loginDto, response);
ResponseUtil.out(response, Result.success(loginVo));
// 获取登录返回信息
LoginVo loginVo = customUserDetailsService.login(loginDto, response);
if (loginVo == null) return;
// 判断用户是否禁用
if (loginVo.getStatus()) {
out(response, Result.error(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED));
return;
}
out(response, Result.success(loginVo));
}
/**
@ -73,6 +120,12 @@ public class TokenLoginFilterService extends UsernamePasswordAuthenticationFilte
*/
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
ResponseUtil.out(response, Result.error(null, failed.getMessage()));
String password = loginDto.getPassword();
String username = loginDto.getUsername();
if (!StringUtils.hasText(password) || !StringUtils.hasText(username))
out(response, Result.error(ResultCodeEnum.USERNAME_OR_PASSWORD_NOT_EMPTY));
else
out(response, Result.error(null, ResultCodeEnum.LOGIN_ERROR));
}
}

View File

@ -7,12 +7,11 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
/**
* 没有权限访问
*/
public class SecurityAccessDeniedHandler implements AccessDeniedHandler {
public class SecurityAccessDeniedHandler implements org.springframework.security.web.access.AccessDeniedHandler {
@SneakyThrows
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) {

View File

@ -5,11 +5,10 @@ import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import java.io.IOException;
public class SecurityAuthenticationFailureHandler implements AuthenticationFailureHandler {
public class SecurityAuthenticationFailureHandler implements org.springframework.security.web.authentication.AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
// 错误消息

View File

@ -0,0 +1,24 @@
package cn.bunny.services.security.service;
import cn.bunny.dao.dto.system.user.LoginDto;
import cn.bunny.dao.vo.system.user.LoginVo;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public interface CustomUserDetailsService extends UserDetailsService {
/**
* 根据用户名获取用户对象获取不到直接抛异常
*/
@Override
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
/**
* 前台用户登录接口
*
* @param loginDto 登录参数
* @return 登录后结果返回
*/
LoginVo login(LoginDto loginDto, HttpServletResponse response);
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.security.custom;
package cn.bunny.services.security.service.impl;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.common.service.utils.JwtHelper;
@ -9,7 +9,8 @@ import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.user.LoginVo;
import cn.bunny.services.mapper.PowerMapper;
import cn.bunny.services.mapper.RoleMapper;
import cn.bunny.services.utils.RoleUtil;
import cn.bunny.services.security.custom.CustomAuthenticationException;
import cn.bunny.services.security.custom.CustomCheckIsAdmin;
import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
@ -18,7 +19,6 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
@ -54,12 +54,12 @@ public class CustomAuthorizationManagerServiceImpl implements AuthorizationManag
// 判断是否有 token
String token = request.getHeader("token");
if (token == null) {
throw new UsernameNotFoundException(ResultCodeEnum.LOGIN_AUTH.getMessage());
throw new CustomAuthenticationException(ResultCodeEnum.LOGIN_AUTH);
}
// 判断 token 是否过期
if (JwtHelper.isExpired(token)) {
throw new UsernameNotFoundException(ResultCodeEnum.AUTHENTICATION_EXPIRED.getMessage());
throw new CustomAuthenticationException(ResultCodeEnum.AUTHENTICATION_EXPIRED);
}
// 解析JWT中的用户名
@ -72,12 +72,12 @@ public class CustomAuthorizationManagerServiceImpl implements AuthorizationManag
// 登录信息为空
if (loginVo == null) {
throw new UsernameNotFoundException(ResultCodeEnum.LOGIN_AUTH.getMessage());
throw new CustomAuthenticationException(ResultCodeEnum.LOGIN_AUTH);
}
// 判断用户是否禁用
if (loginVo.getStatus()) {
throw new UsernameNotFoundException(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED.getMessage());
throw new CustomAuthenticationException(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED);
}
// 设置用户信息
@ -103,7 +103,7 @@ public class CustomAuthorizationManagerServiceImpl implements AuthorizationManag
List<String> roleCodeList = roleList.stream().map(Role::getRoleCode).toList();
// 判断是否是管理员用户
boolean checkedAdmin = RoleUtil.checkAdmin(roleCodeList);
boolean checkedAdmin = CustomCheckIsAdmin.checkAdmin(roleCodeList);
if (checkedAdmin) return true;
// 判断请求地址是否是 noManage 不需要被验证的

View File

@ -0,0 +1,85 @@
package cn.bunny.services.security.service.impl;
import cn.bunny.common.service.exception.AuthCustomerException;
import cn.bunny.dao.dto.system.user.LoginDto;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.dao.vo.result.Result;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.user.LoginVo;
import cn.bunny.services.factory.UserFactory;
import cn.bunny.services.mapper.UserMapper;
import cn.bunny.services.security.custom.CustomUser;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import static cn.bunny.common.service.utils.ResponseUtil.out;
@Component
public class CustomUserDetailsServiceImpl implements cn.bunny.services.security.service.CustomUserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private UserFactory userFactory;
/**
* 根据用户名获取用户对象获取不到直接抛异常
*
* @param username 用户名
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 查询用户相关内容
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<AdminUser>()
.eq(AdminUser::getEmail, username)
.or()
.eq(AdminUser::getUsername, username);
// 根据邮箱查询用户名
AdminUser adminUser = userMapper.selectOne(queryWrapper);
if (adminUser == null) throw new UsernameNotFoundException(ResultCodeEnum.USER_IS_EMPTY.getMessage());
return new CustomUser(adminUser, AuthorityUtils.createAuthorityList());
}
/**
* 前台用户登录接口
*
* @param loginDto 登录参数
* @return 登录后结果返回
*/
@Override
public LoginVo login(LoginDto loginDto, HttpServletResponse response) {
String username = loginDto.getUsername();
String password = loginDto.getPassword();
Long readMeDay = loginDto.getReadMeDay();
// 查询用户相关内容
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<>();
if (loginDto.getType().equals("email")) {
queryWrapper.eq(AdminUser::getEmail, username);
} else {
queryWrapper.eq(AdminUser::getUsername, username);
}
AdminUser user = userMapper.selectOne(queryWrapper);
// 判断用户是否为空
if (user == null) {
out(response, Result.error(ResultCodeEnum.LOGIN_ERROR));
return null;
}
// 对登录密码进行md5加密判断是否与数据库中一致
String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
if (!user.getPassword().equals(md5Password)) throw new AuthCustomerException(ResultCodeEnum.LOGIN_ERROR);
return userFactory.buildLoginUserVo(user, readMeDay);
}
}

View File

@ -6,7 +6,6 @@ import cn.bunny.dao.vo.result.PageResult;
import cn.bunny.dao.vo.system.user.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.jetbrains.annotations.NotNull;
@ -21,14 +20,6 @@ import java.util.List;
* @since 2024-09-26
*/
public interface UserService extends IService<AdminUser> {
/**
* 前台用户登录接口
*
* @param loginDto 登录参数
* @return 登录后结果返回
*/
LoginVo login(LoginDto loginDto, HttpServletResponse response);
/**
* * 获取用户信息列表

View File

@ -9,10 +9,10 @@ import cn.bunny.dao.entity.system.EmailUsers;
import cn.bunny.dao.vo.result.PageResult;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.email.EmailUsersVo;
import cn.bunny.services.factory.EmailFactory;
import cn.bunny.services.mapper.EmailUsersMapper;
import cn.bunny.services.service.EmailUsersService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.validation.Valid;
@ -38,7 +38,7 @@ import java.util.Map;
public class EmailUsersServiceImpl extends ServiceImpl<EmailUsersMapper, EmailUsers> implements EmailUsersService {
@Autowired
private EmailUsersMapper emailUsersMapper;
private EmailFactory emailFactory;
/**
* * 邮箱用户发送配置 服务实现类
@ -67,7 +67,7 @@ public class EmailUsersServiceImpl extends ServiceImpl<EmailUsersMapper, EmailUs
@Override
public void addEmailUsers(EmailUsersAddDto dto) {
// 更新邮箱默认状态
updateEmailUserDefault(dto.getIsDefault());
emailFactory.updateEmailUserDefault(dto.getIsDefault());
// 保存数据
EmailUsers emailUsers = new EmailUsers();
@ -84,7 +84,7 @@ public class EmailUsersServiceImpl extends ServiceImpl<EmailUsersMapper, EmailUs
@Override
public void updateEmailUsers(@Valid EmailUsersUpdateDto dto) {
// 更新邮箱默认状态
updateEmailUserDefault(dto.getIsDefault());
emailFactory.updateEmailUserDefault(dto.getIsDefault());
// 更新内容
EmailUsers emailUsers = new EmailUsers();
@ -113,7 +113,7 @@ public class EmailUsersServiceImpl extends ServiceImpl<EmailUsersMapper, EmailUs
@Override
public void updateEmailUserStatus(EmailUserUpdateStatusDto dto) {
// 更新邮箱默认状态
updateEmailUserDefault(dto.getIsDefault());
emailFactory.updateEmailUserDefault(dto.getIsDefault());
EmailUsers emailUsers = new EmailUsers();
BeanUtils.copyProperties(dto, emailUsers);
@ -134,18 +134,4 @@ public class EmailUsersServiceImpl extends ServiceImpl<EmailUsersMapper, EmailUs
return map;
}).toList();
}
/**
* 判断邮箱是否添加
*
* @param isDefault 邮箱是否为默认
*/
public void updateEmailUserDefault(Boolean isDefault) {
EmailUsers emailUsers = new EmailUsers();
// 判断状态如果是默认将所有的内容都设为false
if (isDefault) {
emailUsers.setIsDefault(false);
emailUsersMapper.update(emailUsers, Wrappers.<EmailUsers>lambdaUpdate().eq(EmailUsers::getIsDefault, true));
}
}
}

View File

@ -11,9 +11,9 @@ import cn.bunny.dao.vo.result.PageResult;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.message.MessageReceivedWithMessageVo;
import cn.bunny.dao.vo.system.message.MessageUserVo;
import cn.bunny.services.factory.UserFactory;
import cn.bunny.services.mapper.MessageReceivedMapper;
import cn.bunny.services.service.MessageReceivedService;
import cn.bunny.services.utils.UserUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -39,7 +39,7 @@ import java.util.List;
public class MessageReceivedServiceImpl extends ServiceImpl<MessageReceivedMapper, MessageReceived> implements MessageReceivedService {
@Autowired
private UserUtil userUtil;
private UserFactory userFactory;
/**
* 管理员管理用户消息接收分页查询
@ -58,7 +58,7 @@ public class MessageReceivedServiceImpl extends ServiceImpl<MessageReceivedMappe
// 设置封面返回内容
String cover = vo.getCover();
cover = userUtil.checkGetUserAvatar(cover);
cover = userFactory.checkGetUserAvatar(cover);
vo.setCover(cover);
return vo;
}).toList();
@ -113,7 +113,7 @@ public class MessageReceivedServiceImpl extends ServiceImpl<MessageReceivedMappe
// 设置封面返回内容
String cover = vo.getCover();
cover = userUtil.checkGetUserAvatar(cover);
cover = userFactory.checkGetUserAvatar(cover);
vo.setCover(cover);
return vo;
}).toList();

View File

@ -14,12 +14,12 @@ import cn.bunny.dao.vo.system.message.MessageDetailVo;
import cn.bunny.dao.vo.system.message.MessageReceivedWithMessageVo;
import cn.bunny.dao.vo.system.message.MessageReceivedWithUserVo;
import cn.bunny.dao.vo.system.message.MessageVo;
import cn.bunny.services.factory.UserFactory;
import cn.bunny.services.mapper.MessageMapper;
import cn.bunny.services.mapper.MessageReceivedMapper;
import cn.bunny.services.mapper.UserMapper;
import cn.bunny.services.service.MessageReceivedService;
import cn.bunny.services.service.MessageService;
import cn.bunny.services.utils.UserUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -48,7 +48,7 @@ import java.util.stream.Collectors;
public class MessageServiceImpl extends ServiceImpl<MessageMapper, Message> implements MessageService {
@Autowired
private UserUtil userUtil;
private UserFactory userFactory;
@Autowired
private MessageReceivedMapper messageReceivedMapper;
@ -152,7 +152,7 @@ public class MessageServiceImpl extends ServiceImpl<MessageMapper, Message> impl
// 设置封面返回内容
String cover = dto.getCover();
dto.setCover(userUtil.checkGetUserAvatar(cover));
dto.setCover(userFactory.checkGetUserAvatar(cover));
// 先保存消息数据之后拿到保存消息的id
Message message = new Message();
@ -198,7 +198,7 @@ public class MessageServiceImpl extends ServiceImpl<MessageMapper, Message> impl
// 设置封面返回内容
String cover = dto.getCover();
dto.setCover(userUtil.checkGetUserAvatar(cover));
dto.setCover(userFactory.checkGetUserAvatar(cover));
// 更新内容
Message message = new Message();

View File

@ -4,11 +4,11 @@ import cn.bunny.dao.dto.system.rolePower.AssignPowersToRoleDto;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.dao.entity.system.RolePower;
import cn.bunny.dao.entity.system.UserRole;
import cn.bunny.services.factory.RoleFactory;
import cn.bunny.services.mapper.RolePowerMapper;
import cn.bunny.services.mapper.UserMapper;
import cn.bunny.services.mapper.UserRoleMapper;
import cn.bunny.services.service.RolePowerService;
import cn.bunny.services.utils.RoleUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
@ -33,7 +33,7 @@ public class RolePowerServiceImpl extends ServiceImpl<RolePowerMapper, RolePower
private UserMapper userMapper;
@Autowired
private RoleUtil roleUtil;
private RoleFactory roleFactory;
@Autowired
private UserRoleMapper userRoleMapper;
@ -84,6 +84,6 @@ public class RolePowerServiceImpl extends ServiceImpl<RolePowerMapper, RolePower
// 更新Redis中用户信息
List<Long> userIds = adminUsers.stream().map(AdminUser::getId).toList();
roleUtil.updateUserRedisInfo(userIds);
roleFactory.updateUserRedisInfo(userIds);
}
}

View File

@ -9,12 +9,12 @@ import cn.bunny.dao.entity.system.UserRole;
import cn.bunny.dao.vo.result.PageResult;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.rolePower.RoleVo;
import cn.bunny.services.factory.RoleFactory;
import cn.bunny.services.mapper.RoleMapper;
import cn.bunny.services.mapper.RolePowerMapper;
import cn.bunny.services.mapper.RouterRoleMapper;
import cn.bunny.services.mapper.UserRoleMapper;
import cn.bunny.services.service.RoleService;
import cn.bunny.services.utils.RoleUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -49,7 +49,7 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
private RouterRoleMapper routerRoleMapper;
@Autowired
private RoleUtil roleUtil;
private RoleFactory roleFactory;
/**
* * 角色 服务实现类
@ -118,7 +118,7 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
// 找到所有和当前更新角色相同的用户并更新Redis中用户信息
List<Long> userIds = userRoleMapper.selectList(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getRoleId, dto.getId()))
.stream().map(UserRole::getUserId).toList();
roleUtil.updateUserRedisInfo(userIds);
roleFactory.updateUserRedisInfo(userIds);
}

View File

@ -15,13 +15,13 @@ import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.router.RouterManageVo;
import cn.bunny.dao.vo.system.router.RouterMeta;
import cn.bunny.dao.vo.system.router.UserRouterVo;
import cn.bunny.services.factory.RouterServiceFactory;
import cn.bunny.services.mapper.RoleMapper;
import cn.bunny.services.mapper.RolePowerMapper;
import cn.bunny.services.mapper.RouterMapper;
import cn.bunny.services.mapper.RouterRoleMapper;
import cn.bunny.services.security.custom.CustomCheckIsAdmin;
import cn.bunny.services.service.RouterService;
import cn.bunny.services.utils.RoleUtil;
import cn.bunny.services.utils.RouterServiceUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -46,7 +46,7 @@ import java.util.stream.Collectors;
@Transactional
public class RouterServiceImpl extends ServiceImpl<RouterMapper, Router> implements RouterService {
@Autowired
private RouterServiceUtil routerServiceUtil;
private RouterServiceFactory routerServiceFactory;
@Autowired
private RoleMapper roleMapper;
@ -84,7 +84,7 @@ public class RouterServiceImpl extends ServiceImpl<RouterMapper, Router> impleme
List<UserRouterVo> list = new ArrayList<>();
// 查询用户角色判断是否是管理员角色
boolean isAdmin = RoleUtil.checkAdmin(userRoleCodeList);
boolean isAdmin = CustomCheckIsAdmin.checkAdmin(userRoleCodeList);
// 查询路由和角色对应关系
List<ViewRouterRole> routerRoleList = routerRoleMapper.viewRouterRolesWithAll();
@ -151,7 +151,7 @@ public class RouterServiceImpl extends ServiceImpl<RouterMapper, Router> impleme
// 构建树形结构
routerVoList.forEach(routerVo -> {
if (routerVo.getParentId() == 0) {
routerVo.setChildren(routerServiceUtil.handleGetChildrenWIthRouter(routerVo.getId(), routerVoList));
routerVo.setChildren(routerServiceFactory.handleGetChildrenWIthRouter(routerVo.getId(), routerVoList));
list.add(routerVo);
}
});

View File

@ -8,10 +8,10 @@ import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.dao.entity.system.UserRole;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.user.LoginVo;
import cn.bunny.services.factory.UserFactory;
import cn.bunny.services.mapper.UserMapper;
import cn.bunny.services.mapper.UserRoleMapper;
import cn.bunny.services.service.UserRoleService;
import cn.bunny.services.utils.UserUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
@ -38,7 +38,7 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
private UserRoleMapper userRoleMapper;
@Autowired
private UserUtil userUtil;
private UserFactory userFactory;
@Autowired
private UserMapper userMapper;
@ -94,7 +94,7 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
// 重新设置Redis中的用户存储信息vo对象
String username = adminUser.getUsername();
loginVo = userUtil.buildLoginUserVo(adminUser, readMeDay);
loginVo = userFactory.buildLoginUserVo(adminUser, readMeDay);
redisTemplate.opsForValue().set(RedisUserConstant.getAdminLoginInfoPrefix(username), loginVo, readMeDay, TimeUnit.DAYS);
}
}

View File

@ -10,24 +10,20 @@ import cn.bunny.dao.dto.system.files.FileUploadDto;
import cn.bunny.dao.dto.system.user.*;
import cn.bunny.dao.entity.log.UserLoginLog;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.dao.entity.system.EmailTemplate;
import cn.bunny.dao.entity.system.Role;
import cn.bunny.dao.entity.system.UserDept;
import cn.bunny.dao.enums.EmailTemplateEnums;
import cn.bunny.dao.enums.LoginEnums;
import cn.bunny.dao.views.ViewUserDept;
import cn.bunny.dao.vo.result.PageResult;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.files.FileInfoVo;
import cn.bunny.dao.vo.system.user.*;
import cn.bunny.services.factory.EmailFactory;
import cn.bunny.services.factory.UserFactory;
import cn.bunny.services.mapper.*;
import cn.bunny.services.service.FilesService;
import cn.bunny.services.service.UserService;
import cn.bunny.services.utils.UserUtil;
import cn.bunny.services.utils.email.ConcreteSenderEmailTemplate;
import cn.bunny.services.utils.login.DefaultLoginStrategy;
import cn.bunny.services.utils.login.EmailLoginStrategy;
import cn.bunny.services.utils.login.LoginContext;
import cn.bunny.services.utils.login.LoginStrategy;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -35,17 +31,15 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
@ -65,58 +59,31 @@ import java.util.concurrent.TimeUnit;
public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implements UserService {
@Autowired
private UserUtil userUtil;
private UserFactory userFactory;
@Autowired
private ConcreteSenderEmailTemplate concreteSenderEmailTemplate;
private EmailFactory emailFactory;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private FilesService filesService;
@Autowired
private UserDeptMapper userDeptMapper;
@Autowired
private UserRoleMapper userRoleMapper;
@Autowired
private UserLoginLogMapper userLoginLogMapper;
@Autowired
private EmailTemplateMapper emailTemplateMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private UserMapper userMapper;
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 前台用户登录接口
* 这里不用判断用户是否为空因为在登录时已经校验过了
* <p>
* 抛出异常使用自带的 UsernameNotFoundException 或者自己封装<br/>
* 但是这两个效果传入参数都是一样的所以全部使用 UsernameNotFoundException
* </p>
*
* @param loginDto 登录参数
* @return 登录后结果返回
*/
@Override
public LoginVo login(LoginDto loginDto, HttpServletResponse response) {
Long readMeDay = loginDto.getReadMeDay();
// 初始化登录策略如果有需要添加策略放在这里
HashMap<String, LoginStrategy> loginStrategyHashMap = new HashMap<>();
loginStrategyHashMap.put(LoginEnums.EMAIL_STRATEGY.getValue(), new EmailLoginStrategy(redisTemplate, userMapper));
loginStrategyHashMap.put(LoginEnums.default_STRATEGY.getValue(), new DefaultLoginStrategy(userMapper));
// 使用登录上下文调用登录策略
LoginContext loginContext = new LoginContext(loginStrategyHashMap);
AdminUser user = loginContext.executeStrategy(loginDto);
// 判断用户是否禁用
if (user.getStatus()) {
throw new UsernameNotFoundException(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED.getMessage());
}
return userUtil.buildLoginUserVo(user, readMeDay);
}
/**
* 登录发送邮件验证码
@ -126,10 +93,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
@Override
public void sendLoginEmail(@NotNull String email) {
// 查询验证码邮件模板
LambdaQueryWrapper<cn.bunny.dao.entity.system.EmailTemplate> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(cn.bunny.dao.entity.system.EmailTemplate::getIsDefault, true);
lambdaQueryWrapper.eq(cn.bunny.dao.entity.system.EmailTemplate::getType, EmailTemplateEnums.VERIFICATION_CODE.getType());
cn.bunny.dao.entity.system.EmailTemplate emailTemplate = emailTemplateMapper.selectOne(lambdaQueryWrapper);
LambdaQueryWrapper<EmailTemplate> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(EmailTemplate::getIsDefault, true);
lambdaQueryWrapper.eq(EmailTemplate::getType, EmailTemplateEnums.VERIFICATION_CODE.getType());
EmailTemplate emailTemplate = emailTemplateMapper.selectOne(lambdaQueryWrapper);
// 生成验证码
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(150, 48, 4, 2);
@ -144,7 +111,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
hashMap.put("#companyName#", "BunnyAdmin");
// 发送邮件
concreteSenderEmailTemplate.sendEmail(email, emailTemplate, hashMap);
emailFactory.sendEmailTemplate(email, emailTemplate, hashMap);
// 在Redis中存储验证码
redisTemplate.opsForValue().set(RedisUserConstant.getAdminUserEmailCodePrefix(email), emailCode, RedisUserConstant.REDIS_EXPIRATION_TIME, TimeUnit.MINUTES);
@ -165,7 +132,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
if (adminUser == null) throw new AuthCustomerException(ResultCodeEnum.USER_IS_EMPTY);
if (adminUser.getStatus()) throw new AuthCustomerException(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED);
LoginVo buildUserVo = userUtil.buildLoginUserVo(adminUser, dto.getReadMeDay());
LoginVo buildUserVo = userFactory.buildLoginUserVo(adminUser, dto.getReadMeDay());
RefreshTokenVo refreshTokenVo = new RefreshTokenVo();
BeanUtils.copyProperties(buildUserVo, refreshTokenVo);
@ -188,7 +155,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
// 查询用户信息
AdminUser adminUser = getOne(Wrappers.<AdminUser>lambdaQuery().eq(AdminUser::getId, userId));
UserLoginLog userLoginLog = userUtil.setUserLoginLog(adminUser, token, ipAddr, ipRegion, "logout");
UserLoginLog userLoginLog = userFactory.setUserLoginLog(adminUser, token, ipAddr, ipRegion, "logout");
userLoginLogMapper.insert(userLoginLog);
// 删除Redis中用户信息
@ -215,7 +182,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
UserVo userVo = new UserVo();
BeanUtils.copyProperties(user, userVo);
userVo.setAvatar(userUtil.checkGetUserAvatar(avatar));
userVo.setAvatar(userFactory.checkGetUserAvatar(avatar));
return userVo;
}
@ -230,19 +197,19 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
String password = dto.getPassword();
// 对密码加密
String encode = passwordEncoder.encode(password);
String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
AdminUser adminUser = getOne(Wrappers.<AdminUser>lambdaQuery().eq(AdminUser::getId, userId));
// 判断是否存在这个用户
if (adminUser == null) throw new AuthCustomerException(ResultCodeEnum.USER_IS_EMPTY);
// 判断新密码是否与旧密码相同
if (adminUser.getPassword().equals(encode))
if (adminUser.getPassword().equals(md5Password))
throw new AuthCustomerException(ResultCodeEnum.UPDATE_NEW_PASSWORD_SAME_AS_OLD_PASSWORD);
// 更新用户密码
adminUser = new AdminUser();
adminUser.setPassword(encode);
adminUser.setPassword(md5Password);
adminUser.setId(userId);
updateById(adminUser);
@ -277,7 +244,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
// 重新生成用户信息到Redis中
user.setAvatar(adminUser.getAvatar());
userUtil.buildUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME);
userFactory.buildUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME);
}
/**
@ -376,7 +343,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
if (user == null) throw new AuthCustomerException(ResultCodeEnum.USER_IS_EMPTY);
// 检查用户头像
dto.setAvatar(userUtil.checkPostUserAvatar(dto.getAvatar()));
dto.setAvatar(userFactory.checkPostUserAvatar(dto.getAvatar()));
// 更新用户
AdminUser adminUser = new AdminUser();
@ -386,7 +353,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
// 重新生成用户信息到Redis中
BeanUtils.copyProperties(dto, user);
userUtil.buildUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME);
userFactory.buildUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME);
}
/**
@ -405,7 +372,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
// 数据库中的密码
String dbPassword = adminUser.getPassword();
password = passwordEncoder.encode(password);
password = DigestUtils.md5DigestAsHex(password.getBytes());
// 判断数据库中密码是否和更新用户密码相同
if (dbPassword.equals(password)) throw new AuthCustomerException(ResultCodeEnum.NEW_PASSWORD_SAME_OLD_PASSWORD);
@ -434,7 +401,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
List<AdminUserVo> voList = page.getRecords().stream()
.map(adminUser -> {
// 如果存在用户头像则设置用户头像
String avatar = userUtil.checkGetUserAvatar(adminUser.getAvatar());
String avatar = userFactory.checkGetUserAvatar(adminUser.getAvatar());
AdminUserVo adminUserVo = new AdminUserVo();
BeanUtils.copyProperties(adminUser, adminUserVo);
@ -462,12 +429,12 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
@Override
public void addAdminUser(@Valid AdminUserAddDto dto) {
// 对密码加密
String encode = passwordEncoder.encode(dto.getPassword());
String md5Password = DigestUtils.md5DigestAsHex(dto.getPassword().getBytes());
// 保存数据
AdminUser adminUser = new AdminUser();
BeanUtils.copyProperties(dto, adminUser);
adminUser.setPassword(encode);
adminUser.setPassword(md5Password);
save(adminUser);
// 插入用户部门关系表
@ -521,7 +488,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
// 重新生成用户信息到Redis中
BeanUtils.copyProperties(dto, adminUser);
userUtil.buildUserVo(adminUser, RedisUserConstant.REDIS_EXPIRATION_TIME);
userFactory.buildUserVo(adminUser, RedisUserConstant.REDIS_EXPIRATION_TIME);
}
/**

View File

@ -1,84 +0,0 @@
package cn.bunny.services.utils;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.dao.constant.RedisUserConstant;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.services.mapper.UserMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class RoleUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private UserMapper userMapper;
@Autowired
private UserUtil userUtil;
/**
* 判断是否是管理员
*
* @param roleList 角色代码列表
* @param permissions 权限列表
* @param adminUser 用户信息
* @return 是否是管理员
*/
public static boolean checkAdmin(List<String> roleList, List<String> permissions, AdminUser adminUser) {
// 判断是否是超级管理员
boolean isIdAdmin = adminUser.getId().equals(1L);
boolean isAdmin = roleList.stream().anyMatch(role -> role.equals("admin"));
// 判断是否是 admin
if (isIdAdmin || isAdmin) {
roleList.add("admin");
permissions.add("*");
permissions.add("*::*");
permissions.add("*::*::*");
return true;
}
return false;
}
/**
* 判断是否是管理员
*
* @param roleList 角色代码列表
* @return 是否是管理员
*/
public static boolean checkAdmin(List<String> roleList) {
// 判断是否是超级管理员
if (BaseContext.getUserId().equals(1L)) return true;
// 判断是否是 admin
return roleList.stream().anyMatch(role -> role.equals("admin"));
}
/**
* 批量更新Redis中用户信息
*
* @param userIds 用户Id列表
*/
public void updateUserRedisInfo(List<Long> userIds) {
// 根据Id查找所有用户
List<AdminUser> adminUsers = userMapper.selectList(Wrappers.<AdminUser>lambdaQuery().in(AdminUser::getId, userIds));
// 用户为空时不更新Redis的key
if (adminUsers.isEmpty()) return;
// 更新Redis中用户信息
adminUsers.stream().filter(user -> {
String adminLoginInfoPrefix = RedisUserConstant.getAdminLoginInfoPrefix(user.getUsername());
Object object = redisTemplate.opsForValue().get(adminLoginInfoPrefix);
return object != null;
}).forEach(user -> userUtil.buildUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME));
}
}

View File

@ -1,28 +0,0 @@
package cn.bunny.services.utils.email;
import cn.bunny.dao.entity.system.EmailTemplate;
import cn.bunny.services.mapper.EmailTemplateMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@Component
public class ConcreteSenderEmailTemplate extends AbstractSenderEmailTemplate {
@Autowired
private EmailTemplateMapper emailTemplateMapper;
/**
* 发送邮件模板
* 根据邮件模板发送邮件
*
* @param email 邮件
* @param emailTemplateId 模板Id
* @param params 替换参数
*/
public void sendEmailTemplate(String email, Long emailTemplateId, HashMap<String, Object> params) {
EmailTemplate emailTemplate = emailTemplateMapper.selectOne(Wrappers.<EmailTemplate>lambdaQuery().eq(EmailTemplate::getId, emailTemplateId));
sendEmail(email, emailTemplate, params);
}
}

View File

@ -1,34 +0,0 @@
package cn.bunny.services.utils.login;
import cn.bunny.dao.dto.system.user.LoginDto;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.services.mapper.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
/**
* 使用用户名登录
*/
public class DefaultLoginStrategy implements LoginStrategy {
private final UserMapper userMapper;
public DefaultLoginStrategy(UserMapper userMapper) {
this.userMapper = userMapper;
}
/**
* 登录鉴定方法
* 默认登录方式使用用户名查询用户
*
* @param loginDto 登录参数
* @return 鉴定身份验证
*/
@Override
public AdminUser authenticate(LoginDto loginDto) {
String username = loginDto.getUsername();
// 查询用户相关内容
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AdminUser::getUsername, username);
return userMapper.selectOne(queryWrapper);
}
}

View File

@ -1,56 +0,0 @@
package cn.bunny.services.utils.login;
import cn.bunny.dao.constant.RedisUserConstant;
import cn.bunny.dao.dto.system.user.LoginDto;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.services.mapper.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* 邮箱登录策略
*/
public class EmailLoginStrategy implements LoginStrategy {
private final RedisTemplate<String, Object> redisTemplate;
private final UserMapper userMapper;
public EmailLoginStrategy(RedisTemplate<String, Object> redisTemplate, UserMapper userMapper) {
this.redisTemplate = redisTemplate;
this.userMapper = userMapper;
}
/**
* 登录鉴定方法
* 只要判断邮箱验证码是否相等在前面已经验证过用户密码
* <p>
* 抛出异常类型 UsernameNotFoundException 如果要自定义状态码需要使用 HttpServletResponse
* 有封装好的 ResponseUtil.out() 方法
* </p>
*
* @param loginDto 登录参数
* @return 鉴定身份验证
*/
@Override
public AdminUser authenticate(LoginDto loginDto) {
String username = loginDto.getUsername();
String emailCode = loginDto.getEmailCode().toLowerCase();
// 查找Redis中的验证码
Object redisEmailCode = redisTemplate.opsForValue().get(RedisUserConstant.getAdminUserEmailCodePrefix(username));
if (redisEmailCode == null) {
throw new UsernameNotFoundException(ResultCodeEnum.EMAIL_CODE_EMPTY.getMessage());
}
// 判断用户邮箱验证码是否和Redis中发送的验证码
if (!emailCode.equals(redisEmailCode.toString().toLowerCase())) {
throw new UsernameNotFoundException(ResultCodeEnum.EMAIL_CODE_NOT_MATCHING.getMessage());
}
// 查询用户相关内容
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AdminUser::getEmail, username);
return userMapper.selectOne(queryWrapper);
}
}

View File

@ -1,34 +0,0 @@
package cn.bunny.services.utils.login;
import cn.bunny.dao.dto.system.user.LoginDto;
import cn.bunny.dao.entity.system.AdminUser;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.Map;
/**
* 登录策略上下文
*/
public class LoginContext {
private final Map<String, LoginStrategy> strategies;
public LoginContext(Map<String, LoginStrategy> strategies) {
this.strategies = strategies;
}
/**
* 执行登录策略
* 根据情况判断 type 是否为空
*
* @param loginDto 登录参数
* @return 用户
*/
public AdminUser executeStrategy(LoginDto loginDto) {
String type = loginDto.getType();
LoginStrategy strategy = strategies.get(type);
if (strategy == null) {
throw new UsernameNotFoundException("不支持登录类型: " + type);
}
return strategy.authenticate(loginDto);
}
}

View File

@ -1,18 +0,0 @@
package cn.bunny.services.utils.login;
import cn.bunny.dao.dto.system.user.LoginDto;
import cn.bunny.dao.entity.system.AdminUser;
/**
* 登录策略
*/
public interface LoginStrategy {
/**
* 登录鉴定方法
*
* @param loginDto 登录参数
* @return 鉴定身份验证
*/
AdminUser authenticate(LoginDto loginDto);
}

View File

@ -1,10 +1,10 @@
_ _
| |__ _ _ _ __ _ __ _ _ (_) __ ___ ____ _
| '_ \| | | | '_ \| '_ \| | | | | |/ _` \ \ / / _` |
| |_) | |_| | | | | | | | |_| | | | (_| |\ V | (_| |
|_.__/ \__,_|_| |_|_| |_|\__, | _/ |\__,_| \_/ \__,_|
|___/ |__/
${spring-boot.formatted-version}
${application.title}
${AnsiColor.BRIGHT_GREEN}
SpringBoot Version: ${spring-boot.version}${spring-boot.formatted-version}
${AnsiColor.BLACK}

View File

@ -1,19 +0,0 @@
package cn.bunny.services.service.impl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
@SpringBootTest
class UserServiceImplTest {
@Autowired
private PasswordEncoder passwordEncoder;
@Test
void updateUserPasswordByAdmin() {
String encode = passwordEncoder.encode("123456");
System.out.println(encode);
}
}

View File

@ -1,22 +0,0 @@
package cn.bunny.services.utils.email;
import cn.bunny.dao.entity.system.EmailTemplate;
import cn.bunny.services.mapper.EmailTemplateMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
@SpringBootTest
class ConcreteSenderEmailTemplateTest extends AbstractSenderEmailTemplate {
@Autowired
private EmailTemplateMapper emailTemplateMapper;
@Test
void sendEmailTemplate() {
EmailTemplate emailTemplate = emailTemplateMapper.selectOne(Wrappers.<EmailTemplate>lambdaQuery().eq(EmailTemplate::getId, 1791870020197625858L));
sendEmail("1319900154@qq.com", emailTemplate, new HashMap<>());
}
}