diff --git a/src/main/java/com/example/demo/security/LearningJwtFilter.java b/src/main/java/com/example/demo/security/LearningJwtFilter.java index f261747..8283a6b 100644 --- a/src/main/java/com/example/demo/security/LearningJwtFilter.java +++ b/src/main/java/com/example/demo/security/LearningJwtFilter.java @@ -28,21 +28,14 @@ public class LearningJwtFilter extends OncePerRequestFilter { @Override protected boolean shouldNotFilter(HttpServletRequest request) { String uri = request.getRequestURI(); - boolean learnRoute = "/learn".equals(uri) || uri.startsWith("/learn/"); - boolean protectedPage = "/".equals(uri) - || "/home".equals(uri) - || "/index.html".equals(uri) - || "/users.html".equals(uri) - || "/aop.html".equals(uri) - || "/events.html".equals(uri); - return !(protectedPage + return !(isProtectedPage(uri) || uri.startsWith("/api/secure/") || uri.equals("/api/users") || uri.startsWith("/api/users/") || "/aop".equals(uri) || uri.startsWith("/aop/") || uri.startsWith("/api/lab/") - || learnRoute); + || isLearnRoute(uri)); } @Override @@ -61,9 +54,28 @@ public class LearningJwtFilter extends OncePerRequestFilter { SecurityContextHolder.getContext().setAuthentication(authToken); } + if (isProtectedPage(request.getRequestURI()) + && SecurityContextHolder.getContext().getAuthentication() == null) { + response.sendRedirect("/access.html"); + return; + } + filterChain.doFilter(request, response); } + private boolean isProtectedPage(String uri) { + return "/".equals(uri) + || "/home".equals(uri) + || "/index.html".equals(uri) + || "/users.html".equals(uri) + || "/aop.html".equals(uri) + || "/events.html".equals(uri); + } + + private boolean isLearnRoute(String uri) { + return "/learn".equals(uri) || uri.startsWith("/learn/"); + } + private String resolveToken(HttpServletRequest request) { String authorization = request.getHeader("Authorization"); if (StringUtils.hasText(authorization) && authorization.startsWith("Bearer ")) { diff --git a/src/main/resources/static/learning-shell.js b/src/main/resources/static/learning-shell.js index 2535d85..a4b6286 100644 --- a/src/main/resources/static/learning-shell.js +++ b/src/main/resources/static/learning-shell.js @@ -8,10 +8,10 @@ const TEXT = { zh: { brand: "Spring 学习工作台", - home: "学习总控台", + home: "总控台", access: "登录页", - loginReady: "已登录,受保护页面和接口都可访问", - loginMissing: "未登录,受保护页面会跳回登录页", + loginReady: "已登录,受保护页面和接口均可访问。", + loginMissing: "未登录,受保护页面会跳回登录页。", currentUser: "当前用户", logout: "退出登录", login: "去登录", @@ -19,7 +19,7 @@ unauthorized: "未登录或认证已失效,请先打开登录页重新登录。", requestFailed: "请求失败,请稍后再试。", authRequiredTitle: "登录后即可使用", - authRequiredMessage: "请先去登录页获取 token,才能继续访问主控台和实验", + authRequiredMessage: "请先去登录页获取 token,随后再访问总控台和实验页。", authRequiredButton: "前往登录" }, en: { @@ -45,19 +45,19 @@ jwt: { label: "JWT 路线", summary: "浏览器先调用 /api/auth/login 获取 Bearer Token,之后通过 Authorization 头访问受保护接口,重点学习无状态鉴权。", - learnMore: "观察 LearningJwtFilter 如何从 Header 或 Cookie 中提取认证信息。" + learnMore: "可以观察 LearningJwtFilter 如何从 Header 或 Cookie 中提取认证信息。" }, satoken: { label: "Sa-Token 对照", summary: "这里先用可视化说明 Sa-Token 常见的会话、注解与拦截器思路,帮助你和 JWT 的链路做对比。", - learnMore: "适合继续扩展成 Sa-Token 章节,比较登录状态持久化、注解鉴权与刷新机制。" + learnMore: "适合后续扩展成 Sa-Token 章节,比较登录态持久化、注解鉴权和续期机制。" }, filters: ["LearningJwtFilter", "AuthenticationEntryPoint", "AuthorizationFilter"], tokenTips: "当前实现会同时下发 Bearer Token 和认证 Cookie。这样既能继续学习 Header 鉴权,也能让普通页面跳转真正受登录保护。", timeline: [ { stage: "1. 登录", detail: "POST /api/auth/login,服务端同时返回 token 与认证 Cookie。" }, - { stage: "2. 访问页面", detail: "浏览器普通跳转自动携带 Cookie,受保护 HTML 页面可直接放行。" }, - { stage: "3. 调接口", detail: "前端 fetch 默认带 Cookie;如果本地存了 token,也会补上 Authorization 头。" }, + { stage: "2. 访问页面", detail: "浏览器普通跳转会自动携带 Cookie,受保护 HTML 页面可以直接放行。" }, + { stage: "3. 调接口", detail: "前端 fetch 默认携带 Cookie;如果本地存有 token,也会补上 Authorization 头。" }, { stage: "4. 失效处理", detail: "认证缺失或失效时,页面跳回 /access.html,接口返回 401。" } ] }, diff --git a/src/test/java/com/example/demo/controller/AuthFlowTest.java b/src/test/java/com/example/demo/controller/AuthFlowTest.java index 7019b9b..a6c3652 100644 --- a/src/test/java/com/example/demo/controller/AuthFlowTest.java +++ b/src/test/java/com/example/demo/controller/AuthFlowTest.java @@ -82,7 +82,7 @@ class AuthFlowTest { void protectedHtmlShouldLoadWithAuthCookie() throws Exception { mockMvc.perform(get("/index.html").cookie(authCookie()).accept(MediaType.TEXT_HTML)) .andExpect(status().isOk()) - .andExpect(content().string(containsString("learning-menu-title"))); + .andExpect(content().string(containsString("consoleOutput"))); } @Test