You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular+Spring Boot项目中实现禁止打开新标签页功能

实现在线考试禁止打开新标签页的方案(Spring Boot + Angular)

Hey there! 针对你在线考试场景下禁止用户打开新标签页的需求,我结合Spring Boot和Angular给你梳理一套可行的方案——毕竟你是新手,我尽量写得直白易懂,分前端控制和后端兜底两部分来讲:

一、前端(Angular):拦截常规的新标签页打开行为

前端主要负责拦截用户通过右键、快捷键等方式打开新标签的操作,同时检测标签页切换行为,及时给出提示。

1. 禁止右键菜单(含“打开新标签”选项)

在你的根组件(比如app.component.ts)里添加右键事件监听,阻止默认菜单弹出:

import { Component, HostListener } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  // 拦截右键菜单
  @HostListener('contextmenu', ['$event'])
  onContextMenu(event: MouseEvent) {
    event.preventDefault(); // 阻止默认右键菜单弹出
  }
}

2. 拦截快捷键打开新标签/窗口

监听键盘事件,拦截Ctrl+T(Windows)、Cmd+T(Mac)这类打开新标签的快捷键,以及Ctrl+N打开新窗口的操作:

// 在同一个AppComponent里添加
@HostListener('document:keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
  // 检测Ctrl/Cmd + T(新标签)和Ctrl/Cmd + N(新窗口)
  const isNewTab = (event.ctrlKey || event.metaKey) && event.key === 't';
  const isNewWindow = (event.ctrlKey || event.metaKey) && event.key === 'n';
  
  if (isNewTab || isNewWindow) {
    event.preventDefault(); // 阻止快捷键生效
    alert('考试过程中禁止打开新标签页/窗口,请专注答题!');
  }
}

3. 检测标签页切换行为

用户如果已经打开了其他标签,切换过去时会触发visibilitychange事件,我们可以在这里触发警告或记录违规:

// 继续在AppComponent里添加
@HostListener('document:visibilitychange', ['$event'])
onVisibilityChange(event: Event) {
  if (document.hidden) {
    // 检测到用户切换到其他标签页
    alert('检测到您离开考试页面,请勿切换标签页作弊!');
    // 可选:调用后端接口记录这次违规行为
    // this.examService.reportTabSwitch().subscribe();
  }
}

4. 可选:强制全屏模式

让考试页面强制进入全屏,进一步降低用户切换标签的可能性:

// 在ngOnInit里调用全屏方法
ngOnInit() {
  // 请求全屏
  document.documentElement.requestFullscreen().catch(err => {
    alert('请允许页面进入全屏模式,否则可能影响考试体验!');
  });
  
  // 监听退出全屏事件
  document.addEventListener('fullscreenchange', () => {
    if (!document.fullscreenElement) {
      alert('考试过程中请勿退出全屏!');
      // 重新请求全屏
      document.documentElement.requestFullscreen();
    }
  });
}

二、后端(Spring Boot):做兜底校验,防止前端被绕过

前端的JS防护只能防普通用户,懂技术的人可以通过禁用JS等方式绕开,所以后端必须做兜底校验:

1. 会话与标签页绑定

给每个考试标签页生成唯一标识,绑定到用户会话,每次答题请求都校验这个标识:

  • 前端生成唯一ID并传给后端:
    // 先安装uuid:npm install uuid
    import { v4 as uuidv4 } from 'uuid';
    import { HttpClient } from '@angular/common/http';
    
    constructor(private http: HttpClient) {}
    
    ngOnInit() {
      let tabId = localStorage.getItem('exam-tab-id');
      if (!tabId) {
        tabId = uuidv4(); // 生成唯一ID
        localStorage.setItem('exam-tab-id', tabId);
      }
      // 把tabId传给后端绑定会话
      this.http.post('/api/exam/bind-tab', { tabId }).subscribe();
    }
    
  • 后端接收并存储tabId,在答题请求时校验:
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    import javax.servlet.http.HttpSession;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/api/exam")
    public class ExamController {
    
        // 绑定标签页ID到会话
        @PostMapping("/bind-tab")
        public void bindTab(@RequestBody Map<String, String> request, HttpSession session) {
            String tabId = request.get("tabId");
            session.setAttribute("exam-tab-id", tabId);
        }
    
        // 提交答案时校验标签页ID
        @PostMapping("/submit-answer")
        public String submitAnswer(@RequestBody Map<String, Object> answerRequest, HttpSession session) {
            String requestTabId = (String) answerRequest.get("tabId");
            String sessionTabId = (String) session.getAttribute("exam-tab-id");
            
            if (requestTabId == null || !requestTabId.equals(sessionTabId)) {
                // 检测到新标签页,返回违规提示
                return "error: 检测到您使用了新标签页,考试成绩无效";
            }
            // 正常处理提交逻辑
            return "success";
        }
    }
    

2. 答题操作间隔校验

记录用户每次提交答案的时间,如果两次操作间隔过长(比如超过30秒),可以判定为可能切标签查资料,触发警告或强制交卷:

// 在ExamController里添加时间记录逻辑
private static final long MAX_INTERVAL = 30 * 1000; // 30秒阈值

@PostMapping("/submit-answer")
public String submitAnswer(@RequestBody Map<String, Object> answerRequest, HttpSession session) {
    // 先校验tabId...
    
    // 校验操作间隔
    Long lastOperateTime = (Long) session.getAttribute("last-operate-time");
    long currentTime = System.currentTimeMillis();
    
    if (lastOperateTime != null && (currentTime - lastOperateTime) > MAX_INTERVAL) {
        // 这里可以返回警告信息给前端
        Integer cheatCount = (Integer) session.getAttribute("cheat-count");
        cheatCount = cheatCount == null ? 1 : cheatCount + 1;
        session.setAttribute("cheat-count", cheatCount);
        if (cheatCount >= 3) {
            // 执行强制交卷逻辑
            return "error: 违规次数过多,已强制交卷";
        }
        return "warn: 答题间隔过长,请专注考试!";
    }
    session.setAttribute("last-operate-time", currentTime);
    
    // 正常处理提交...
    return "success";
}

三、注意事项

  • 前端防护只是辅助,后端校验才是核心,因为前端代码可以被篡改或禁用
  • 提前告知用户考试规则,明确禁止切标签/开新标签的后果,避免纠纷
  • 可以结合摄像头监控、屏幕共享等方式进一步加强作弊防控(如果需求允许)

内容的提问来源于stack exchange,提问作者Rahul Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 23:02:43