package com.jeecms.cms.web; import static com.jeecms.common.web.Constants.MESSAGE; import static com.jeecms.core.action.front.LoginAct.PROCESS_URL; import static com.jeecms.core.action.front.LoginAct.RETURN_URL; import java.util.List; import java.util.Set; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.util.UrlPathHelper; import com.jeecms.cms.entity.main.CmsSite; import com.jeecms.cms.entity.main.CmsUser; import com.jeecms.cms.manager.main.CmsSiteMng; import com.jeecms.cms.manager.main.CmsUserMng; import com.jeecms.common.web.CookieUtils; import com.jeecms.common.web.session.SessionProvider; import com.jeecms.common.web.springmvc.MessageResolver; import com.jeecms.core.manager.AuthenticationMng; /** * CMS上下文信息拦截器 * * 包括登录信息、权限信息、站点信息 */ public class AdminContextInterceptor extends HandlerInterceptorAdapter { private static final Logger log = Logger .getLogger(AdminContextInterceptor.class); public static final String SITE_PARAM = "_site_id_param"; public static final String SITE_COOKIE = "_site_id_cookie"; public static final String PERMISSION_MODEL = "_permission_key"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 获得站点 CmsSite site = getSite(request, response); CmsUtils.setSite(request, site); // Site加入线程变量 CmsThreadVariable.setSite(site); // 获得用户 CmsUser user = null; if (adminId != null) { // 指定管理员(开发状态) user = cmsUserMng.findById(adminId); if (user == null) { throw new IllegalStateException("User ID=" + adminId + " not found!"); } } else { // 正常状态 Integer userId = authMng .retrieveUserIdFromSession(session, request); if (userId != null) { user = cmsUserMng.findById(userId); } } // 此时用户可以为null CmsUtils.setUser(request, user); // User加入线程变量 CmsThreadVariable.setUser(user); String uri = getURI(request); // 不在验证的范围内 if (exclude(uri)) { return true; } // 用户为null跳转到登陆页面 if (user == null) { response.sendRedirect(getLoginUrl(request)); return false; } // 用户不是管理员,提示无权限。 if (!user.getAdmin()) { request.setAttribute(MESSAGE, MessageResolver.getMessage(request, "login.notAdmin")); response.sendError(HttpServletResponse.SC_FORBIDDEN); return false; } // 不属于该站点的管理员,提示无权限。 if (!user.getSites().contains(site)) { request.setAttribute(MESSAGE, MessageResolver.getMessage(request, "login.notInSite")); response.sendError(HttpServletResponse.SC_FORBIDDEN); return false; } boolean viewonly = user.getViewonlyAdmin(); // 没有访问权限,提示无权限。 if (auth && !user.isSuper() && !permistionPass(uri, user.getPerms(), viewonly)) { request.setAttribute(MESSAGE, MessageResolver.getMessage(request, "login.notPermission")); response.sendError(HttpServletResponse.SC_FORBIDDEN); return false; } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav) throws Exception { CmsUser user = CmsUtils.getUser(request); // 不控制权限时perm为null,PermistionDirective标签将以此作为依据不处理权限问题。 if (auth && user != null && !user.isSuper() && mav != null && mav.getModelMap() != null && mav.getViewName() != null && !mav.getViewName().startsWith("redirect:")) { mav.getModelMap().addAttribute(PERMISSION_MODEL, user.getPerms()); } } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // Sevlet容器有可能使用线程池,所以必须手动清空线程变量。 CmsThreadVariable.removeUser(); CmsThreadVariable.removeSite(); } private String getLoginUrl(HttpServletRequest request) { StringBuilder buff = new StringBuilder(); if (loginUrl.startsWith("/")) { String ctx = request.getContextPath(); if (!StringUtils.isBlank(ctx)) { buff.append(ctx); } } buff.append(loginUrl).append("?"); buff.append(RETURN_URL).append("=").append(returnUrl); if (!StringUtils.isBlank(processUrl)) { buff.append("&").append(PROCESS_URL).append("=").append( getProcessUrl(request)); } return buff.toString(); } private String getProcessUrl(HttpServletRequest request) { StringBuilder buff = new StringBuilder(); if (loginUrl.startsWith("/")) { String ctx = request.getContextPath(); if (!StringUtils.isBlank(ctx)) { buff.append(ctx); } } buff.append(processUrl); return buff.toString(); } /** * 按参数、cookie、域名、默认。 * * @param request * @return 不会返回null,如果站点不存在,则抛出异常。 */ private CmsSite getSite(HttpServletRequest request, HttpServletResponse response) { CmsSite site = getByParams(request, response); if (site == null) { site = getByCookie(request); } if (site == null) { site = getByDomain(request); } if (site == null) { site = getByDefault(); } if (site == null) { throw new RuntimeException("cannot get site!"); } else { return site; } } private CmsSite getByParams(HttpServletRequest request, HttpServletResponse response) { String p = request.getParameter(SITE_PARAM); if (!StringUtils.isBlank(p)) { try { Integer siteId = Integer.parseInt(p); CmsSite site = cmsSiteMng.findById(siteId); if (site != null) { // 若使用参数选择站点,则应该把站点保存至cookie中才好。 CookieUtils.addCookie(request, response, SITE_COOKIE, site .getId().toString(), null, null); return site; } } catch (NumberFormatException e) { log.warn("param site id format exception", e); } } return null; } private CmsSite getByCookie(HttpServletRequest request) { Cookie cookie = CookieUtils.getCookie(request, SITE_COOKIE); if (cookie != null) { String v = cookie.getValue(); if (!StringUtils.isBlank(v)) { try { Integer siteId = Integer.parseInt(v); return cmsSiteMng.findById(siteId); } catch (NumberFormatException e) { log.warn("cookie site id format exception", e); } } } return null; } private CmsSite getByDomain(HttpServletRequest request) { String domain = request.getServerName(); if (!StringUtils.isBlank(domain)) { return cmsSiteMng.findByDomain(domain, true); } return null; } private CmsSite getByDefault() { List<CmsSite> list = cmsSiteMng.getListFromCache(); if (list.size() > 0) { return list.get(0); } else { return null; } } private boolean exclude(String uri) { if (excludeUrls != null) { for (String exc : excludeUrls) { if (exc.equals(uri)) { return true; } } } return false; } private boolean permistionPass(String uri, Set<String> perms, boolean viewOnly) { String u = null; int i; for (String perm : perms) { if (uri.startsWith(perm)) { // 只读管理员 if (viewOnly) { // 获得最后一个 '/' 的URI地址。 i = uri.lastIndexOf("/"); if (i == -1) { throw new RuntimeException("uri must start width '/':" + uri); } u = uri.substring(i + 1); // 操作型地址被禁止 if (u.startsWith("o_")) { return false; } } return true; } } return false; } /** * 获得第三个路径分隔符的位置 * * @param request * @throws IllegalStateException * 访问路径错误,没有三(四)个'/' */ private static String getURI(HttpServletRequest request) throws IllegalStateException { UrlPathHelper helper = new UrlPathHelper(); String uri = helper.getOriginatingRequestUri(request); String ctxPath = helper.getOriginatingContextPath(request); int start = 0, i = 0, count = 2; if (!StringUtils.isBlank(ctxPath)) { count++; } while (i < count && start != -1) { start = uri.indexOf('/', start + 1); i++; } if (start <= 0) { throw new IllegalStateException( "admin access path not like '/jeeadmin/jspgou/...' pattern: " + uri); } return uri.substring(start); } private SessionProvider session; private AuthenticationMng authMng; private CmsSiteMng cmsSiteMng; private CmsUserMng cmsUserMng; private Integer adminId; private boolean auth = true; private String[] excludeUrls; private String loginUrl; private String processUrl; private String returnUrl; @Autowired public void setSession(SessionProvider session) { this.session = session; } @Autowired public void setCmsSiteMng(CmsSiteMng cmsSiteMng) { this.cmsSiteMng = cmsSiteMng; } @Autowired public void setCmsUserMng(CmsUserMng cmsUserMng) { this.cmsUserMng = cmsUserMng; } @Autowired public void setAuthMng(AuthenticationMng authMng) { this.authMng = authMng; } public void setAuth(boolean auth) { this.auth = auth; } public void setExcludeUrls(String[] excludeUrls) { this.excludeUrls = excludeUrls; } public void setAdminId(Integer adminId) { this.adminId = adminId; } public void setLoginUrl(String loginUrl) { this.loginUrl = loginUrl; } public void setProcessUrl(String processUrl) { this.processUrl = processUrl; } public void setReturnUrl(String returnUrl) { this.returnUrl = returnUrl; } }