学习了shiro的一些身份验证和授权知识,那么现在主要学习大飞怎么把shiro结合到jfinal,怎么在jfinal里使用shiro的注解的。
一、首先,要先了解一下jfinal启动插件的流程,才能好的理解jfinal怎么使用shiro,jfinal使用其他框架都采用Plugins的形式,在启动jfinal的时候,会依次启动在configPlugin里配置的插件,这个插件要实现接口IPlugin就可以,如public class ShiroPlugin implements IPlugin。 jfinal启动继承了IPlugin的插件的流程如下:
1、启动jfinal,会将web.xml的内容都初始化并加载到web容器,即会通过反射给每个类创建一个实例,如下所示:
org.apache.shiro.web.env.EnvironmentLoaderListener ShiroFilter org.apache.shiro.web.servlet.ShiroFilter ShiroFilter /* jfinal com.jfinal.core.JFinalFilter configClass com.learnging.system.LearngingConfig jfinal /*
这里它会生成JFinalFilter的实例,并调用JFinalFilter的init方法,这里init又委托给JFinal的init方法,如下:
void init(JFinalConfig jfinalConfig, ServletContext servletContext){ this.servletContext = servletContext; this.contextPath = servletContext.getContextPath(); initPathKit(); Config.configJFinal(jfinalConfig); // start plugin, init log factory and init engine in this method constants = Config.getConstants(); initActionMapping(); initHandler(); initRender(); initOreillyCos(); initTokenManager();}
JFinal的init方法又调用Config.configJFinal()方法,如下:
static void configJFinal(JFinalConfig jfinalConfig){ jfinalConfig.configConstant(constants); initLogFactory(); initEngine(); jfinalConfig.configRoute(routes); jfinalConfig.configEngine(engine); jfinalConfig.configPlugin(plugins); startPlugins(); // very important!!! jfinalConfig.configInterceptor(interceptors); jfinalConfig.configHandler(handlers);}
private static void startPlugins(){ ListpluginList = plugins.getPluginList(); if (pluginList == null) { return ; } for (IPlugin plugin : pluginList) { try { // process ActiveRecordPlugin devMode if (plugin instanceof com.jfinal.plugin.activerecord.ActiveRecordPlugin) { com.jfinal.plugin.activerecord.ActiveRecordPlugin arp = (com.jfinal.plugin.activerecord.ActiveRecordPlugin)plugin; if (arp.getDevMode() == null) { arp.setDevMode(constants.getDevMode()); } } if (plugin.start() == false) { String message = "Plugin start error: " + plugin.getClass().getName(); log.error(message); throw new RuntimeException(message); } } catch (Exception e) { String message = "Plugin start error: " + plugin.getClass().getName() + ". \n" + e.getMessage(); log.error(message, e); throw new RuntimeException(message, e); } }}
至此,启动完所有插件,只要一个插件启动失败,就会都失败。
二接下来就是将一个框架做成一个插件,整合到jfinal。大飞写的ShiroPlugin插件最主要的start方法,如下所示:
public boolean start(){ SetexcludedMethodName = buildExcludedMethodName(); ConcurrentMap authzMaps = new ConcurrentHashMap (); //逐个访问所有注册的Controller,解析Controller及action上的所有Shiro注解。 //并依据这些注解,actionKey提前构建好权限检查处理器。 for (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); /** * 设定登录,登录成功,未授权等url地址 */ ShiroKit.setLoginUrl(loginUrl); ShiroKit.setSuccessUrl(successUrl); ShiroKit.setUnauthorizedUrl(unauthorizedUrl); ShiroKit.setExtName(extName); return true;}
1、buildExcludedMethodName()取得controller里没有参数的方法,即action方法。
2、authzMaps用于保存cotroller和action及其上面对应的注解。
3、逐个访问所有注册的Controller,解析Controller及action上的所有Shiro注解。并依据这些注解,actionKey提前构建好权限检查处理器。
4、用controllerAnnotations保存Controller的所有Shiro注解。
5、用methodAnnotations保存不是基类,又没有参数的,又没有ClearShiro的方法的所有Shiro注解。
6、调用方法createAuthzHandler依据Controller的注解和方法的注解来生成访问控制处理器。以上authzHandlers.add(null);是因为注解的处理是有顺序的,依次为RequiresRoles,RequiresPermissions, RequiresAuthentication,RequiresUser,RequiresGuest。
/** * 依据Controller的注解和方法的注解来生成访问控制处理器。 * @param controllerAnnotations Controller的注解 * @param methodAnnotations 方法的注解 * @return 访问控制处理器 */private AuthzHandler createAuthzHandler( ListcontrollerAnnotations, 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);}
7、生成访问控制处理器成功,构建ActionKey,参考ActionMapping中实现,添加映射,大飞实现如下:
/** * 构建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;}
本博客代码主要来源于jfinal源码和大飞的开源代码。主要目的是学习.
JFinalShiroPlugin下载地址: