本文目录导读:

我来为您详细介绍Java分页安全流程规范,这是一个重要的企业级开发规范。
分页安全基础规范
1 统一分页参数规范
public class PageRequest {
// 最小页码为1
private static final int MIN_PAGE = 1;
// 最大每页条数限制
private static final int MAX_PAGE_SIZE = 100;
// 默认每页条数
private static final int DEFAULT_PAGE_SIZE = 20;
@Min(value = 1, message = "页码必须大于0")
private Integer pageNum;
@Min(value = 1, message = "每页条数必须大于0")
@Max(value = 100, message = "每页条数不能超过100")
private Integer pageSize;
// 排序字段白名单
private String sortField;
private String sortOrder; // ASC/DESC
// 构造函数含参数校验
public PageRequest(Integer pageNum, Integer pageSize) {
this.pageNum = pageNum != null ? Math.max(MIN_PAGE, pageNum) : MIN_PAGE;
this.pageSize = pageSize != null ? Math.min(MAX_PAGE_SIZE, Math.max(1, pageSize)) : DEFAULT_PAGE_SIZE;
}
}
2 统一响应格式
public class PageResult<T> {
private List<T> records;
private long total;
private int currentPage;
private int pageSize;
private int totalPages;
private boolean hasMore;
// 是否脱敏
private boolean desensitized;
}
安全防护措施
1 SQL注入防护
@Component
public class SafePageHelper {
// 排序字段白名单
private static final Set<String> ALLOWED_SORT_FIELDS = Set.of(
"id", "createTime", "updateTime", "name", "status"
);
// 排序方向白名单
private static final Set<String> ALLOWED_ORDER = Set.of("ASC", "DESC", "asc", "desc");
public String validateSortField(String sortField) {
if (sortField == null || sortField.trim().isEmpty()) {
return "id";
}
// 1. 移除可能存在SQL注入的字符
sortField = sortField.replaceAll("[^a-zA-Z0-9_]", "");
// 2. 白名单校验
if (!ALLOWED_SORT_FIELDS.contains(sortField)) {
return "id"; // 默认排序字段
}
return sortField;
}
public String validateSortOrder(String sortOrder) {
if (sortOrder == null || !ALLOWED_ORDER.contains(sortOrder.toUpperCase())) {
return "DESC";
}
return sortOrder.toUpperCase();
}
}
2 数据权限控制
@Service
public class SecurePageService {
public PageResult<User> queryUsers(PageRequest pageRequest, UserContext user) {
// 1. 参数校验与清洗
validatePageRequest(pageRequest);
// 2. 转换安全参数
SafePageParams safeParams = buildSafeParams(pageRequest);
// 3. 数据权限过滤
// 普通用户只能看到自己的数据
// 管理员可以看到部门数据
// 超级管理员可以看到所有数据
DataPermission permission = buildDataPermission(user);
// 4. 执行查询(使用MyBatis-Plus的分页)
Page<User> page = new Page<>(safeParams.getPageNum(), safeParams.getPageSize());
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.eq(permission.isSelfOnly(), User::getUserId, user.getUserId())
.in(permission.hasDeptLimit(), User::getDeptId, user.getDeptIds())
.orderBy(permission.isAdmin(), User::getCreateTime, false);
// 5. 结果脱敏
Page<User> result = userMapper.selectPage(page, wrapper);
return buildSecurePageResult(result, user);
}
}
防爬虫与限流
1 频率控制
@Component
public class PageAccessController {
@Autowired
private RedisTemplate redisTemplate;
// 使用Redis实现滑动窗口限流
public boolean checkPageAccess(String userId, String apiPath) {
String key = "page:access:" + userId + ":" + apiPath;
// 滑动窗口大小:60秒
long windowSize = 60L;
// 最大请求次数
long maxRequests = 30L;
long currentTime = System.currentTimeMillis() / 1000;
long windowStart = currentTime - windowSize;
// 移除窗口外的记录
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
// 获取当前窗口请求数
Long count = redisTemplate.opsForZSet().zCard(key);
if (count != null && count >= maxRequests) {
return false; // 超出限制
}
// 添加当前请求
redisTemplate.opsForZSet().add(key, String.valueOf(currentTime), currentTime);
redisTemplate.expire(key, windowSize + 10, TimeUnit.SECONDS);
return true;
}
}
2 敏感数据脱敏
public class PageDataDesensitizer {
// 手机号脱敏
public String maskPhone(String phone) {
if (phone == null || phone.length() < 7) {
return phone;
}
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
// 邮箱脱敏
public String maskEmail(String email) {
if (email == null || !email.contains("@")) {
return email;
}
String[] parts = email.split("@");
String name = parts[0];
if (name.length() <= 3) {
return name.charAt(0) + "***@" + parts[1];
}
return name.substring(0, 3) + "***@" + parts[1];
}
// 分页结果脱敏
public <T> PageResult<T> desensitizePage(PageResult<T> pageResult,
Class<T> clazz,
UserContext user) {
if (user.isAdmin()) {
return pageResult; // 管理员不脱敏
}
List<T> records = pageResult.getRecords();
// 根据用户角色和数据类型进行不同级别的脱敏
records = records.stream()
.map(record -> desensitizeRecord(record, clazz, user))
.collect(Collectors.toList());
pageResult.setRecords(records);
pageResult.setDesensitized(true);
return pageResult;
}
}
性能与安全优化
1 缓存策略
@Service
public class CachedPageService {
// 分页查询缓存Key生成
public String buildPageCacheKey(String queryType, PageRequest pageRequest,
Map<String, Object> filters) {
StringBuilder sb = new StringBuilder();
sb.append("page:").append(queryType)
.append(":p").append(pageRequest.getPageNum())
.append("-s").append(pageRequest.getPageSize());
if (filters != null) {
filters.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> sb.append(":")
.append(entry.getKey())
.append("=")
.append(entry.getValue()));
}
return sb.toString();
}
// 带缓存的查询
public PageResult<Data> queryWithCache(String type, PageRequest pageRequest,
UserContext user) {
String cacheKey = buildPageCacheKey(type, pageRequest, user.getFilters());
// 1. 尝试从缓存获取
PageResult<Data> cachedResult = redisTemplate.opsForValue().get(cacheKey);
if (cachedResult != null) {
return cachedResult;
}
// 2. 数据库查询
PageResult<Data> result = queryFromDatabase(type, pageRequest, user);
// 3. 设置缓存(考虑过期时间)
long ttl = calculateCacheTTL(type, pageRequest);
redisTemplate.opsForValue().set(cacheKey, result, ttl, TimeUnit.MINUTES);
return result;
}
}
2 审计日志
@Component
public class PageAuditLogger {
@Autowired
private AuditLogRepository auditLogRepository;
public void logPageAccess(String userId, String apiPath,
PageRequest pageRequest,
boolean success) {
PageAuditLog log = new PageAuditLog();
log.setUserId(userId);
log.setApiPath(apiPath);
log.setPageNum(pageRequest.getPageNum());
log.setPageSize(pageRequest.getPageSize());
log.setRequestTime(new Date());
log.setSuccess(success);
log.setIpAddress(getClientIp());
log.setUserAgent(getUserAgent());
// 异步记录审计日志
CompletableFuture.runAsync(() -> {
auditLogRepository.save(log);
});
}
// 检测异常行为
public boolean detectAnomaly(String userId, String apiPath) {
// 检查是否存在异常的大量数据导出
// 检查是否存在频繁的深度分页
// 检查是否存在疑似爬虫行为
return checkExcessiveDataExport(userId)
|| checkDeepPaginationAttack(userId)
|| checkCrawlBehavior(userId);
}
}
完整使用示例
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private SecurePageService pageService;
@Autowired
private PageAccessController accessController;
@Autowired
private PageAuditLogger auditLogger;
@GetMapping("/page")
public Result<PageResult<UserDTO>> queryUsers(
@Valid PageRequest pageRequest,
@RequestParam(required = false) String keyword,
@AuthenticationPrincipal UserContext user) {
// 1. 访问频率检查
if (!accessController.checkPageAccess(user.getUserId(), "/api/users/page")) {
auditLogger.logPageAccess(user.getUserId(), "/api/users/page",
pageRequest, false);
return Result.error("请求过于频繁");
}
// 2. 检测异常行为
if (auditLogger.detectAnomaly(user.getUserId(), "/api/users/page")) {
auditLogger.logPageAccess(user.getUserId(), "/api/users/page",
pageRequest, false);
return Result.error("检测到异常访问行为");
}
// 3. 执行安全分页查询
PageResult<User> queryResult = pageService.queryUsers(pageRequest, user);
// 4. 结果脱敏
PageResult<UserDTO> safeResult = dataDesensitizer
.desensitizePage(queryResult, UserDTO.class, user);
// 5. 审计日志
auditLogger.logPageAccess(user.getUserId(), "/api/users/page",
pageRequest, true);
return Result.success(safeResult);
}
}
安全规范检查清单
| 检查项 | 要求 | 优先级 |
|---|---|---|
| 参数校验 | 页码≥1,每页条数1-100 | 强制 |
| SQL注入防护 | 排序字段白名单验证 | 强制 |
| 数据权限 | 按角色过滤数据 | 强制 |
| 访问频率控制 | 单用户每分钟≤30次 | 推荐 |
| 敏感数据脱敏 | 按用户角色脱敏 | 强制 |
| 深度分页限制 | 最大偏移量≤10000 | 推荐 |
| 缓存策略 | 合理设置缓存TTL | 推荐 |
| 审计日志 | 记录所有分页请求 | 强制 |
| 异常检测 | 检测爬虫等异常行为 | 推荐 |
Java分页安全流程规范不仅要关注功能实现,更要注重:
- 输入验证:严格的参数校验和格式化
- 权限控制:基于角色的数据访问限制
- 安全防护:SQL注入、XSS等攻击防护
- 性能优化:合理的缓存和索引策略
- 监控审计:全面的日志记录和异常检测
这些规范能有效防止数据泄露、接口滥用等安全风险。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播