Files
gym-manage/docs/design/technical/SEC-安全设计.md
T
2026-03-08 21:47:09 +08:00

627 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 健身房管理系统安全设计文档
> 文档编号:GYM-SEC-DESIGN-001
> 版本:v1.0
> 创建日期:2026-03-08
> 最后更新日期:2026-03-08
> 作者:张翔
> 状态:正式发布
## 文档修订历史
| 版本 | 日期 | 作者 | 修订内容 |
| ---- | ---------- | ---- | -------- |
| v1.0 | 2026-03-08 | 张翔 | 创建安全设计文档 |
## 参考文档
- OWASP Top 10 安全规范
- Spring Security 官方文档
- GDPR 数据保护条例
- 网络安全等级保护 2.0
---
## 一、安全架构设计
### 1.1 安全分层
```
┌─────────────────────────────────────┐
│ 应用层安全 │
│ (认证、授权、输入验证、输出编码) │
├─────────────────────────────────────┤
│ 数据层安全 │
│ (加密、脱敏、审计、备份) │
├─────────────────────────────────────┤
│ 基础设施安全 │
│ (网络安全、主机安全、容器安全) │
└─────────────────────────────────────┘
```
### 1.2 安全原则
1. **纵深防御**:多层安全防护
2. **最小权限**:只授予必要权限
3. **默认安全**:默认配置即安全
4. **零信任**:始终验证,永不信任
5. **安全审计**:所有操作可追溯
---
## 二、认证与授权
### 2.1 认证机制
#### 2.1.1 JWT Token 认证
**Token 生成**
```java
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String secretKey;
@Value("${jwt.expiration}")
private long expiration;
public String generateToken(Authentication auth) {
UserPrincipal principal = (UserPrincipal) auth.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expiration);
return Jwts.builder()
.setSubject(principal.getId().toString())
.claim("tenantId", principal.getTenantId())
.claim("storeId", principal.getStoreId())
.claim("roles", principal.getRoles())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, secretKey)
.compact();
}
}
```
**Token 验证**
```java
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Authentication auth = tokenProvider.getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(auth);
}
} catch (Exception ex) {
logger.error("Could not set user authentication", ex);
}
filterChain.doFilter(request, response);
}
}
```
#### 2.1.2 Token 刷新机制
**双 Token 机制**
- Access Token:有效期 2 小时
- Refresh Token:有效期 7 天
**刷新流程**
```java
@PostMapping("/refresh")
public Mono<ApiResponse<TokenResponse>> refreshToken(
@RequestBody RefreshTokenRequest request) {
return authService.refreshToken(request.getRefreshToken())
.map(tokens -> ApiResponse.success(tokens));
}
```
### 2.2 授权机制
#### 2.2.1 基于角色的访问控制 (RBAC)
**角色定义**
```java
public enum Role {
SUPER_ADMIN, // 超级管理员
TENANT_ADMIN, // 租户管理员
STORE_MANAGER, // 店长
COACH, // 教练
MEMBER // 会员
}
```
**权限配置**
```java
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterFilterChain securityFilterChain(
ServerHttpSecurity http) {
http
.authorizeExchange(exchanges -> exchanges
.pathMatchers("/api/v1/auth/**").permitAll()
.pathMatchers("/api/v1/admin/**").hasRole("ADMIN")
.pathMatchers("/api/v1/members/**").hasAnyRole("ADMIN", "COACH")
.pathMatchers("/api/v1/my/**").authenticated()
.anyExchange().permitAll()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}
}
```
#### 2.2.2 数据权限隔离
**租户隔离**
```java
@Component
public class TenantInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
String tenantId = request.getHeader("X-Tenant-ID");
if (StringUtils.isEmpty(tenantId)) {
throw new UnauthorizedException("缺少租户标识");
}
TenantContext.setTenantId(tenantId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
TenantContext.clear();
}
}
```
---
## 三、数据安全
### 3.1 数据加密
#### 3.1.1 敏感数据加密存储
**加密算法**AES-256-GCM
**加密工具类**
```java
@Component
public class EncryptionUtil {
@Value("${encryption.key}")
private String encryptionKey;
public String encrypt(String plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(
encryptionKey.getBytes(), "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(
128, generateIV());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
byte[] cipherText = cipher.doFinal(plaintext.getBytes());
return Base64.getEncoder().encodeToString(cipherText);
}
public String decrypt(String cipherText) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(
encryptionKey.getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec,
new GCMParameterSpec(128, generateIV()));
byte[] plainText = cipher.doFinal(
Base64.getDecoder().decode(cipherText));
return new String(plainText);
}
}
```
**加密字段**
- 手机号
- 身份证号
- 银行卡号
- 地址
#### 3.1.2 密码加密
**BCrypt 加密**
```java
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
```
### 3.2 数据脱敏
#### 3.2.1 脱敏规则
**手机号脱敏**
```java
public class DesensitizationUtil {
public static String maskPhone(String phone) {
if (StringUtils.isEmpty(phone)) {
return "";
}
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
public static String maskIdCard(String idCard) {
if (StringUtils.isEmpty(idCard)) {
return "";
}
return idCard.replaceAll("(\\d{6})\\d{8}(\\d{4})", "$1********$2");
}
}
```
#### 3.2.2 JSON 序列化脱敏
**自定义注解**
```java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerializer.class)
public @interface Desensitization {
DesensitizationType type();
}
public enum DesensitizationType {
PHONE, // 手机号
ID_CARD, // 身份证
BANK_CARD, // 银行卡
ADDRESS // 地址
}
```
**使用示例**
```java
public class MemberDTO {
@Desensitization(type = DesensitizationType.PHONE)
private String phone;
@Desensitization(type = DesensitizationType.ID_CARD)
private String idCard;
}
```
### 3.3 数据备份
#### 3.3.1 备份策略
**全量备份**
- 频率:每天凌晨 2 点
- 保留:最近 30 天
- 存储:异地灾备中心
**增量备份**
- 频率:每小时
- 保留:最近 7 天
- 存储:本地高速存储
#### 3.3.2 恢复演练
- 频率:每季度一次
- 范围:随机抽取 10% 数据
- 验证:数据完整性校验
---
## 四、网络安全
### 4.1 HTTPS 强制
**配置**
```yaml
server:
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: ${SSL_KEY_PASSWORD}
key-store-type: PKCS12
```
**HTTP 重定向**
```java
@Bean
public SecurityWebFilterFilterChain securityFilterChain(
ServerHttpSecurity http) {
http
.redirectHttpsRedirect(Customizer.withDefaults());
return http.build();
}
```
### 4.2 CORS 配置
**跨域配置**
```java
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://yourdomain.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
};
}
```
### 4.3 限流与防 DDOS
**限流配置**
```yaml
resilience4j:
ratelimiter:
instances:
apiRateLimiter:
limit-for-period: 100
limit-refresh-period: 1s
timeout-duration: 0
loginRateLimiter:
limit-for-period: 5
limit-refresh-period: 1m
timeout-duration: 0
```
**IP 黑名单**
```java
@Component
public class IpBlacklistFilter implements Filter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) {
String ip = getClientIp(request);
if (isBlacklisted(ip)) {
((HttpServletResponse) response).sendError(403);
return;
}
chain.doFilter(request, response);
}
}
```
---
## 五、输入验证与输出编码
### 5.1 输入验证
**请求体验证**
```java
public class CreateMemberRequest {
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@NotBlank(message = "姓名不能为空")
@Size(min = 1, max = 50, message = "姓名长度不能超过 50 个字符")
private String name;
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 0, message = "年龄不能小于 0")
@Max(value = 150, message = "年龄不能大于 150")
private Integer age;
}
```
**SQL 注入防护**
```java
// ❌ 错误示例
@Query("SELECT m FROM Member m WHERE m.phone = :phone")
Member findByPhone(@Param("phone") String phone);
// ✅ 正确示例(使用参数化查询)
@Query("SELECT m FROM Member m WHERE m.phone = :phone")
Member findByPhone(@Param("phone") String phone);
```
**XSS 防护**
```java
@Component
public class XssFilter implements Filter {
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
XssHttpServletRequestWrapper xssRequest =
new XssHttpServletRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
}
```
### 5.2 输出编码
**HTML 编码**
```java
public class HtmlUtil {
public static String escapeHtml(String html) {
return StringEscapeUtils.escapeHtml4(html);
}
}
```
---
## 六、安全审计
### 6.1 审计日志
**审计内容**
- 登录/登出
- 创建/更新/删除操作
- 数据导出
- 权限变更
**日志格式**
```json
{
"timestamp": "2026-03-08T10:30:00Z",
"userId": 1,
"action": "CREATE_MEMBER",
"resource": "member",
"resourceId": 123,
"ip": "192.168.1.100",
"userAgent": "Mozilla/5.0...",
"result": "SUCCESS",
"details": {...}
}
```
**审计注解**
```java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuditLog {
String action();
String resource();
}
```
**使用示例**
```java
@AuditLog(action = "CREATE", resource = "member")
public Mono<Member> createMember(CreateMemberRequest request) {
return memberRepository.save(request.toEntity());
}
```
### 6.2 日志存储
**存储策略**
- 热存储:最近 30 天,Elasticsearch
- 冷存储:30-180 天,对象存储
- 归档:180 天以上,磁带库
**日志保护**
- 完整性:数字签名
- 机密性:加密存储
- 可用性:多副本备份
---
## 七、安全监控
### 7.1 监控指标
**认证监控**
- 登录成功率
- 登录失败次数
- Token 刷新率
- 异常登录行为
**授权监控**
- 权限拒绝次数
- 越权访问尝试
- 敏感操作频率
**数据监控**
- 敏感数据访问
- 大批量数据导出
- 异常数据修改
### 7.2 告警规则
**告警级别**
- P0(紧急):系统被入侵、数据泄露
- P1(严重):大规模认证失败、DDOS 攻击
- P2(警告):异常登录行为、权限异常
- P3(提示):配置变更、版本升级
**告警渠道**
- 短信:P0、P1
- 邮件:P1、P2
- 钉钉/企业微信:P2、P3
---
## 八、合规性
### 8.1 GDPR 合规
**数据主体权利**
- 知情权:明确告知数据收集目的
- 访问权:用户可查询个人数据
- 更正权:用户可修改个人数据
- 删除权:用户可申请删除数据
- 可携带权:支持数据导出
**数据保护措施**
- 数据最小化:只收集必要数据
- 目的限制:仅用于声明的目的
- 存储限制:到期自动删除
- 安全保障:加密、访问控制
### 8.2 等保 2.0 合规
**技术要求**
- 身份鉴别:多因素认证
- 访问控制:最小权限原则
- 安全审计:操作可追溯
- 入侵防范:实时监测告警
- 数据完整性:校验和验证
- 数据保密性:加密传输存储
**管理要求**
- 安全管理制度
- 安全管理机构
- 人员安全管理
- 系统建设管理
- 系统运维管理
---
**文档结束**