将shiro作为插件整合到jfinal,在jfinal里读取shiro的注解

学习了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容器,即会通过反射给每个类创建一个实例,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
  </listener>
  <filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <filter>
    <filter-name>jfinal</filter-name>
    <filter-class>com.jfinal.core.JFinalFilter</filter-class>
    <init-param>
      <param-name>configClass</param-name>
      <param-value>com.learnging.system.LearngingConfig</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>jfinal</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

这里它会生成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()
{
    List<IPlugin> pluginList = 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()
{
    Set<String> excludedMethodName = buildExcludedMethodName();
    ConcurrentMap<String, AuthzHandler> authzMaps = new ConcurrentHashMap<String, AuthzHandler>();
    //逐个访问所有注册的Controller,解析Controller及action上的所有Shiro注解。
    //并依据这些注解,actionKey提前构建好权限检查处理器。
    for (Route route : routes.getRouteItemList())
    {
        Class <? extends Controller > controllerClass = route.getControllerClass();

        String controllerKey = route.getControllerKey();

        // 获取Controller的所有Shiro注解。
        List<Annotation> 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<Annotation> 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(
    List<Annotation> controllerAnnotations,
    List<Annotation> methodAnnotations)
{

    //没有注解
    if (controllerAnnotations.size() == 0 && methodAnnotations.size() == 0)
    {
        return null;
    }
    //至少有一个注解
    List<AuthzHandler> authzHandlers = new ArrayList<AuthzHandler>(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<AuthzHandler> finalAuthzHandlers = new ArrayList<AuthzHandler>();
    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 <? extends Controller > 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下载地址:http://git.oschina.net/myaniu/jfinalshiroplugin

转载于:https://my.oschina.net/u/2427561/blog/1524101

JFinal是一款基于Java的轻量级Web开发框架,而Shiro是一个强大且易用的Java安全框架。集成JFinalShiro可以为你的应用程序提供更好的安全性和权限控制。 要在JFinal中集成Shiro,你需要进行以下步骤: 1. 添加Shiro依赖:在你的项目中添加Shiro的依赖,可以通过Maven或者手动下载jar包的方式引入。 2. 创建Shiro配置类:创建一个继承自JFinalJFinalConfig类,并重写configConstant()和configInterceptor()方法。在configConstant()方法中配置Shiro的相关参数,如设置登录页面、未授权页面等。在configInterceptor()方法中添加Shiro的拦截器,用于实现权限控制。 3. 创建ShiroRealm类:创建一个继承自org.apache.shiro.realm.AuthorizingRealm的类,用于实现用户认证和授权逻辑。在该类中,你需要重写doGetAuthenticationInfo()方法用于用户认证,以及重写doGetAuthorizationInfo()方法用于用户授权。 4. 配置ShiroFilter:在JFinal的configRoute()方法中配置ShiroFilter,用于拦截请求并进行权限验证。你可以通过配置URL的匹配规则和相应的权限要求来实现不同页面的权限控制。 5. 配置登录和注销功能:在JFinal的Controller中添加登录和注销的处理逻辑,包括用户登录验证、生成和保存用户的身份信息等。 6. 配置权限注解:使用Shiro注解来标记需要进行权限验证的方法或类,以实现细粒度的权限控制。 以上是集成JFinalShiro的基本步骤,你可以根据具体需求进行更详细的配置和扩展。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值