🎉 ureport增加token认证功能

This commit is contained in:
smallchill 2025-07-01 22:46:22 +08:00
parent ad84479e92
commit 450cf2e2a2
5 changed files with 264 additions and 2 deletions

View File

@ -124,6 +124,19 @@ public class WebUtil extends org.springframework.web.util.WebUtils {
return (requestAttributes == null) ? null : ((ServletRequestAttributes) requestAttributes).getRequest();
}
/**
* 获取 RequestUri
*
* @param request HttpServletRequest
* @return {RequestUri}
*/
public static String getRequestURI(HttpServletRequest request) {
if (request == null) {
return StringPool.EMPTY;
}
return request.getRequestURI();
}
/**
* 返回json
*

View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.report.config;
import jakarta.servlet.Filter;
import org.springblade.core.report.filter.UReportAuthFilter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
/**
* UReport认证配置类
*
* @author BladeX
*/
@Order
@AutoConfiguration
@ConditionalOnProperty(value = "report.auth", havingValue = "true", matchIfMissing = true)
public class ReportAuthConfiguration {
@Bean
public FilterRegistrationBean<Filter> uReportAuthFilter() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(new UReportAuthFilter());
registration.addUrlPatterns("/ureport/*");
registration.setName("uReportAuthFilter");
registration.setOrder(1);
return registration;
}
}

View File

@ -18,6 +18,7 @@ package org.springblade.core.report.config;
import com.bstek.ureport.UReportPropertyPlaceholderConfigurer;
import com.bstek.ureport.console.UReportServlet;
import com.bstek.ureport.provider.report.ReportProvider;
import jakarta.servlet.Servlet;
import org.springblade.core.report.props.ReportDatabaseProperties;
import org.springblade.core.report.props.ReportProperties;
import org.springblade.core.report.provider.DatabaseProvider;
@ -32,8 +33,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportResource;
import org.springframework.core.annotation.Order;
import jakarta.servlet.Servlet;
/**
* UReport配置类
*

View File

@ -0,0 +1,203 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.report.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.WebUtil;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
/**
* UReport 鉴权过滤器
*
* <p>处理 /ureport/* 路径的访问鉴权放行前端资源文件</p>
*
* @author BladeX
*/
@Slf4j
public class UReportAuthFilter implements Filter {
/**
* 前端资源文件扩展名
*/
private static final List<String> STATIC_RESOURCE_EXTENSIONS = Arrays.asList(
".js", ".css", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg",
".woff", ".woff2", ".ttf", ".eot", ".map", ".html", ".htm"
);
/**
* 资源文件路径前缀
*/
private static final String RESOURCE_PATH_PREFIX = "/ureport/res/ureport-asserts";
/**
* Session 中存储认证状态的键名
*/
private static final String SESSION_AUTH_KEY = "UREPORT_AUTHENTICATED";
/**
* Session 中存储用户信息的键名
*/
private static final String SESSION_USER_KEY = "UREPORT_USER_INFO";
/**
* Session 中存储用户主键的键名
*/
private static final String SESSION_USER_ID = "UREPORT_USER_ID";
/**
* Session 超时时间单位为秒
*/
private static final Integer INTERVAL_TIME = 120 * 60;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestURI = WebUtil.getRequestURI(httpRequest);
String requestIP = WebUtil.getIP(httpRequest);
// 检查是否为静态资源文件如果是则直接放行
if (isStaticResource(requestURI)) {
chain.doFilter(request, response);
return;
}
// 验证用户是否已认证优先检查 Session再检查 Token
if (!isAuthenticatedWithSession(httpRequest)) {
log.warn("未授权访问 UReport API: {}, IP: {}", requestURI, requestIP);
handleUnauthorized(httpResponse);
return;
}
// 验证通过放行请求
chain.doFilter(request, response);
}
/**
* 判断是否为静态资源文件
*/
private boolean isStaticResource(String requestURI) {
// 检查是否为资源文件路径
if (requestURI.startsWith(RESOURCE_PATH_PREFIX)) {
return true;
}
// 检查文件扩展名
String lowerCaseURI = requestURI.toLowerCase();
return STATIC_RESOURCE_EXTENSIONS.stream()
.anyMatch(lowerCaseURI::endsWith);
}
/**
* 验证用户是否已认证Session + Token 双重验证
*/
private boolean isAuthenticatedWithSession(HttpServletRequest request) {
HttpSession session = request.getSession(false);
// 1. 优先检查 Session 认证状态
if (session != null && Boolean.TRUE.equals(session.getAttribute(SESSION_AUTH_KEY))) {
log.debug("Session 认证有效,用户: {}", session.getAttribute(SESSION_USER_KEY));
return true;
}
// 2. Session 无效尝试 Token 认证
if (isTokenAuthenticated(request)) {
// Token 认证成功建立 Session
establishSession(request);
log.info("Token 认证成功,已建立 Session 会话");
return true;
}
return false;
}
/**
* Token 认证验证
*/
private boolean isTokenAuthenticated(HttpServletRequest request) {
try {
Long userId = AuthUtil.getUserId(request);
return userId != null && userId > 0;
} catch (Exception e) {
log.debug("Token 认证失败: {}", e.getMessage());
return false;
}
}
/**
* 建立认证会话
*/
private void establishSession(HttpServletRequest request) {
HttpSession session = request.getSession(true);
try {
String userAccount = AuthUtil.getUserAccount(request);
Long userId = AuthUtil.getUserId(request);
// 设置认证标记
session.setAttribute(SESSION_AUTH_KEY, true);
session.setAttribute(SESSION_USER_KEY, userAccount);
session.setAttribute(SESSION_USER_ID, userId);
// 设置 Session 超时时间60分钟
session.setMaxInactiveInterval(INTERVAL_TIME);
log.debug("建立 UReport 认证会话: 用户 {}, SessionID: {}", userAccount, session.getId());
} catch (Exception e) {
log.warn("建立认证会话失败: {}", e.getMessage());
}
}
/**
* Session 获取用户账号
*/
private String getUserFromSession(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
Object userAccount = session.getAttribute(SESSION_USER_KEY);
return userAccount != null ? userAccount.toString() : "未知用户";
}
return "未知用户";
}
/**
* 处理未授权访问
*/
private void handleUnauthorized(HttpServletResponse response) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
try (PrintWriter writer = response.getWriter()) {
writer.write("访问 UReport 需要 BladeX 的 Token 认证");
writer.flush();
}
}
}

View File

@ -28,6 +28,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "report")
public class ReportProperties {
private Boolean enabled = true;
private Boolean auth = true;
private Boolean disableHttpSessionReportCache = false;
private Boolean disableFileProvider = true;
private String fileStoreDir = StringPool.EMPTY;