后端:增加管理员访问日志
This commit is contained in:
parent
bb612cac64
commit
532daf6299
@ -1,6 +1,7 @@
|
||||
package cn.iocoder.mall.admin.application.config;
|
||||
|
||||
import cn.iocoder.common.framework.config.GlobalExceptionHandler;
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminAccessLogInterceptor;
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -18,16 +19,17 @@ public class MVCConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
private AdminSecurityInterceptor adminSecurityInterceptor;
|
||||
@Autowired
|
||||
private AdminAccessLogInterceptor adminAccessLogInterceptor;
|
||||
//
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// registry.addInterceptor(securityInterceptor).addPathPatterns("/user/**", "/admin/**"); // 只拦截我们定义的接口
|
||||
registry.addInterceptor(adminAccessLogInterceptor).addPathPatterns("/admins/**");
|
||||
registry.addInterceptor(adminSecurityInterceptor).addPathPatterns("/admins/**")
|
||||
.excludePathPatterns("/admins/passport/login"); // 排除登陆接口
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// 解决 swagger-ui.html 的访问,参考自 https://stackoverflow.com/questions/43545540/swagger-ui-no-mapping-found-for-http-request 解决
|
||||
|
@ -0,0 +1,78 @@
|
||||
package cn.iocoder.mall.admin.sdk.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.mall.admin.api.AdminAccessLogService;
|
||||
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
|
||||
import com.alibaba.dubbo.config.annotation.Reference;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 访问日志拦截器
|
||||
*/
|
||||
@Component
|
||||
public class AdminAccessLogInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private static final ThreadLocal<Date> START_TIME = new ThreadLocal<>();
|
||||
/**
|
||||
* 管理员编号
|
||||
*/
|
||||
private static final ThreadLocal<Integer> ADMIN_ID = new ThreadLocal<>();
|
||||
|
||||
@Reference
|
||||
private AdminAccessLogService adminAccessLogService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 记录当前时间
|
||||
START_TIME.set(new Date());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
AdminAccessLogAddDTO accessLog = new AdminAccessLogAddDTO();
|
||||
try {
|
||||
accessLog.setAdminId(ADMIN_ID.get());
|
||||
if (accessLog.getAdminId() == null) {
|
||||
accessLog.setAdminId(AdminAccessLogAddDTO.ADMIN_ID_NULL);
|
||||
}
|
||||
accessLog.setUri(request.getRequestURI()); // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。
|
||||
accessLog.setQueryString(HttpUtil.buildQueryString(request));
|
||||
accessLog.setMethod(request.getMethod());
|
||||
accessLog.setUserAgent(HttpUtil.getUserAgent(request));
|
||||
accessLog.setIp(HttpUtil.getIp(request));
|
||||
accessLog.setStartTime(START_TIME.get());
|
||||
accessLog.setResponseTime((int) (System.currentTimeMillis() - accessLog.getStartTime().getTime()));// 默认响应时间设为0
|
||||
adminAccessLogService.addAdminAccessLog(accessLog);
|
||||
// TODO 提升:暂时不考虑 ELK 的方案。而是基于 MySQL 存储。如果访问日志比较多,需要定期归档。
|
||||
} catch (Throwable th) {
|
||||
logger.error("[afterCompletion][插入管理员访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th));
|
||||
} finally {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAdminId(Integer adminId) {
|
||||
ADMIN_ID.set(adminId);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
START_TIME.remove();
|
||||
ADMIN_ID.remove();
|
||||
}
|
||||
|
||||
}
|
@ -39,6 +39,13 @@ public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||
// 添加到 AdminSecurityContext
|
||||
AdminSecurityContext context = new AdminSecurityContext(authentication.getAdminId(), authentication.getRoleIds());
|
||||
AdminSecurityContextHolder.setContext(context);
|
||||
// 同时也记录管理员编号到 AdminAccessLogInterceptor 中。因为:
|
||||
// AdminAccessLogInterceptor 需要在 AdminSecurityInterceptor 之前执行,这样记录的访问日志才健全
|
||||
// AdminSecurityInterceptor 执行后,会移除 AdminSecurityContext 信息,这就导致 AdminAccessLogInterceptor 无法获得管理员编号
|
||||
// 因此,这里需要进行记录
|
||||
if (authentication.getAdminId() != null) {
|
||||
AdminAccessLogInterceptor.setAdminId(authentication.getAdminId());
|
||||
}
|
||||
} else {
|
||||
String url = request.getRequestURI();
|
||||
if (!url.equals("/admin/passport/login")) { // TODO 临时写死。非登陆接口,必须已经认证身份,不允许匿名访问
|
||||
|
@ -0,0 +1,13 @@
|
||||
package cn.iocoder.mall.admin.api;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
|
||||
|
||||
/**
|
||||
* 管理员访问日志 Service 接口
|
||||
*/
|
||||
public interface AdminAccessLogService {
|
||||
|
||||
CommonResult<Boolean> addAdminAccessLog(AdminAccessLogAddDTO adminAccessLogAddDTO);
|
||||
|
||||
}
|
@ -9,6 +9,9 @@ import cn.iocoder.mall.admin.api.dto.AdminUpdateDTO;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 管理员 Service 接口
|
||||
*/
|
||||
public interface AdminService {
|
||||
|
||||
CommonResult<AdminPageBO> getAdminPage(AdminPageDTO adminPageDTO);
|
||||
|
@ -0,0 +1,132 @@
|
||||
package cn.iocoder.mall.admin.api.dto;
|
||||
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 管理员访问日志添加 DTO
|
||||
*/
|
||||
public class AdminAccessLogAddDTO {
|
||||
|
||||
/**
|
||||
* 管理员编号 - 空
|
||||
*/
|
||||
public static final Integer ADMIN_ID_NULL = 0;
|
||||
|
||||
/**
|
||||
* 管理员编号.
|
||||
*
|
||||
* 当管理员为空时,该值为0
|
||||
*/
|
||||
@NotNull(message = "管理员编号不能为空")
|
||||
private Integer adminId;
|
||||
/**
|
||||
* 访问地址
|
||||
*/
|
||||
@NotNull(message = "访问地址不能为空")
|
||||
private String uri;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
@NotNull(message = "请求参数不能为空")
|
||||
private String queryString;
|
||||
/**
|
||||
* http 方法
|
||||
*/
|
||||
@NotNull(message = "http 请求方法不能为空")
|
||||
private String method;
|
||||
/**
|
||||
* User Agent
|
||||
*/
|
||||
@NotNull(message = "User-Agent 不能为空")
|
||||
private String userAgent;
|
||||
/**
|
||||
* ip
|
||||
*/
|
||||
@NotNull(message = "ip 不能为空")
|
||||
private String ip;
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
@NotNull(message = "请求时间不能为空")
|
||||
private Date startTime;
|
||||
/**
|
||||
* 响应时长 -- 毫秒级
|
||||
*/
|
||||
@NotNull(message = "响应时长不能为空")
|
||||
private Integer responseTime;
|
||||
|
||||
public Integer getAdminId() {
|
||||
return adminId;
|
||||
}
|
||||
|
||||
public AdminAccessLogAddDTO setAdminId(Integer adminId) {
|
||||
this.adminId = adminId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public AdminAccessLogAddDTO setUri(String uri) {
|
||||
this.uri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getQueryString() {
|
||||
return queryString;
|
||||
}
|
||||
|
||||
public AdminAccessLogAddDTO setQueryString(String queryString) {
|
||||
this.queryString = queryString;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public AdminAccessLogAddDTO setMethod(String method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
public AdminAccessLogAddDTO setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public AdminAccessLogAddDTO setIp(String ip) {
|
||||
this.ip = ip;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public AdminAccessLogAddDTO setStartTime(Date startTime) {
|
||||
this.startTime = startTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getResponseTime() {
|
||||
return responseTime;
|
||||
}
|
||||
|
||||
public AdminAccessLogAddDTO setResponseTime(Integer responseTime) {
|
||||
this.responseTime = responseTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.iocoder.mall.admin.convert;
|
||||
|
||||
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
|
||||
import cn.iocoder.mall.admin.dataobject.AdminAccessLogDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface AdminAccessLogConvert {
|
||||
|
||||
AdminAccessLogConvert INSTANCE = Mappers.getMapper(AdminAccessLogConvert.class);
|
||||
|
||||
@Mappings({})
|
||||
AdminAccessLogDO convert(AdminAccessLogAddDTO adminAccessLogAddDTO);
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package cn.iocoder.mall.admin.dao;
|
||||
|
||||
import cn.iocoder.mall.admin.dataobject.AdminAccessLogDO;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface AdminAccessLogMapper {
|
||||
|
||||
void insert(AdminAccessLogDO entity);
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.BaseDO;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 管理员访问日志 DO
|
||||
*/
|
||||
public class AdminAccessLogDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 管理员编号.
|
||||
*
|
||||
* 当管理员为空时,该值为0
|
||||
*/
|
||||
private Integer adminId;
|
||||
/**
|
||||
* 访问地址
|
||||
*/
|
||||
private String uri;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private String queryString;
|
||||
/**
|
||||
* http 方法
|
||||
*/
|
||||
private String method;
|
||||
/**
|
||||
* userAgent
|
||||
*/
|
||||
private String userAgent;
|
||||
/**
|
||||
* ip
|
||||
*/
|
||||
private String ip;
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
private Date startTime;
|
||||
/**
|
||||
* 响应时长 -- 毫秒级
|
||||
*/
|
||||
private Integer responseTime;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public AdminAccessLogDO setId(Integer id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getAdminId() {
|
||||
return adminId;
|
||||
}
|
||||
|
||||
public AdminAccessLogDO setAdminId(Integer adminId) {
|
||||
this.adminId = adminId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public AdminAccessLogDO setUri(String uri) {
|
||||
this.uri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getQueryString() {
|
||||
return queryString;
|
||||
}
|
||||
|
||||
public AdminAccessLogDO setQueryString(String queryString) {
|
||||
this.queryString = queryString;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public AdminAccessLogDO setMethod(String method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
public AdminAccessLogDO setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public AdminAccessLogDO setIp(String ip) {
|
||||
this.ip = ip;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public AdminAccessLogDO setStartTime(Date startTime) {
|
||||
this.startTime = startTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getResponseTime() {
|
||||
return responseTime;
|
||||
}
|
||||
|
||||
public AdminAccessLogDO setResponseTime(Integer responseTime) {
|
||||
this.responseTime = responseTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package cn.iocoder.mall.admin.service;
|
||||
|
||||
import cn.iocoder.common.framework.util.StringUtil;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.admin.api.AdminAccessLogService;
|
||||
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
|
||||
import cn.iocoder.mall.admin.convert.AdminAccessLogConvert;
|
||||
import cn.iocoder.mall.admin.dao.AdminAccessLogMapper;
|
||||
import cn.iocoder.mall.admin.dataobject.AdminAccessLogDO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Service
|
||||
@com.alibaba.dubbo.config.annotation.Service(validation = "true")
|
||||
public class AdminAccessLogServiceImpl implements AdminAccessLogService {
|
||||
|
||||
/**
|
||||
* 请求参数最大长度。
|
||||
*/
|
||||
private static final Integer QUERY_STRING_MAX_LENGTH = 4096;
|
||||
/**
|
||||
* 请求地址最大长度。
|
||||
*/
|
||||
private static final Integer URI_MAX_LENGTH = 4096;
|
||||
/**
|
||||
* User-Agent 最大长度。
|
||||
*/
|
||||
private static final Integer USER_AGENT_MAX_LENGTH = 1024;
|
||||
|
||||
@Autowired
|
||||
private AdminAccessLogMapper adminAccessLogMapper;
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> addAdminAccessLog(AdminAccessLogAddDTO adminAccessLogAddDTO) {
|
||||
// 创建 AdminAccessLogDO
|
||||
AdminAccessLogDO accessLog = AdminAccessLogConvert.INSTANCE.convert(adminAccessLogAddDTO);
|
||||
accessLog.setCreateTime(new Date());
|
||||
// 截取最大长度
|
||||
if (accessLog.getUri().length() > URI_MAX_LENGTH) {
|
||||
accessLog.setUri(StringUtil.substring(accessLog.getUri(), URI_MAX_LENGTH));
|
||||
}
|
||||
if (accessLog.getQueryString().length() > QUERY_STRING_MAX_LENGTH) {
|
||||
accessLog.setQueryString(StringUtil.substring(accessLog.getQueryString(), QUERY_STRING_MAX_LENGTH));
|
||||
}
|
||||
if (accessLog.getUserAgent().length() > USER_AGENT_MAX_LENGTH) {
|
||||
accessLog.setUserAgent(StringUtil.substring(accessLog.getUserAgent(), USER_AGENT_MAX_LENGTH));
|
||||
}
|
||||
// 插入
|
||||
adminAccessLogMapper.insert(accessLog);
|
||||
// 返回成功
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.mall.admin.dao.AdminAccessLogMapper">
|
||||
|
||||
<!--<sql id="FIELDS">-->
|
||||
<!--id, username, nickname, password, status,-->
|
||||
<!--create_time-->
|
||||
<!--</sql>-->
|
||||
|
||||
<insert id="insert" parameterType="AdminAccessLogDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||
INSERT INTO admin_access_log (
|
||||
admin_id, uri, query_string, method, user_agent,
|
||||
ip, start_time, response_time, create_time
|
||||
) VALUES (
|
||||
#{adminId}, #{uri}, #{queryString}, #{method}, #{userAgent},
|
||||
#{ip}, #{startTime}, #{responseTime}, #{createTime}
|
||||
)
|
||||
</insert>
|
||||
|
||||
</mapper>
|
@ -1,11 +1,49 @@
|
||||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class HttpUtil {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);
|
||||
|
||||
/**
|
||||
* Standard Servlet 2.3+ spec request attributes for include URI and paths.
|
||||
* <p>If included via a RequestDispatcher, the current resource will see the
|
||||
* originating request. Its own URI and paths are exposed as request attributes.
|
||||
*/
|
||||
public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
|
||||
public static final String INCLUDE_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.include.context_path";
|
||||
// public static final String INCLUDE_SERVLET_PATH_ATTRIBUTE = "javax.servlet.include.servlet_path";
|
||||
// public static final String INCLUDE_PATH_INFO_ATTRIBUTE = "javax.servlet.include.path_info";
|
||||
// public static final String INCLUDE_QUERY_STRING_ATTRIBUTE = "javax.servlet.include.query_string";
|
||||
//
|
||||
// /**
|
||||
// * Standard Servlet 2.4+ spec request attributes for forward URI and paths.
|
||||
// * <p>If forwarded to via a RequestDispatcher, the current resource will see its
|
||||
// * own URI and paths. The originating URI and paths are exposed as request attributes.
|
||||
// */
|
||||
// public static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri";
|
||||
// public static final String FORWARD_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.forward.context_path";
|
||||
// public static final String FORWARD_SERVLET_PATH_ATTRIBUTE = "javax.servlet.forward.servlet_path";
|
||||
// public static final String FORWARD_PATH_INFO_ATTRIBUTE = "javax.servlet.forward.path_info";
|
||||
// public static final String FORWARD_QUERY_STRING_ATTRIBUTE = "javax.servlet.forward.query_string";
|
||||
|
||||
/**
|
||||
* Default character encoding to use when <code>request.getCharacterEncoding</code>
|
||||
* returns <code>null</code>, according to the Servlet spec.
|
||||
*
|
||||
* @see javax.servlet.ServletRequest#getCharacterEncoding
|
||||
*/
|
||||
public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
|
||||
|
||||
public static String obtainAccess(HttpServletRequest request) {
|
||||
String authorization = request.getHeader("Authorization");
|
||||
if (!StringUtils.hasText(authorization)) {
|
||||
@ -39,4 +77,243 @@ public class HttpUtil {
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request 请求
|
||||
* @return ua
|
||||
*/
|
||||
public static String getUserAgent(HttpServletRequest request) {
|
||||
String ua = request.getHeader("User-Agent");
|
||||
return ua != null ? ua : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据request拼接queryString
|
||||
*
|
||||
* @return queryString
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static String buildQueryString(HttpServletRequest request) {
|
||||
Enumeration<String> es = request.getParameterNames();
|
||||
if (!es.hasMoreElements()) {
|
||||
return "";
|
||||
}
|
||||
String parameterName, parameterValue;
|
||||
StringBuilder params = new StringBuilder();
|
||||
while (es.hasMoreElements()) {
|
||||
parameterName = es.nextElement();
|
||||
parameterValue = request.getParameter(parameterName);
|
||||
params.append(parameterName).append("=").append(parameterValue).append("&");
|
||||
}
|
||||
return params.deleteCharAt(params.length() - 1).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path within the web application for the given request.
|
||||
* Detects include request URL if called within a RequestDispatcher include.
|
||||
* <p/>
|
||||
* For example, for a request to URL
|
||||
* <p/>
|
||||
* <code>http://www.somehost.com/myapp/my/url.jsp</code>,
|
||||
* <p/>
|
||||
* for an application deployed to <code>/mayapp</code> (the application's context path), this method would return
|
||||
* <p/>
|
||||
* <code>/my/url.jsp</code>.
|
||||
*
|
||||
* 该方法,是从 Shiro 源码中,扣出来。add by 芋艿
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the path within the web application
|
||||
*/
|
||||
public static String getPathWithinApplication(HttpServletRequest request) {
|
||||
String contextPath = getContextPath(request);
|
||||
String requestUri = getRequestUri(request);
|
||||
if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
|
||||
// Normal case: URI contains context path.
|
||||
String path = requestUri.substring(contextPath.length());
|
||||
return (StringUtils.hasText(path) ? path : "/");
|
||||
} else {
|
||||
// Special case: rather unusual.
|
||||
return requestUri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the request URI for the given request, detecting an include request
|
||||
* URL if called within a RequestDispatcher include.
|
||||
* <p>As the value returned by <code>request.getRequestURI()</code> is <i>not</i>
|
||||
* decoded by the servlet container, this method will decode it.
|
||||
* <p>The URI that the web container resolves <i>should</i> be correct, but some
|
||||
* containers like JBoss/Jetty incorrectly include ";" strings like ";jsessionid"
|
||||
* in the URI. This method cuts off such incorrect appendices.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the request URI
|
||||
*/
|
||||
public static String getRequestUri(HttpServletRequest request) {
|
||||
String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
|
||||
if (uri == null) {
|
||||
uri = request.getRequestURI();
|
||||
}
|
||||
return normalize(decodeAndCleanUriString(request, uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a relative URI path that may have relative values ("/./",
|
||||
* "/../", and so on ) it it. <strong>WARNING</strong> - This method is
|
||||
* useful only for normalizing application-generated paths. It does not
|
||||
* try to perform security checks for malicious input.
|
||||
* Normalize operations were was happily taken from org.apache.catalina.util.RequestUtil in
|
||||
* Tomcat trunk, r939305
|
||||
*
|
||||
* @param path Relative path to be normalized
|
||||
* @return normalized path
|
||||
*/
|
||||
public static String normalize(String path) {
|
||||
return normalize(path, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a relative URI path that may have relative values ("/./",
|
||||
* "/../", and so on ) it it. <strong>WARNING</strong> - This method is
|
||||
* useful only for normalizing application-generated paths. It does not
|
||||
* try to perform security checks for malicious input.
|
||||
* Normalize operations were was happily taken from org.apache.catalina.util.RequestUtil in
|
||||
* Tomcat trunk, r939305
|
||||
*
|
||||
* @param path Relative path to be normalized
|
||||
* @param replaceBackSlash Should '\\' be replaced with '/'
|
||||
* @return normalized path
|
||||
*/
|
||||
private static String normalize(String path, boolean replaceBackSlash) {
|
||||
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
// Create a place for the normalized path
|
||||
String normalized = path;
|
||||
|
||||
if (replaceBackSlash && normalized.indexOf('\\') >= 0)
|
||||
normalized = normalized.replace('\\', '/');
|
||||
|
||||
if (normalized.equals("/."))
|
||||
return "/";
|
||||
|
||||
// Add a leading "/" if necessary
|
||||
if (!normalized.startsWith("/"))
|
||||
normalized = "/" + normalized;
|
||||
|
||||
// Resolve occurrences of "//" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("//");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index) +
|
||||
normalized.substring(index + 1);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/./" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/./");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index) +
|
||||
normalized.substring(index + 2);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/../" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/../");
|
||||
if (index < 0)
|
||||
break;
|
||||
if (index == 0)
|
||||
return (null); // Trying to go outside our context
|
||||
int index2 = normalized.lastIndexOf('/', index - 1);
|
||||
normalized = normalized.substring(0, index2) +
|
||||
normalized.substring(index + 3);
|
||||
}
|
||||
|
||||
// Return the normalized path that we have completed
|
||||
return (normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the supplied URI string and strips any extraneous portion after a ';'.
|
||||
*
|
||||
* @param request the incoming HttpServletRequest
|
||||
* @param uri the application's URI string
|
||||
* @return the supplied URI string stripped of any extraneous portion after a ';'.
|
||||
*/
|
||||
private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {
|
||||
uri = decodeRequestString(request, uri);
|
||||
int semicolonIndex = uri.indexOf(';');
|
||||
return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the context path for the given request, detecting an include request
|
||||
* URL if called within a RequestDispatcher include.
|
||||
* <p>As the value returned by <code>request.getContextPath()</code> is <i>not</i>
|
||||
* decoded by the servlet container, this method will decode it.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the context path
|
||||
*/
|
||||
public static String getContextPath(HttpServletRequest request) {
|
||||
String contextPath = (String) request.getAttribute(INCLUDE_CONTEXT_PATH_ATTRIBUTE);
|
||||
if (contextPath == null) {
|
||||
contextPath = request.getContextPath();
|
||||
}
|
||||
if ("/".equals(contextPath)) {
|
||||
// Invalid case, but happens for includes on Jetty: silently adapt it.
|
||||
contextPath = "";
|
||||
}
|
||||
return decodeRequestString(request, contextPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given source string with a URLDecoder. The encoding will be taken
|
||||
* from the request, falling back to the default "ISO-8859-1".
|
||||
* <p>The default implementation uses <code>URLDecoder.decode(input, enc)</code>.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param source the String to decode
|
||||
* @return the decoded String
|
||||
* @see #DEFAULT_CHARACTER_ENCODING
|
||||
* @see javax.servlet.ServletRequest#getCharacterEncoding
|
||||
* @see java.net.URLDecoder#decode(String, String)
|
||||
* @see java.net.URLDecoder#decode(String)
|
||||
*/
|
||||
@SuppressWarnings({"deprecation"})
|
||||
public static String decodeRequestString(HttpServletRequest request, String source) {
|
||||
String enc = determineEncoding(request);
|
||||
try {
|
||||
return URLDecoder.decode(source, enc);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not decode request string [" + source + "] with encoding '" + enc +
|
||||
"': falling back to platform default encoding; exception message: " + ex.getMessage());
|
||||
}
|
||||
return URLDecoder.decode(source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the encoding for the given request.
|
||||
* Can be overridden in subclasses.
|
||||
* <p>The default implementation checks the request's
|
||||
* {@link ServletRequest#getCharacterEncoding() character encoding}, and if that
|
||||
* <code>null</code>, falls back to the {@link #DEFAULT_CHARACTER_ENCODING}.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the encoding for the request (never <code>null</code>)
|
||||
* @see javax.servlet.ServletRequest#getCharacterEncoding()
|
||||
*/
|
||||
protected static String determineEncoding(HttpServletRequest request) {
|
||||
String enc = request.getCharacterEncoding();
|
||||
if (enc == null) {
|
||||
enc = DEFAULT_CHARACTER_ENCODING;
|
||||
}
|
||||
return enc;
|
||||
}
|
||||
|
||||
}
|
@ -27,4 +27,8 @@ public class StringUtil {
|
||||
return array;
|
||||
}
|
||||
|
||||
public static String substring(String str, int start) {
|
||||
return org.apache.commons.lang3.StringUtils.substring(str, start);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user