package com.vci.starter.web.interceptor; import com.vci.starter.web.annotation.controller.VciUnCheckRight; import com.vci.starter.web.autoconfigure.SpringMVCConfig; import com.vci.starter.web.constant.TokenKeyConstant; import com.vci.starter.web.enumpck.ResultCodeEnum; import com.vci.starter.web.pagemodel.BaseResult; import com.vci.starter.web.pagemodel.SessionInfo; import com.vci.starter.web.util.ApplicationContextProvider; import com.vci.starter.web.util.LangBaseUtil; import com.vci.starter.web.util.VciBaseUtil; import com.vci.starter.web.util.WebThreadLocalUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.annotation.Resource; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * 权限拦截器 * @author weidy * @date 2019/11/7 2:32 PM */ //@Configuration public class VciSecurityInterceptor implements HandlerInterceptor { /** * 日志对象 */ private Logger logger = LoggerFactory.getLogger(getClass()); /** * 引入配置 */ @Autowired(required = false) private SpringMVCConfig springMVCConfig; /** * 会话,权限,token的接口 */ @Autowired private VciSessionForLoginI vciSessionForLoginI; /** * 执行拦截 * @param request 请求对象 * @param response 相应对象 * @param handler 处理器 * @return 是否成功,false表示失败,其他的拦截器将不会执行 * @throws ServletException */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //从头中获取token,然后调用接口类,获取用户的信息,具体实现类可以是从redis里获取,也可以是通过其他的方式获取 //如果头里面没有用户的token,那说明没有登录, String requestUri = request.getRequestURI(); String contextPath = request.getContextPath(); String url = requestUri.substring(contextPath.length()); String userToken = request.getHeader(TokenKeyConstant.USER_TOKEN_KEY); if(StringUtils.isBlank(userToken)){ userToken = request.getParameter(TokenKeyConstant.USER_TOKEN_KEY); } if(!(handler instanceof HandlerMethod)){ return true; } boolean unCheckLogin = false; if(handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod)handler; Method method = hm.getMethod(); //设置了不校验的会直接返回true if (method.isAnnotationPresent(VciUnCheckRight.class)) { unCheckLogin = true; } if (method.getDeclaringClass().isAnnotationPresent(VciUnCheckRight.class)) { unCheckLogin = true; } } if(url.endsWith(".md")){ unCheckLogin = true; } if(unCheckLogin){ //虽然不校验权限,但是如果token不为空,需要更新当前用户 SessionInfo sessionInfo = getSessionInfo(userToken); if(sessionInfo != null){ //初始化平台的token vciSessionForLoginI.initInvocationInfo(sessionInfo); } return true; } //获取配置文件中,不校验权限的路径 List unCheckUrls = new ArrayList<>(); if(springMVCConfig !=null && springMVCConfig.getUnCheckUrls() !=null){ unCheckUrls = springMVCConfig.getUnCheckUrls(); } if(StringUtils.isBlank(userToken) && !unCheckUrls.contains(url)){ //说明是没有用户信息的,而且也必须要校验是否登录的情况 //firefox上传文件依然使用头里存放token if(logger.isErrorEnabled()) { logger.error("请求" + url + ",但是没有登录"); } sendErrorMsg(response,"没有登录系统,请先登录",1); return false; //被踢下线由websocket直接提醒 }else{ SessionInfo sessionInfo = getSessionInfo(userToken); if(sessionInfo == null){ //也是说明不存在,被踢下线时也获取不到session的信息了 if(logger.isErrorEnabled()) { logger.error("token值非法,或过期,或者用户已经被踢下线," + userToken); } sendErrorMsg(response,"token值非法,或过期,或者用户已经被踢下线," + userToken,1); return false; }else{ if(!unCheckUrls.contains(url)){ if(vciSessionForLoginI == null){ //说明没办法校验 String msg = "请求路径"+ url +"没权限访问"; if(logger.isErrorEnabled()) { logger.error(msg + userToken); } sendErrorMsg(response,msg,2); return false; }else{ //初始化平台的token vciSessionForLoginI.initInvocationInfo(sessionInfo); String systemPrivateToken = request.getHeader(TokenKeyConstant.SYSTEM_PRIVATE_KEY); try { if (vciSessionForLoginI.checkRequestRights(request, systemPrivateToken, sessionInfo, handler)) { updateRequestTime(url,userToken); }else{ return false; } }catch (Throwable e){ String errorMsg = LangBaseUtil.getErrorMsg(e); if(logger.isErrorEnabled()) { logger.error("检查请求是否符合权限出现了错误," + errorMsg, e); } sendErrorMsg(response,errorMsg,1); return false; } } }else{ updateRequestTime(url,userToken); } } } return true; } private SessionInfo getSessionInfo(String userToken){ SessionInfo sessionInfo = null; if(StringUtils.isNotBlank(userToken)){ try{ if(vciSessionForLoginI == null){ vciSessionForLoginI = ApplicationContextProvider.getBean(VciSessionForLoginI.class); } sessionInfo = vciSessionForLoginI.getSessionInfoByToken(userToken); }catch (Throwable e){ logger.error("获取token出错",e); } if(sessionInfo!=null){ WebThreadLocalUtil.setCurrentUserSessionInfoInThread(sessionInfo); WebThreadLocalUtil.setTokenInThread(TokenKeyConstant.TOKEN_KEY_PREFIX_IN_REDIS+sessionInfo.getToken()); } } return sessionInfo; } /** * 返回错误信息 * @param response 相应对象 * @param errorMsg 错误信息 * @param innerErrorCode 内部错误,1表示没有登录,2表示没有权限 * @throws Exception */ private void sendErrorMsg(HttpServletResponse response, String errorMsg, int innerErrorCode) throws Exception{ BaseResult baseResult = BaseResult.fail(errorMsg); if(innerErrorCode ==1) { baseResult.setCode(ResultCodeEnum.UNAUTHORIZED.code); }else if(innerErrorCode == 2){ baseResult.setCode(ResultCodeEnum.NOT_RIGHT.code); } response.setCharacterEncoding("UTF-8"); response.getWriter().write(VciBaseUtil.getJSONStringWithDateFormat(baseResult)); } /** * 更新最后请求时间 * @param url 当前请求的路径 * @param userToken 用户的token */ private void updateRequestTime(String url,String userToken){ //记录最后访问的时间 List unStorageRequestTimeUrls = new ArrayList<>(); if(springMVCConfig !=null && springMVCConfig.getUnStorageRequestTimeUrls() != null){ unStorageRequestTimeUrls = springMVCConfig.getUnStorageRequestTimeUrls(); } if(CollectionUtils.isEmpty(unStorageRequestTimeUrls)){ unStorageRequestTimeUrls.add("smSessionController/checkIdleTime"); } String url1 = url; while(url1.startsWith("/")){ url1 = url1.substring(1); } if(vciSessionForLoginI != null && !unStorageRequestTimeUrls.contains(url1)){ vciSessionForLoginI.updateRequestTime(userToken); } } }