package com.huajian.common.core;
import com.huajian.common.interceptor.GlobalInterceptor;
import com.huajian.common.interceptor.LoginInterceptor;
import com.huajian.common.model._MappingKit;
import com.huajian.common.route.AdminRoutes;
import com.huajian.common.route.BuyRoutes;
import com.huajian.common.route.ProdRoutes;
import com.huajian.common.route.QaRoutes;
import com.huajian.common.route.SaleRoutes;
import com.huajian.common.route.TechRoutes;
import com.huajian.common.route.WareRoutes;
import com.huajian.common.route.WebRoutes;
import com.huajian.dict.model.GlueSetDetail;
import com.huajian.dict.model.StructDesc;
import com.huajian.dict.model.SysSeqno;
import com.huajian.system.model.Dept;
import com.huajian.system.model.Log;
import com.huajian.system.model.Menu;
import com.huajian.system.model.Role;
import com.huajian.system.model.RoleMenu;
import com.huajian.system.model.User;
import com.huajian.system.model.UserLoginLog;
import com.huajian.system.model.UserRole;
import com.alibaba.druid.filter.stat.StatFilter;
import com.jfinal.config.Constants;
import com.jfinal.config.Handlers;
import com.jfinal.config.Interceptors;
import com.jfinal.config.JFinalConfig;
import com.jfinal.config.Plugins;
import com.jfinal.config.Routes;
import com.jfinal.core.JFinal;
import com.jfinal.ext.handler.ContextPathHandler;
import com.jfinal.ext.plugin.shiro.ShiroPlugin3;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.cron4j.Cron4jPlugin;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.plugin.ehcache.EhCachePlugin;
import com.jfinal.render.ViewType;
import com.jfinal.template.Engine;
public class AppConfig extends JFinalConfig{
private static Prop p = PropKit.use("config.properties"); // 加载配置文件
/**
* 供Shiro插件使用
*/
private static Routes routes;
/**
* 配置常量
*/
@Override
public void configConstant(Constants me) {
me.setDevMode(p.getBoolean("devMode", false));
//me.setBaseUploadPath("upload"); //设置上传文件保存的基础路径
me.setViewType(ViewType.JFINAL_TEMPLATE);
me.setError500View("/error/500.html");
me.setError403View("/error/403.html");
me.setError404View("/error/404.html");
me.setError401View("/error/401.html");
me.setBaseDownloadPath("/download");
}
/**
* 配置路由
*/
@Override
public void configRoute(Routes me) {
this.routes = me;
me.add(new WebRoutes()); //前台管理
me.add(new AdminRoutes()); //后台管理
me.add(new SaleRoutes()); //销售部门
me.add(new TechRoutes()); //技术部门
me.add(new QaRoutes()); //质量部门
me.add(new ProdRoutes()); //生产部门
me.add(new WareRoutes()); //仓库部门
me.add(new BuyRoutes()); //采购部门
}
public static DruidPlugin createDruidPlugin() {
return new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password").trim());
}
/**
* 配置插件
*/
@Override
public void configPlugin(Plugins me) {
// 配置Druid数据库连接池插件
DruidPlugin druidPlugin = createDruidPlugin();
druidPlugin.addFilter(new StatFilter()); // 添加 StatFilter 才会有统计数据
me.add(druidPlugin);
// 配置ActiveRecord插件
ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
String baseSqlTemplatePath = PathKit.getWebRootPath()+"/WEB-INF/sql";
arp.setBaseSqlTemplatePath(baseSqlTemplatePath);
arp.addSqlTemplate("_common.sql");
// File[] files = new File(baseSqlTemplatePath).listFiles();
// for(File f : files) {
// if(f.isFile() && f.getName().endsWith(".sql") ){
// arp.addSqlTemplate(f.getName());
// }
// }
if (p.getBoolean("devMode", false)) {
arp.setShowSql(true);
}
me.add(arp);
arp.addMapping("sys_user","id", User.class); // 映射sys_user 表到 User模型
arp.addMapping("sys_role","id", Role.class); // 映射sys_role 表到 Role模型
arp.addMapping("sys_menu","id", Menu.class); // 映射sys_menu 表到 Menu模型
arp.addMapping("sys_dept","id", Dept.class); // 映射sys_dept 表到 Dept模型
arp.addMapping("sys_user_role","id", UserRole.class); // 映射sys_user_role 表到 UserRole模型
arp.addMapping("sys_role_menu","id", RoleMenu.class); // 映射sys_role_menu 表到 RoleMenu模型
arp.addMapping("sys_log", "id",Log.class); //日志
arp.addMapping("user_login_log", "id",UserLoginLog.class); //日志
arp.addMapping("struct_desc", "id",StructDesc.class); //结构描述
arp.addMapping("sys_seq_no", "id",SysSeqno.class); //序列号
arp.addMapping("glue_set_detail", "id",GlueSetDetail.class); //胶配置详情
// 权限系统相关表以外的所有配置在_MappingKit中通过_JFinalGenerator自动生成
_MappingKit.mapping(arp);
//配置任务调度插件 每小时执行一次
//me.add(new Cron4jPlugin(p));
me.add(new EhCachePlugin());
// 开启shiro插件控制权限
me.add(new ShiroPlugin3(routes));
}
/**
* 配置全局拦截器
*/
@Override
public void configInterceptor(Interceptors me) {
//添加控制层全局拦截器
me.add(new GlobalInterceptor());
//me.add(new LoginInterceptor());
//me.addGlobalActionInterceptor(new LoginInterceptor());
// 添加事物,对save、update和delete自动进行拦截
//me.add(new TxByMethods("save", "update", "delete","deleteBatch","updateBatch"));
}
/**
* 配置处理器
*/
@Override
public void configHandler(Handlers me) { // index、detail 两类 action 的 url seo
//me.add(new FakeStaticHandler(".do")); // http://localhost:8080/jfinal_pkm/user/login.do
me.add(new ContextPathHandler("ctx"));
}
/**
* 配置模板引擎,通常情况只需配置共享的模板函数
*/
@Override
public void configEngine(Engine me) {
me.addSharedFunction("/common/_title.html");
me.addSharedFunction("/common/_core.html");
me.addSharedFunction("/common/_home.html");
me.addSharedFunction("/common/_normal.html");
me.addSharedFunction("/common/_header.html");
me.addSharedFunction("/common/_footer.html");
}
/**
* 本方法会在 jfinal 启动过程完成之后被回调,详见 jfinal 手册
*/
@Override
public void afterJFinalStart() {
}
/**
* 建议使用 JFinal 手册推荐的方式启动项目
* 运行此 main 方法可以启动项目,此main方法可以放置在任意的Class类定义中,不一定要放于此
*/
public static void main(String[] args) {
/**
* 特别注意:Eclipse 之下建议的启动方式
*/
JFinal.start("WebRoot", 80, "/", 5);
/**
* 特别注意:IDEA 之下建议的启动方式,仅比 eclipse 之下少了最后一个参数
*/
// JFinal.start("WebRoot", 80, "/");
}
}
在这个类中,由于public void configPlugin(Plugins me) 先执行,所以me.add(new ShiroPlugin3(routes));这个地方传入shiro插件中的routes=null。
* Copyright (c) 2011-2017, dafei 李飞 (myaniu AT gmail DOT com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.jfinal.ext.plugin.shiro;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;
import com.jfinal.config.Routes;
import com.jfinal.config.Routes.Route;
import com.jfinal.core.ActionKey;
import com.jfinal.core.Controller;
import com.jfinal.plugin.IPlugin;
/**
* Shiro插件,启动时加载所有Shiro访问控制注解。
*
* @author dafei
*
*/
@SuppressWarnings("unchecked")
public class ShiroPlugin3 implements IPlugin {
/**
* 登录成功时所用的页面。
*/
private String successUrl = "/";
/**
* 登录成功时所用的页面。
*/
private String loginUrl = "/";
/**
* action的扩展名,比如.action或者 .do
*/
private String extName = "";
/**
* 登录成功时所用的页面。
*/
private String unauthorizedUrl = "/401.jsp";
private final String SLASH = "/";
/**
* Shiro的几种访问控制注解
*/
private static final Class[] AUTHZ_ANNOTATION_CLASSES = new Class[] {
RequiresPermissions.class, RequiresRoles.class, RequiresUser.class, RequiresGuest.class,
RequiresAuthentication.class };
/**
* 路由设定
*/
private final Routes routes;
/**
* 构造函数
*
* @param routes
* 路由设定
*/
public ShiroPlugin3(Routes routes) {
this.routes = routes;
}
/**
* 停止插件
*/
public boolean stop() {
return true;
}
/**
* 启动插件
*/
public boolean start() {
Set excludedMethodName = buildExcludedMethodName();
ConcurrentMap authzMaps = new ConcurrentHashMap();
//逐个访问所有注册的Controller,解析Controller及action上的所有Shiro注解。
//并依据这些注解,actionKey提前构建好权限检查处理器。
for (Routes routes : getRoutesList()) {
for (Routes.Route route : routes.getRouteItemList()) {
Class controllerClass = route.getControllerClass();
String controllerKey = route.getControllerKey();
// 获取Controller的所有Shiro注解。
List controllerAnnotations = getAuthzAnnotations(controllerClass);
// 逐个遍历方法。
Method[] methods = controllerClass.getMethods();
for (Method method : methods) {
//排除掉Controller基类的所有方法,并且只关注没有参数的Action方法。
if (!excludedMethodName.contains(method.getName())
&& method.getParameterTypes().length == 0) {
//若该方法上存在ClearShiro注解,则对该action不进行访问控制检查。
if (isClearShiroAnnotationPresent(method)) {
continue;
}
//获取方法的所有Shiro注解。
List methodAnnotations = getAuthzAnnotations(method);
//依据Controller的注解和方法的注解来生成访问控制处理器。
AuthzHandler authzHandler = createAuthzHandler(controllerAnnotations, methodAnnotations);
//生成访问控制处理器成功。
if (authzHandler != null) {
//构建ActionKey,参考ActionMapping中实现
String actionKey = createActionKey(controllerClass, method, controllerKey);
//添加映射
authzMaps.put(actionKey, authzHandler);
}
}
}
}
}
//注入到ShiroKit类中。ShiroKit类以单例模式运行。
ShiroKit.init(authzMaps);
// ShiroKit.init(jdbcAuthzService, authzMaps, false);
/**
* 设定登录,登录成功,未授权等url地址
*/
ShiroKit.setLoginUrl(loginUrl);
ShiroKit.setSuccessUrl(successUrl);
ShiroKit.setUnauthorizedUrl(unauthorizedUrl);
return true;
}
/**
* 获取所有配置的Route
* @return
*/
private List getRoutesList() {
List routesList = Routes.getRoutesList();
List ret = new ArrayList(routesList.size() + 1);
ret.add(routes);
ret.addAll(routesList);
return ret;
}
/**
* 从Controller方法中构建出需要排除的方法列表
*
* @return
*/
private Set buildExcludedMethodName() {
Set excludedMethodName = new HashSet();
Method[] methods = Controller.class.getMethods();
for (Method m : methods) {
if(Modifier.isPublic(m.getModifiers())) {
excludedMethodName.add(m.getName());
}
}
return excludedMethodName;
}
/**
* 依据Controller的注解和方法的注解来生成访问控制处理器。
*
* @param controllerAnnotations
* Controller的注解
* @param methodAnnotations
* 方法的注解
* @return 访问控制处理器
*/
private AuthzHandler createAuthzHandler(List controllerAnnotations,
List methodAnnotations) {
// 没有注解
if (controllerAnnotations.size() == 0 && methodAnnotations.size() == 0) {
return null;
}
// 至少有一个注解
List authzHandlers = new ArrayList(AUTHZ_ANNOTATION_CLASSES.length);
for (int index = 0; index < AUTHZ_ANNOTATION_CLASSES.length; index++) {
authzHandlers.add(null);
}
// 逐个扫描注解,若是相应的注解则在相应的位置赋值。
scanAnnotation(authzHandlers, controllerAnnotations);
// 逐个扫描注解,若是相应的注解则在相应的位置赋值。函数的注解优先级高于Controller
scanAnnotation(authzHandlers, methodAnnotations);
// 去除空值
List finalAuthzHandlers = new ArrayList();
for (AuthzHandler a : authzHandlers) {
if (a != null) {
finalAuthzHandlers.add(a);
}
}
authzHandlers = null;
// 存在多个,则构建组合AuthzHandler
if (finalAuthzHandlers.size() > 1) {
return new CompositeAuthzHandler(finalAuthzHandlers);
}
// 一个的话直接返回
return finalAuthzHandlers.get(0);
}
/**
* 逐个扫描注解,若是相应的注解则在相应的位置赋值。 注解的处理是有顺序的,依次为RequiresRoles,RequiresPermissions,
* RequiresAuthentication,RequiresUser,RequiresGuest
*
* @param authzArray
* @param annotations
*/
private void scanAnnotation(List authzArray, List annotations) {
if (null == annotations || 0 == annotations.size()) {
return;
}
for (Annotation a : annotations) {
if (a instanceof RequiresRoles) {
authzArray.set(0, new RoleAuthzHandler(a));
} else if (a instanceof RequiresPermissions) {
authzArray.set(1, new PermissionAuthzHandler(a));
} else if (a instanceof RequiresAuthentication) {
authzArray.set(2, AuthenticatedAuthzHandler.me());
} else if (a instanceof RequiresUser) {
authzArray.set(3, UserAuthzHandler.me());
} else if (a instanceof RequiresGuest) {
authzArray.set(4, GuestAuthzHandler.me());
}
}
}
/**
* 构建actionkey,参考ActionMapping中的实现。
*
* @param controllerClass
* @param method
* @param controllerKey
* @return
*/
private String createActionKey(Class controllerClass, Method method, String controllerKey) {
String methodName = method.getName();
String actionKey = "";
ActionKey ak = method.getAnnotation(ActionKey.class);
if (ak != null) {
actionKey = ak.value().trim();
if ("".equals(actionKey))
throw new IllegalArgumentException(controllerClass.getName() + "." + methodName
+ "(): The argument of ActionKey can not be blank.");
if (!actionKey.startsWith(SLASH))
actionKey = SLASH + actionKey;
} else if (methodName.equals("index")) {
actionKey = controllerKey;
} else {
actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
}
return actionKey;
}
/**
* 返回该方法的所有访问控制注解
*
* @param method
* @return
*/
private List getAuthzAnnotations(Method method) {
List annotations = new ArrayList();
for (Class annClass : AUTHZ_ANNOTATION_CLASSES) {
Annotation a = method.getAnnotation(annClass);
if (a != null) {
annotations.add(a);
}
}
return annotations;
}
/**
* 返回该Controller的所有访问控制注解
*
* @param method
* @return
*/
private List getAuthzAnnotations(Class targetClass) {
List annotations = new ArrayList();
for (Class annClass : AUTHZ_ANNOTATION_CLASSES) {
Annotation a = targetClass.getAnnotation(annClass);
if (a != null) {
annotations.add(a);
}
}
return annotations;
}
/**
* 该方法上是否有ClearShiro注解
*
* @param method
* @return
*/
private boolean isClearShiroAnnotationPresent(Method method) {
Annotation a = method.getAnnotation(ClearShiro.class);
if (a != null) {
return true;
}
return false;
}
/**
* 登录成功连接
*
* @param successUrl
*/
public final void setSuccessUrl(String successUrl) {
this.successUrl = successUrl;
}
/**
* 登录连接
*
* @param loginUrl
*/
public final void setLoginUrl(String loginUrl) {
this.loginUrl = loginUrl;
}
/**
* 未授权连接
*
* @param unauthorizedUrl
*/
public final void setUnauthorizedUrl(String unauthorizedUrl) {
this.unauthorizedUrl = unauthorizedUrl;
}
/**
* 扩展名,比如.action/.do
*
* @param extName
*/
public final void setExtName(String extName) {
this.extName = extName;
}
}
这样在这个类中,由于在构造函数中传入的routes=null,所以在start函数中,由于routes为null,异常退出。导致启动shiro插件失败。这个问题该如何解决?