传统方式实现HandlerInterceptor
这里代码不但实现了token的认证拦截,也在afterCompletion实现了接口请求日志打印
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.core.annotation.VisitorAccessible;
import org.linlinjava.litemall.core.enums.CommonCodeEnum;
import org.linlinjava.litemall.core.exception.ApplicationException;
import org.linlinjava.litemall.core.util.IpUtil;
import org.linlinjava.litemall.core.util.JWTVerifierUtil;
import org.linlinjava.litemall.db.domain.LitemallUser;
import org.linlinjava.litemall.db.domain.UserVo;
import org.linlinjava.litemall.db.service.LitemallUserService;
import org.linlinjava.litemall.wx.global.GlobalHolder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class InitInterceptor implements HandlerInterceptor {
private static final Log LOGGER = LogFactory.getLog(InitInterceptor.class);
@Autowired
private LitemallUserService userService;
private NamedThreadLocal<Long> startTimeThreadLocal =
new NamedThreadLocal<Long>("StopWatch-StartTime");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ip = IpUtil.getIpAddr(request);
LOGGER.info("ip:"+ ip +"request:"+ String.format("%s consume millis", request.getRequestURI()));
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type,Accept,X-Requested-With,token");
long beginTime = System.currentTimeMillis();//1、开始时间
startTimeThreadLocal.set(beginTime);
if (request.getMethod().toUpperCase().equals("OPTIONS") || request.getServletPath().contains("ok") || request.getServletPath().contains("api/user/login")) {
return true;
}
// 处理用户
String token = request.getHeader("token");
if (StringUtils.isNotEmpty(token)) {
String userCode = "";
try {
userCode = (String) JWTVerifierUtil.verify(token).get(2);
if (StringUtils.isEmpty(userCode)) {
throw new ApplicationException(CommonCodeEnum.NO_ACCESS_RIGHT);
}
} catch (Exception e) {
throw new ApplicationException(CommonCodeEnum.NO_ACCESS_RIGHT);
}
LitemallUser userInfoEntity = userService.findById(Integer.valueOf(userCode));
if (userInfoEntity == null) {
throw new ApplicationException(CommonCodeEnum.NO_ACCESS_RIGHT);
}
UserVo userInfo = new UserVo();
BeanUtils.copyProperties(userInfoEntity, userInfo);
GlobalHolder.setCurrentLoginUser(userInfo);
} else {
HandlerMethod handlerMethod = (HandlerMethod) handler;
VisitorAccessible annotation = handlerMethod.getMethodAnnotation(VisitorAccessible.class);
if (annotation == null) {
throw new ApplicationException(CommonCodeEnum.NO_ACCESS_RIGHT);
}
}
return true;
}
/**
* 在业务处理器处理请求执行完成后,生成视图之前执行的动作 可在modelAndView中加入数据,比如当前时间
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
* 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion() 处理写session工作
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
try {
long endTime = System.currentTimeMillis();//2、结束时间
long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)
long consumeTime = endTime - beginTime;//3、消耗的时间
String ip = IpUtil.getIpAddr(request);
LOGGER.info("ip:"+ ip +"request:"+ String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
} catch (Exception e) {
e.printStackTrace();
}
GlobalHolder.removeCurrentLoginUser();
}
}
shiro中AdviceFilter实现请求日志打印
AdviceFilter 提供了 AOP 的功能,其实现和 SpringMVC 中的 Interceptor 思想一样
public class MyAdviceFilter extends AdviceFilter {
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
System.out.println("====预处理/前置处理");
return true;//返回 false 将中断后续拦截器链的执行
}
@Override
protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
System.out.println("====后处理/后置返回处理");
}
@Override
public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
System.out.println("====完成处理/后置最终处理");
}
}
preHandle:进行请求的预处理,然后根据返回值决定是否继续处理(true:继续过滤器链);可以通过它实现权限控制;
postHandle:执行完拦截器链之后正常返回后执行;
afterCompletion:不管最后有没有异常,afterCompletion 都会执行,完成如清理资源功能。
这里只要在afterCompletion处打印请求日志即可,代码遗留的问题在于如何打印post,put请求的body,先看看完整代码:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.web.servlet.AdviceFilter;
import org.springframework.core.NamedThreadLocal;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
* @author Cheertan
* @date 2020-08-11
*/
public class MyAdviceFilter extends AdviceFilter {
private static final Log LOGGER = LogFactory.getLog(MyAdviceFilter.class);
private NamedThreadLocal<Long> startTimeThreadLocal =
new NamedThreadLocal<Long>("StopWatch-StartTime");
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
return true;
//返回 false 将中断后续拦截器链的执行
}
@Override
protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
}
@Override
public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
try {
long endTime = System.currentTimeMillis();
long consumeTime = endTime;
String ip = (request.getRemoteHost());
if (request instanceof HttpServletRequest) {
String url = ((HttpServletRequest)request).getRequestURL().toString();
String queryString = ((HttpServletRequest)request).getQueryString();
//String body = IOUtils.toString(((HttpServletRequest)request).getReader());
System.out.println(((HttpServletRequest)request).getMethod());
if (((HttpServletRequest)request).getMethod().equalsIgnoreCase("GET")) {
LOGGER.info("ip:" + ip + " request: " + url + "?" + queryString);
} else {
String body = request.getInputStream().toString();
//body并不能打印出来,待解决,本打算用body = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
//但是会报错
LOGGER.info("ip:" + ip + " request: " + url+" body: "+body);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
在shiro配置类里加载自定义的MyAdviceFilter
关键就这段代码:
Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
filters.put("myAdviceFilter", myAdviceFilter());
shiroFilterFactoryBean.setFilters(filters);
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.linlinjava.litemall.admin.shiro.AdminAuthorizingRealm;
import org.linlinjava.litemall.admin.shiro.AdminWebSessionManager;
import org.linlinjava.litemall.admin.shiro.MyAdviceFilter;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public Realm realm() {
return new AdminAuthorizingRealm();
}
@Bean
public ShiroLogFilter shiroLogFilter(){
return new ShiroLogFilter();
}
@Bean
public MyAdviceFilter myAdviceFilter() {
return new MyAdviceFilter();
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
filters.put("myAdviceFilter", myAdviceFilter());
shiroFilterFactoryBean.setFilters(filters);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("/admin/auth/login", "anon");
filterChainDefinitionMap.put("/admin/oss/upload", "anon");
filterChainDefinitionMap.put("/admin/auth/401", "anon");
filterChainDefinitionMap.put("/admin/auth/index", "anon");
filterChainDefinitionMap.put("/admin/auth/403", "anon");
filterChainDefinitionMap.put("/admin/index/index", "anon");
filterChainDefinitionMap.put("/admin/**", "authc");
shiroFilterFactoryBean.setLoginUrl("/admin/auth/401");
shiroFilterFactoryBean.setSuccessUrl("/admin/auth/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/admin/auth/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SessionManager sessionManager() {
return new AdminWebSessionManager();
}
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
securityManager.setSessionManager(sessionManager());
return securityManager;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
}
遗留问题
在MyAdviceFilter中使用 String body = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
ava.lang.IllegalStateException: getReader() has already been called for this request