Initial commit

This commit is contained in:
2025-11-05 23:30:19 +08:00
commit 1910732965
107 changed files with 10748 additions and 0 deletions
+190
View File
@@ -0,0 +1,190 @@
spring:
application:
name: auth-system
# 环境配置(默认开发环境)
profiles:
active: dev
# 数据源配置
datasource:
url: jdbc:mysql://152.32.168.79:3306/auth?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: auth_user
password: auth_pwd
driver-class-name: com.mysql.cj.jdbc.Driver
# HikariCP 连接池配置
hikari:
# 连接池名称
pool-name: AuthSystemHikariCP
# 最小空闲连接数
minimum-idle: 5
# 最大连接数
maximum-pool-size: 20
# 连接超时时间(毫秒)
connection-timeout: 30000
# 空闲连接超时时间(毫秒)
idle-timeout: 600000
# 连接最大存活时间(毫秒)
max-lifetime: 1800000
# 连接测试查询
connection-test-query: SELECT 1
# Jackson配置
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
# Redis配置
data:
redis:
host: 152.32.168.79
port: 6379
password: 20001201tds
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
# 邮件配置
mail:
host: smtp.qq.com
port: 587
username: kurihada@qq.com
password: objliupbasxvbeig
default-encoding: UTF-8
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
ssl:
enable: false
socketFactory:
port: 587
class: javax.net.ssl.SSLSocketFactory
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
# 服务器配置
server:
port: 8001
# JWT配置
jwt:
secret: authSystemSecretKeyForJWTTokenGenerationAndValidation2024
expiration: 3600000 # Access Token: 1小时(毫秒)
refresh-expiration: 604800000 # Refresh Token: 7天(毫秒)
header: Authorization
prefix: Bearer
# 限流配置
rate-limit:
# 登录接口限流
login:
enabled: true
window: 60 # 时间窗口:60秒
max-requests: 5 # 最大请求次数:5次
message: "登录请求过于频繁,请稍后再试"
# 注册接口限流
register:
enabled: true
window: 300 # 时间窗口:5分钟
max-requests: 3 # 最大请求次数:3次
message: "注册请求过于频繁,请稍后再试"
# 刷新Token限流
refresh:
enabled: true
window: 60 # 时间窗口:60秒
max-requests: 10 # 最大请求次数:10次
message: "刷新Token请求过于频繁,请稍后再试"
# 密码策略配置
password:
# 最小长度
min-length: 8
# 最大长度
max-length: 32
# 是否要求包含小写字母
require-lowercase: true
# 是否要求包含大写字母
require-uppercase: true
# 是否要求包含数字
require-digit: true
# 是否要求包含特殊字符
require-special-char: true
# 允许的特殊字符
special-chars: "!@#$%^&*()_+-=[]{}|;:,.<>?"
# 是否禁止常见弱密码
forbid-common-passwords: true
# 是否禁止包含用户名
forbid-username: true
# 密码重置token过期时间(秒)- 默认1小时
reset-token-expiration: 3600
# 权限缓存配置
permission-cache:
# 是否启用权限缓存
enabled: true
# 用户权限缓存过期时间(秒)- 默认2小时
user-permission-ttl: 7200
# 角色权限缓存过期时间(秒)- 默认1小时
role-permission-ttl: 3600
# 用户信息缓存过期时间(秒)- 默认30分钟
user-info-ttl: 1800
# 用户注册配置
registration:
# 是否为新用户自动分配默认角色
auto-assign-default-role: true
# 默认角色代码
default-role-code: USER
# 是否要求邮箱验证(如果为true,未验证邮箱的用户无法登录)
require-email-verification: true
# 邮箱验证配置
email:
# 验证token过期时间(秒)- 默认24小时
verification-token-expiration: 86400
# 账户锁定配置
account-lock:
# 最大登录失败次数(默认5次)
max-failed-attempts: 5
# 账户锁定时长(分钟,默认30分钟)
lock-duration-minutes: 30
# MyBatis Plus配置
mybatis-plus:
configuration:
# 开启驼峰命名映射
map-underscore-to-camel-case: true
# 日志实现
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 主键类型:AUTO-数据库自增
id-type: auto
# 逻辑删除字段名
logic-delete-field: deleted
# 逻辑删除值
logic-delete-value: 1
# 逻辑未删除值
logic-not-delete-value: 0
# mapper xml文件位置
mapper-locations: classpath*:/mapper/**/*.xml
# 实体类包路径
type-aliases-package: com.kurihada.auth.entity
# 日志配置
logging:
level:
com.kurihada.auth: debug
org.springframework.security: debug
com.baomidou.mybatisplus: debug
@@ -0,0 +1,259 @@
-- ========================================
-- 认证系统数据库初始化脚本
-- 版本: 1.0
-- 说明: 包含所有表的创建和初始数据
-- ========================================
-- ========================================
-- 1. 租户表
-- ========================================
CREATE TABLE IF NOT EXISTS `tenants` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '租户ID',
`code` VARCHAR(50) NOT NULL COMMENT '租户代码(唯一标识)',
`name` VARCHAR(100) NOT NULL COMMENT '租户名称',
`contact_name` VARCHAR(50) COMMENT '联系人',
`contact_phone` VARCHAR(20) COMMENT '联系电话',
`contact_email` VARCHAR(100) COMMENT '联系邮箱',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(0:禁用,1:启用)',
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)',
`expire_time` DATETIME COMMENT '过期时间',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`),
KEY `idx_status` (`status`),
KEY `idx_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户表';
-- ========================================
-- 2. 用户表(整合了账户锁定和邮箱验证功能)
-- ========================================
CREATE TABLE IF NOT EXISTS `users` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`password` VARCHAR(255) NOT NULL COMMENT '密码(加密后)',
`tenant_id` BIGINT COMMENT '租户ID',
`real_name` VARCHAR(50) COMMENT '真实姓名',
`email` VARCHAR(100) COMMENT '邮箱',
`phone` VARCHAR(20) COMMENT '手机号',
`gender` TINYINT COMMENT '性别(0:女,1:男,2:未知)',
`avatar` VARCHAR(255) COMMENT '头像URL',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(0:禁用,1:启用)',
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)',
`last_login_time` DATETIME COMMENT '最后登录时间',
-- 账户锁定相关字段
`login_failed_count` INT DEFAULT 0 COMMENT '登录失败次数',
`account_locked_at` DATETIME NULL COMMENT '账户锁定时间',
`account_unlock_at` DATETIME NULL COMMENT '账户解锁时间',
-- 邮箱验证相关字段
`email_verified` TINYINT(1) DEFAULT 0 COMMENT '邮箱是否已验证(0:未验证,1:已验证)',
`email_verified_at` DATETIME DEFAULT NULL COMMENT '邮箱验证时间',
-- 时间戳字段
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_status_deleted` (`status`, `deleted`),
KEY `idx_phone` (`phone`),
KEY `idx_create_time` (`create_time`),
KEY `idx_last_login_time` (`last_login_time`),
KEY `idx_account_locked_at` (`account_locked_at`),
KEY `idx_account_unlock_at` (`account_unlock_at`),
KEY `idx_email_verified` (`email_verified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- ========================================
-- 3. 角色表
-- ========================================
CREATE TABLE IF NOT EXISTS `roles` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`name` VARCHAR(50) NOT NULL COMMENT '角色名称',
`code` VARCHAR(50) NOT NULL COMMENT '角色代码',
`tenant_id` BIGINT COMMENT '租户ID',
`description` VARCHAR(255) COMMENT '角色描述',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(0:禁用,1:启用)',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code_tenant` (`code`, `tenant_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_status` (`status`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
-- ========================================
-- 4. 权限表
-- ========================================
CREATE TABLE IF NOT EXISTS `permissions` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '权限ID',
`name` VARCHAR(50) NOT NULL COMMENT '权限名称',
`code` VARCHAR(100) NOT NULL COMMENT '权限代码',
`tenant_id` BIGINT COMMENT '租户IDnull表示公共权限)',
`description` VARCHAR(255) COMMENT '权限描述',
`type` VARCHAR(20) COMMENT '权限类型(menu:菜单,button:按钮,api:接口)',
`resource` VARCHAR(255) COMMENT '资源路径(URL或方法)',
`parent_id` BIGINT COMMENT '父权限ID',
`sort` INT DEFAULT 0 COMMENT '排序',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(0:禁用,1:启用)',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code_tenant` (`code`, `tenant_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_parent_id` (`parent_id`),
KEY `idx_type` (`type`),
KEY `idx_status` (`status`),
KEY `idx_sort` (`sort`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';
-- ========================================
-- 5. 用户角色关联表
-- ========================================
CREATE TABLE IF NOT EXISTS `user_roles` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`role_id` BIGINT NOT NULL COMMENT '角色ID',
`tenant_id` BIGINT COMMENT '租户ID',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_role_tenant` (`user_id`, `role_id`, `tenant_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_role_id` (`role_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';
-- ========================================
-- 6. 角色权限关联表
-- ========================================
CREATE TABLE IF NOT EXISTS `role_permissions` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',
`role_id` BIGINT NOT NULL COMMENT '角色ID',
`permission_id` BIGINT NOT NULL COMMENT '权限ID',
`tenant_id` BIGINT COMMENT '租户ID',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_role_permission_tenant` (`role_id`, `permission_id`, `tenant_id`),
KEY `idx_role_id` (`role_id`),
KEY `idx_permission_id` (`permission_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';
-- ========================================
-- 7. Refresh Token 表
-- ========================================
CREATE TABLE IF NOT EXISTS `refresh_tokens` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`token` VARCHAR(100) NOT NULL COMMENT 'Refresh Token',
`tenant_id` BIGINT COMMENT '租户ID',
`expire_time` DATETIME NOT NULL COMMENT '过期时间',
`used` TINYINT NOT NULL DEFAULT 0 COMMENT '是否已使用(0:未使用,1:已使用)',
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_token` (`token`),
KEY `idx_user_id` (`user_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_expire_time` (`expire_time`),
KEY `idx_used` (`used`),
KEY `idx_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Refresh Token表';
-- ========================================
-- 8. 审计日志表
-- ========================================
CREATE TABLE IF NOT EXISTS `audit_log` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`action_type` VARCHAR(50) NOT NULL COMMENT '操作类型(LOGIN, LOGOUT, CREATE_USER, UPDATE_USER, DELETE_USER, ASSIGN_ROLE, REMOVE_ROLE, etc.',
`user_id` BIGINT COMMENT '操作用户ID',
`username` VARCHAR(50) COMMENT '操作用户名',
`tenant_id` BIGINT NOT NULL COMMENT '租户ID',
`resource_type` VARCHAR(50) COMMENT '资源类型(USER, ROLE, PERMISSION',
`resource_id` VARCHAR(100) COMMENT '资源ID',
`details` TEXT COMMENT '操作详情',
`result` VARCHAR(20) NOT NULL COMMENT '操作结果(SUCCESS/FAILURE',
`error_message` TEXT COMMENT '错误信息(如果失败)',
`ip_address` VARCHAR(50) COMMENT '请求IP地址',
`user_agent` VARCHAR(500) COMMENT 'User Agent',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
INDEX `idx_user_id` (`user_id`),
INDEX `idx_username` (`username`),
INDEX `idx_tenant_id` (`tenant_id`),
INDEX `idx_action_type` (`action_type`),
INDEX `idx_result` (`result`),
INDEX `idx_create_time` (`create_time`),
INDEX `idx_composite_user_time` (`user_id`, `create_time`),
INDEX `idx_composite_action_result` (`action_type`, `result`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审计日志表 - 记录系统中的所有重要操作';
-- ========================================
-- 初始化数据
-- ========================================
-- 插入租户数据
INSERT INTO `tenants` (`code`, `name`, `contact_name`, `status`)
VALUES ('default', '默认租户', '系统管理员', 1);
INSERT INTO `tenants` (`code`, `name`, `contact_name`, `contact_phone`, `contact_email`, `status`)
VALUES
('company_a', 'A公司', '张三', '13800138000', 'zhangsan@companya.com', 1),
('company_b', 'B公司', '李四', '13900139000', 'lisi@companyb.com', 1);
-- 插入用户数据
-- 密码: admin123 (BCrypt加密)
INSERT INTO `users` (`username`, `password`, `tenant_id`, `real_name`, `email`, `status`)
VALUES
('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', 1, '系统管理员', 'admin@example.com', 1),
('user', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', 1, '普通用户', 'user@example.com', 1);
-- 插入角色数据
INSERT INTO `roles` (`name`, `code`, `tenant_id`, `description`, `status`)
VALUES
('超级管理员', 'ADMIN', 1, '拥有所有权限', 1),
('普通用户', 'USER', 1, '基础用户权限', 1);
-- 插入权限数据(公共权限,tenant_id 为 NULL
INSERT INTO `permissions` (`name`, `code`, `tenant_id`, `description`, `type`, `resource`, `parent_id`, `sort`, `status`)
VALUES
-- 系统管理
('系统管理', 'system:manage', NULL, '系统管理模块', 'menu', '/system', NULL, 1, 1),
-- 用户管理
('用户管理', 'user:manage', NULL, '用户管理模块', 'menu', '/system/user', 1, 1, 1),
('用户查看', 'user:read', NULL, '查看用户信息', 'api', 'GET:/api/users/**', 2, 1, 1),
('用户新增', 'user:create', NULL, '新增用户', 'api', 'POST:/api/users', 2, 2, 1),
('用户修改', 'user:update', NULL, '修改用户信息', 'api', 'PUT:/api/users/**', 2, 3, 1),
('用户删除', 'user:delete', NULL, '删除用户', 'api', 'DELETE:/api/users/**', 2, 4, 1),
-- 角色管理
('角色管理', 'role:manage', NULL, '角色管理模块', 'menu', '/system/role', 1, 2, 1),
('角色查看', 'role:read', NULL, '查看角色信息', 'api', 'GET:/api/roles/**', 7, 1, 1),
('角色新增', 'role:create', NULL, '新增角色', 'api', 'POST:/api/roles', 7, 2, 1),
('角色修改', 'role:update', NULL, '修改角色信息', 'api', 'PUT:/api/roles/**', 7, 3, 1),
('角色删除', 'role:delete', NULL, '删除角色', 'api', 'DELETE:/api/roles/**', 7, 4, 1),
-- 权限管理
('权限管理', 'permission:manage', NULL, '权限管理模块', 'menu', '/system/permission', 1, 3, 1),
('权限查看', 'permission:read', NULL, '查看权限信息', 'api', 'GET:/api/permissions/**', 12, 1, 1),
('权限新增', 'permission:create', NULL, '新增权限', 'api', 'POST:/api/permissions', 12, 2, 1),
('权限修改', 'permission:update', NULL, '修改权限信息', 'api', 'PUT:/api/permissions/**', 12, 3, 1),
('权限删除', 'permission:delete', NULL, '删除权限', 'api', 'DELETE:/api/permissions/**', 12, 4, 1);
-- 插入用户角色关联数据
INSERT INTO `user_roles` (`user_id`, `role_id`, `tenant_id`)
VALUES
(1, 1, 1), -- admin 用户 -> 超级管理员角色
(2, 2, 1); -- user 用户 -> 普通用户角色
-- 插入角色权限关联数据
-- 为超级管理员角色分配所有权限
INSERT INTO `role_permissions` (`role_id`, `permission_id`, `tenant_id`)
SELECT 1, id, 1 FROM `permissions` WHERE `status` = 1;
-- 为普通用户角色分配查看权限
INSERT INTO `role_permissions` (`role_id`, `permission_id`, `tenant_id`)
SELECT 2, id, 1 FROM `permissions` WHERE `code` IN ('user:read', 'role:read', 'permission:read');
+158
View File
@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kurihada.auth.mapper.UserMapper">
<!-- 结果映射:用户及其角色权限 -->
<resultMap id="UserWithRolesAndPermissionsMap" type="com.kurihada.auth.entity.User">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="tenant_id" property="tenantId"/>
<result column="real_name" property="realName"/>
<result column="email" property="email"/>
<result column="phone" property="phone"/>
<result column="gender" property="gender"/>
<result column="avatar" property="avatar"/>
<result column="status" property="status"/>
<result column="deleted" property="deleted"/>
<result column="email_verified" property="emailVerified"/>
<result column="email_verified_at" property="emailVerifiedAt"/>
<result column="login_failed_count" property="loginFailedCount"/>
<result column="account_locked_at" property="accountLockedAt"/>
<result column="account_unlock_at" property="accountUnlockAt"/>
<result column="last_login_time" property="lastLoginTime"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<!-- 角色集合 -->
<collection property="roles" ofType="com.kurihada.auth.entity.Role">
<id column="role_id" property="id"/>
<result column="role_name" property="name"/>
<result column="role_code" property="code"/>
<result column="role_tenant_id" property="tenantId"/>
<result column="role_description" property="description"/>
<result column="role_status" property="status"/>
<result column="role_create_time" property="createTime"/>
<result column="role_update_time" property="updateTime"/>
<!-- 权限集合 -->
<collection property="permissions" ofType="com.kurihada.auth.entity.Permission">
<id column="permission_id" property="id"/>
<result column="permission_name" property="name"/>
<result column="permission_code" property="code"/>
<result column="permission_tenant_id" property="tenantId"/>
<result column="permission_description" property="description"/>
<result column="permission_type" property="type"/>
<result column="permission_resource" property="resource"/>
<result column="permission_parent_id" property="parentId"/>
<result column="permission_sort" property="sort"/>
<result column="permission_status" property="status"/>
<result column="permission_create_time" property="createTime"/>
<result column="permission_update_time" property="updateTime"/>
</collection>
</collection>
</resultMap>
<!-- 查询用户及其所有角色和权限(一次查询) -->
<select id="selectUserWithRolesAndPermissions" resultMap="UserWithRolesAndPermissionsMap">
SELECT
u.id AS user_id,
u.username,
u.password,
u.tenant_id,
u.real_name,
u.email,
u.phone,
u.gender,
u.avatar,
u.status,
u.deleted,
u.email_verified,
u.email_verified_at,
u.login_failed_count,
u.account_locked_at,
u.account_unlock_at,
u.last_login_time,
u.create_time,
u.update_time,
r.id AS role_id,
r.name AS role_name,
r.code AS role_code,
r.tenant_id AS role_tenant_id,
r.description AS role_description,
r.status AS role_status,
r.create_time AS role_create_time,
r.update_time AS role_update_time,
p.id AS permission_id,
p.name AS permission_name,
p.code AS permission_code,
p.tenant_id AS permission_tenant_id,
p.description AS permission_description,
p.type AS permission_type,
p.resource AS permission_resource,
p.parent_id AS permission_parent_id,
p.sort AS permission_sort,
p.status AS permission_status,
p.create_time AS permission_create_time,
p.update_time AS permission_update_time
FROM users u
LEFT JOIN user_roles ur ON u.id = ur.user_id
LEFT JOIN roles r ON ur.role_id = r.id
LEFT JOIN role_permissions rp ON r.id = rp.role_id
LEFT JOIN permissions p ON rp.permission_id = p.id
WHERE u.username = #{username}
AND u.deleted = 0
</select>
<!-- 根据用户ID查询用户及其所有角色和权限 -->
<select id="selectUserWithRolesAndPermissionsById" resultMap="UserWithRolesAndPermissionsMap">
SELECT
u.id AS user_id,
u.username,
u.password,
u.tenant_id,
u.real_name,
u.email,
u.phone,
u.gender,
u.avatar,
u.status,
u.deleted,
u.email_verified,
u.email_verified_at,
u.login_failed_count,
u.account_locked_at,
u.account_unlock_at,
u.last_login_time,
u.create_time,
u.update_time,
r.id AS role_id,
r.name AS role_name,
r.code AS role_code,
r.tenant_id AS role_tenant_id,
r.description AS role_description,
r.status AS role_status,
r.create_time AS role_create_time,
r.update_time AS role_update_time,
p.id AS permission_id,
p.name AS permission_name,
p.code AS permission_code,
p.tenant_id AS permission_tenant_id,
p.description AS permission_description,
p.type AS permission_type,
p.resource AS permission_resource,
p.parent_id AS permission_parent_id,
p.sort AS permission_sort,
p.status AS permission_status,
p.create_time AS permission_create_time,
p.update_time AS permission_update_time
FROM users u
LEFT JOIN user_roles ur ON u.id = ur.user_id
LEFT JOIN roles r ON ur.role_id = r.id
LEFT JOIN role_permissions rp ON r.id = rp.role_id
LEFT JOIN permissions p ON rp.permission_id = p.id
WHERE u.id = #{userId}
AND u.deleted = 0
</select>
</mapper>
@@ -0,0 +1,166 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>邮箱验证</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
max-width: 600px;
margin: 40px auto;
background: #ffffff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
padding: 30px;
text-align: center;
}
.header h1 {
margin: 0;
font-size: 28px;
font-weight: 600;
}
.content {
padding: 40px 30px;
}
.greeting {
font-size: 18px;
font-weight: 500;
margin-bottom: 20px;
color: #2d3748;
}
.message {
font-size: 15px;
color: #4a5568;
margin-bottom: 30px;
line-height: 1.8;
}
.button-container {
text-align: center;
margin: 35px 0;
}
.verify-button {
display: inline-block;
padding: 14px 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
text-decoration: none;
border-radius: 8px;
font-weight: 600;
font-size: 16px;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.verify-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.5);
}
.alternative {
background: #f7fafc;
border-left: 4px solid #667eea;
padding: 20px;
margin: 25px 0;
border-radius: 4px;
}
.alternative p {
margin: 0 0 10px 0;
font-size: 14px;
color: #4a5568;
}
.link {
word-break: break-all;
color: #667eea;
text-decoration: none;
font-size: 13px;
}
.warning {
background: #fff5f5;
border-left: 4px solid #f56565;
padding: 15px;
margin: 25px 0;
border-radius: 4px;
}
.warning p {
margin: 0;
font-size: 14px;
color: #742a2a;
}
.footer {
background: #f7fafc;
padding: 25px 30px;
text-align: center;
border-top: 1px solid #e2e8f0;
}
.footer p {
margin: 5px 0;
font-size: 13px;
color: #718096;
}
.expiry {
text-align: center;
color: #718096;
font-size: 14px;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>✉️ 验证您的邮箱</h1>
</div>
<div class="content">
<div class="greeting">
您好,<span th:text="${username}">用户</span>
</div>
<div class="message">
<p>感谢您注册我们的认证系统!</p>
<p>为了确保账户安全并激活您的账号,请点击下方按钮验证您的邮箱地址:</p>
</div>
<div class="button-container">
<a th:href="${verificationUrl}" class="verify-button">
验证邮箱
</a>
</div>
<div class="expiry">
⏰ 此链接将在 <strong th:text="${validHours}">24</strong> 小时后过期
</div>
<div class="alternative">
<p><strong>如果按钮无法点击,请复制以下链接到浏览器:</strong></p>
<a th:href="${verificationUrl}" th:text="${verificationUrl}" class="link">
验证链接
</a>
</div>
<div class="warning">
<p>
<strong>⚠️ 安全提示:</strong>
如果您没有注册我们的服务,请忽略此邮件。您的账户安全不会受到影响。
</p>
</div>
</div>
<div class="footer">
<p>此邮件由系统自动发送,请勿直接回复</p>
<p>© 2025 认证系统. 保留所有权利</p>
</div>
</div>
</body>
</html>
@@ -0,0 +1,112 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>密码重置请求</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: #ffffff;
border-radius: 8px;
padding: 40px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.logo {
text-align: center;
margin-bottom: 30px;
}
h1 {
color: #2563eb;
font-size: 24px;
margin-bottom: 20px;
text-align: center;
}
.content {
margin-bottom: 30px;
}
.button {
display: inline-block;
padding: 12px 30px;
background-color: #2563eb;
color: #ffffff;
text-decoration: none;
border-radius: 5px;
font-weight: bold;
text-align: center;
}
.button-container {
text-align: center;
margin: 30px 0;
}
.warning {
background-color: #fef3c7;
border-left: 4px solid #f59e0b;
padding: 15px;
margin: 20px 0;
border-radius: 4px;
}
.footer {
text-align: center;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e5e7eb;
font-size: 12px;
color: #6b7280;
}
.link {
word-break: break-all;
color: #2563eb;
text-decoration: none;
}
</style>
</head>
<body>
<div class="container">
<div class="logo">
<h1>🔐 Auth System</h1>
</div>
<h1>密码重置请求</h1>
<div class="content">
<p>您好,<strong th:text="${username}">用户</strong></p>
<p>我们收到了您的密码重置请求。如果这不是您的操作,请忽略此邮件。</p>
<p>要重置您的密码,请点击下面的按钮:</p>
</div>
<div class="button-container">
<a th:href="${resetUrl}" class="button">重置密码</a>
</div>
<div class="content">
<p>或者复制以下链接到浏览器:</p>
<p><a th:href="${resetUrl}" th:text="${resetUrl}" class="link">重置链接</a></p>
</div>
<div class="warning">
<p><strong>⏰ 重要提示:</strong></p>
<ul>
<li>此链接将在 <span th:text="${validMinutes}">60</span> 分钟后失效</li>
<li>为了您的账号安全,请不要将此链接分享给他人</li>
<li>如果您没有请求重置密码,请忽略此邮件</li>
</ul>
</div>
<div class="footer">
<p>此邮件由系统自动发送,请勿直接回复。</p>
<p>© 2025 Auth System. All rights reserved.</p>
</div>
</div>
</body>
</html>