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