Jfinal多模块项目整合shiro

需要的shiro相关的jar包

<properties>

    <shiro.version>1.3.2</shiro.version>

    <!--<jfinal.shiro.version>0.3</jfinal.shiro.version>-->

    <javax.servlet.version>4.0.0</javax.servlet.version> <!-- shiro  -->

    <commons-logging.version>1.2</commons-logging.version> <!-- shiro -->

    <ehcache.version>2.6.11</ehcache.version>

</properties>

 

<dependency>

    <groupId>net.sf.ehcache</groupId>

    <artifactId>ehcache-core</artifactId>

    <version>${ehcache.version}</version>

</dependency>



<!-- Shiro Start -->

<dependency>

    <groupId>org.apache.shiro</groupId>

    <artifactId>shiro-core</artifactId>

    <version>${shiro.version}</version>

</dependency>

<dependency>

    <groupId>org.apache.shiro</groupId>

    <artifactId>shiro-web</artifactId>

    <version>${shiro.version}</version>

</dependency>

<dependency>

    <groupId>org.apache.shiro</groupId>

    <artifactId>shiro-ehcache</artifactId>

    <version>${shiro.version}</version>

</dependency>



<dependency>

    <groupId>org.apache.shiro</groupId>

    <artifactId>shiro-cas</artifactId>

    <version>${shiro.version}</version>

    <exclusions>

        <exclusion>

            <groupId>commons-logging</groupId>

            <artifactId>commons-logging</artifactId>

        </exclusion>

    </exclusions>

</dependency>



<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>${javax.servlet.version}</version>

    <scope>provided</scope>

</dependency>

<dependency>

    <groupId>commons-logging</groupId>

    <artifactId>commons-logging</artifactId>

    <version>${commons-logging.version}</version>

</dependency>

<!-- Shiro End -->

 

一,ehcache-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"
         monitoring="autodetect" dynamicConfig="true">

    <!--
       缓存配置
       name:缓存名称。
       maxElementsInMemory:缓存最大个数。
       eternal:对象是否永久有效,一但设置了,timeout将不起作用。
       timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
       timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
       overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
       diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
       maxElementsOnDisk:硬盘最大缓存个数。
       diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
       memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
       clearOnFlush:内存数量最大时是否清除。
    -->

    <!-- 默认缓存 -->
    <defaultCache maxElementsInMemory="1000" eternal="false"
                  overflowToDisk="true" timeToIdleSeconds="300" timeToLiveSeconds="180"
                  diskPersistent="false" diskExpiryThreadIntervalSeconds="120" />

    <!-- 登录记录缓存 -->
    <cache name="passwordRetryCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <!-- 授权缓存 -->
    <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <!-- 认证缓存 -->
    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <!-- 活动会话缓存 -->
    <cache name="shiro-activeSessionCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
</ehcache>

二,shiro.ini

[main]
#path
#认证失败后默认redirect到/login.jsp.这里修改默认项
authc.loginUrl = /login
#登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。
authc.successUrl = /loginSuccess

#realm
myRealm = com.weixun.admin.shiro.ShiroDbRealm

#密码加密  加密方式 salt自定义、hash次数为1、加密算法为md5
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=MD5
credentialsMatcher.hashIterations=1
myRealm.credentialsMatcher=$credentialsMatcher

securityManager.realm = $myRealm

#cache
shiroCacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
shiroCacheManager.cacheManagerConfigFile = classpath:ehcache-shiro.xml
securityManager.cacheManager = $shiroCacheManager

#session
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionDAO.activeSessionsCacheName = shiro-activeSessionCache
sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.globalSessionTimeout = 360000

#这里的规则,web.xml中的配置的ShiroFilter会使用到。
[urls]
#登陆页面 登陆操作只进行用户身份认证
#anon表示此地址不需要任何权限即可访问
/static/**=anon
#配置所有webservice请求不需要权限
/services/**=anon
#验证码
/code=anon
#加载框架主页方法
/index=anon
#登录
/login = anon
/login.jsp = anon
/msg/** = anon
#所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login
/** = authc

 

三,在web.xml中配置shiro

<?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"
         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">
  <display-name>wx-app</display-name>
  <welcome-file-list>
    <welcome-file>login.jsp</welcome-file>
  </welcome-file-list>

  <!--shiro配置文件-->
    <listener>
      <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>
    <filter>
      <filter-name>shiro</filter-name>
      <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>shiro</filter-name>
      <url-pattern>/*</url-pattern>
      <dispatcher>REQUEST</dispatcher>
      <dispatcher>FORWARD</dispatcher>
      <dispatcher>INCLUDE</dispatcher>
      <dispatcher>ERROR</dispatcher>
    </filter-mapping>
 <!--shiro配置结束-->

  <!-- session过滤器配置相关 -->
  <filter>
    <filter-name>SessionFilter</filter-name>
    <filter-class>com.weixun.admin.filter.SessionFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>SessionFilter</filter-name>
    <url-pattern>*.action</url-pattern>
    <!--<dispatcher>FORWARD</dispatcher>-->
    <!--在这种情况下,如果请求是以/contract/…或者/user/…开头的,并且是通过request dispatcher的forward方法传递过来或者直接从客户端传递过来的,则必须经过这个过滤器。-->
    <!--<dispatcher>REQUEST</dispatcher>-->
  </filter-mapping>


  <!--jfinal 配置-->
    <filter>
      <filter-name>jfinal</filter-name>
      <filter-class>com.jfinal.core.JFinalFilter</filter-class>
      <init-param>
        <param-name>configClass</param-name>
        <param-value>com.weixun.core.config.AppConfig</param-value>
      </init-param>
    </filter>
    <filter-mapping>
      <filter-name>jfinal</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>
  <!--jfinal 配置结束-->
</web-app>

 

四,在jfinal项目的config类中加入缓存和shiro插件,并将路由添加到集合中

package com.weixun.core.config;

/**
 * Created by myd on 2017/5/14.
 */

import com.jfinal.config.*;
import com.jfinal.core.JFinal;
import com.jfinal.ext.handler.ContextPathHandler;
import com.jfinal.ext.plugin.shiro.ShiroInterceptor;
import com.jfinal.ext.plugin.shiro.ShiroPlugin;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.plugin.druid.DruidStatViewHandler;
import com.jfinal.plugin.ehcache.EhCachePlugin;
import com.jfinal.render.ViewType;
import com.jfinal.template.Engine;
import com.weixun.admin.route.AdminRoutes;
import com.weixun.cms.route.CmsRoutes;
import com.weixun.model._MappingKit;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by myd on 2016/12/31.
 */
public class AppConfig extends JFinalConfig {
    private Routes route = null;
    private static List<Routes> routeList = new ArrayList<>();
    /**
     * Config constant
     *
     * @param me
     */
    @Override
    public void configConstant(Constants me) {
        PropKit.use("jdbc.properties");
        String site_path = PropKit.get("site_path");
        me.setDevMode(true);
        me.setEncoding("UTF-8");
        me.setViewType(ViewType.JSP);
        me.setBaseUploadPath(site_path);
    }

    /**
     * Config route
     *
     * @param me
     */
    @Override
    public void configRoute(Routes me) {
        routeList.add(new AdminRoutes());
        routeList.add(new CmsRoutes());
        //加入admin模块的路由
        me.add(new AdminRoutes());
        //加入cms模块的路由
        me.add(new CmsRoutes());
//        this.route=me;
    }

    @Override
    public void configEngine(Engine engine) {

    }

    /**
     * Config plugin
     *
     * @param plugins
     */
    @Override
    public void configPlugin(Plugins plugins) {
        // DruidPlugin
        PropKit.use("jdbc.properties");


        final String URL = PropKit.get("jdbcUrl");
        final String USERNAME = PropKit.get("user");
        final String PASSWORD = PropKit.get("password");

//        final String DRIVER1 = PropKit.get("oracle.driver");
//        final String URL1 = PropKit.get("oracle.url1");
//        final String USERNAME1 = PropKit.get("oracle.user1");
//        final String PASSWORD1 = PropKit.get("oracle.password1");
//
//        final String URL2 = PropKit.get("oracle.url2");
//        final String USERNAME2 = PropKit.get("oracle.user2");
//        final String PASSWORD2 = PropKit.get("oracle.password2");

        final Integer INITIALSIZE = PropKit.getInt("initialSize");
        final Integer MIDIDLE = PropKit.getInt("minIdle");
        final Integer MAXACTIVEE = PropKit.getInt("maxActivee");

        DruidPlugin druidPlugin = new DruidPlugin(URL,USERNAME,PASSWORD);
        druidPlugin.set(INITIALSIZE,MIDIDLE,MAXACTIVEE);
        druidPlugin.setFilters("stat,wall");
        plugins.add(druidPlugin);

//        DruidPlugin dp = new DruidPlugin(URL1,USERNAME1,PASSWORD1,DRIVER1);
//        dp.set(INITIALSIZE,MIDIDLE,MAXACTIVEE);
//        dp.setFilters("stat,wall");
//        plugins.add(dp);
//
//        DruidPlugin druidPlugin2 = new DruidPlugin(URL2,USERNAME2,PASSWORD2,DRIVER1);
//        druidPlugin2.set(INITIALSIZE,MIDIDLE,MAXACTIVEE);
//        druidPlugin2.setFilters("stat,wall");
//        plugins.add(druidPlugin2);


        //多数据源的配置
        ActiveRecordPlugin arp = new ActiveRecordPlugin("datasource",druidPlugin);
        arp.setShowSql(true);//显示sql语句
        plugins.add(arp);

        //oracle数据源
//        ActiveRecordPlugin arp1 = new ActiveRecordPlugin("datasource1",dp);
//        plugins.add(arp1);
//
//        ActiveRecordPlugin arp2 = new ActiveRecordPlugin("datasource2",druidPlugin2);
//        plugins.add(arp2);
//        new AutoBindModels(arp1);

        // 所有配置在 MappingKit 中搞定
        _MappingKit.mapping(arp);
//        _MappingKit.mapping(arp1);
//        _MappingKit.mapping(arp2);
        //配置缓存插件
        plugins.add(new EhCachePlugin());
        //在configPlugin方法内使用
        plugins.add(new ShiroPlugin(routeList));

//        ShiroPlugin shiroPlugin = new ShiroPlugin(this.);
//        plugins.add(shiroPlugin);

//        //加载Shiro插件
//        plugins.add(new ShiroPlugin(routes));
    }


    /**
     * Config interceptor applied to all actions.
     *
     * @param me
     */
    @Override
    public void configInterceptor(Interceptors me) {
        me.add(new ShiroInterceptor());
    }

    /**
     * Config handler
     *
     * @param me
     */
    @Override
    public void configHandler(Handlers me) {
        me.add(new ContextPathHandler("basePath"));
        DruidStatViewHandler dvh =  new DruidStatViewHandler("/druid");
        me.add(dvh);
    }

    /**
     * 需要启动的目录
     * 端口
     * 请求的路径
     * 时间
     * @param args
     */
    public static void main(String[] args)
    {
        JFinal.start("web",8090,"/web",5);
    }
}

五,下载jfinalshiro源代码添加到项目中,下载地址为:https://gitee.com/myaniu/jfinalshiroplugin

六,maven多模块下需要修改jfinalshiro中ShiroPlugin的源代码,单模块则不需要修改该插件的源码,修改后的代码如下,主要修改了获取路由集合

/**
 * 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 ShiroPlugin 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<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES = new Class[] {
            RequiresPermissions.class, RequiresRoles.class, RequiresUser.class, RequiresGuest.class,
            RequiresAuthentication.class };

    /**
     * 路由设定
     */
    private static Routes routes=null;
    private static List<Routes> routeList=null;

    /**
     * 构造函数
     * 
     * @param routes
     *            路由设定
     */
    public ShiroPlugin(Routes routes) {
        this.routes = routes;
    }

    public ShiroPlugin(List<Routes> routeList) {
        this.routeList = routeList;
    }
    /**
     * 停止插件
     */
    public boolean stop() {
        return true;
    }

    /**
     * 启动插件
     */
    public boolean start() {
        Set<String> excludedMethodName = buildExcludedMethodName();
        ConcurrentMap<String, AuthzHandler> authzMaps = new ConcurrentHashMap<String, AuthzHandler>();
        // 逐个访问所有注册的Controller,解析Controller及action上的所有Shiro注解。
        // 并依据这些注解,actionKey提前构建好权限检查处理器。
        for (Routes rs : getRoutesList()) {
            for (Route route : rs.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基类的所有方法,并且只关注public方法。
                    if (Modifier.isPublic(method.getModifiers()) && !excludedMethodName.contains(method.getName())) {
                        // 若该方法上存在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;
    }
    
    /**
     * 获取所有配置的Route
     * @return
     */
//    private List<Routes> getRoutesList() {
//        List<Routes> routesList = Routes.getRoutesList();
//        List<Routes> ret = new ArrayList<Routes>(routesList.size() + 1);
//        ret.add(this.routes);
//        ret.addAll(routesList);
//        return ret;
//    }

    private List<Routes> getRoutesList() {
//        List<Routes> routesList = this.routeList;
        List routesList = Routes.getRoutesList();
        List<Routes> ret = new ArrayList<Routes>(routesList.size());
//        ret.add(this.routes);
//        ret.addAll(routesList);
        ret.addAll(routesList);
        return ret;
    }

    /**
     * 从Controller方法中构建出需要排除的方法列表
     * 
     * @return
     */
    private Set<String> buildExcludedMethodName() {
        Set<String> excludedMethodName = new HashSet<String>();
        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<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);
    }

    /**
     * 逐个扫描注解,若是相应的注解则在相应的位置赋值。 注解的处理是有顺序的,依次为RequiresRoles,RequiresPermissions,
     * RequiresAuthentication,RequiresUser,RequiresGuest
     *
     * @param authzArray
     * @param annotations
     */
    private void scanAnnotation(List<AuthzHandler> authzArray, List<Annotation> 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<? 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;
    }

    /**
     * 返回该方法的所有访问控制注解
     *
     * @param method
     * @return
     */
    private List<Annotation> getAuthzAnnotations(Method method) {
        List<Annotation> annotations = new ArrayList<Annotation>();
        for (Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES) {
            Annotation a = method.getAnnotation(annClass);
            if (a != null) {
                annotations.add(a);
            }
        }
        return annotations;
    }

    /**
     * 返回该Controller的所有访问控制注解
     *
     * @param
     * @return
     */
    private List<Annotation> getAuthzAnnotations(Class<? extends Controller> targetClass) {
        List<Annotation> annotations = new ArrayList<Annotation>();
        for (Class<? extends Annotation> 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;
    }
}

 

七,shiroDbRealm实现类

package com.weixun.admin.shiro;

/**
 * Created by myd on 2017/7/17.
 */

import com.jfinal.plugin.activerecord.Record;
import com.weixun.comm.model.vo.Staff;
import com.weixun.admin.model.vo.TreeView;
import com.weixun.admin.service.MenuService;
import com.weixun.admin.service.RoleService;
import com.weixun.admin.service.StaffService;
import com.weixun.model.SysMenu;
import com.weixun.model.SysRole;
import com.weixun.utils.md5.Md5Utils;
import com.weixun.utils.record.RecordUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.ArrayList;
import java.util.List;


public class ShiroDbRealm extends AuthorizingRealm {

    /**
     * 菜单操作类
     */
    static MenuService menuService = new MenuService();

    /**
     * 员工信息操作类
     */
    StaffService staffService = new StaffService();

    /**
     * 角色信息操作类
     */
    RoleService roleService = new RoleService();
    /**
     * 认证回调函数,登录信息和用户验证信息验证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authcToken) throws AuthenticationException {
        System.out.println("##### 身份证认证 #####");
        // 把token转换成User对象
//        Staff userLogin = tokenToUser((UsernamePasswordToken) authcToken);
        UsernamePasswordToken userLogin = (UsernamePasswordToken) authcToken;
        Record record = staffService.find(userLogin.getUsername(),"");
        Staff staff = RecordUtils.converModel(Staff.class,record);
        if(staff != null) {
            //检测密码是否正确交给凭证匹配 --加密方式 salt = username 、hash次数为3、加密算法为md5
            Object principal = authcToken.getPrincipal();
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,
                    staff.getStaff_password(),this.getName());


            ShiroCache.setLoginUser(staff);


            //预加载角色权限,存入用户缓存
            List<SysRole> roles = roleService.findModelList(staff.getFk_roles_pk());
            ShiroCache.setAttribute(ShiroCache.LOGIN_ROLE,roles);
            //预加载角色资源(菜单)访问权限,存入缓存
            List<SysMenu> permissionAll = new ArrayList<SysMenu>();
            TreeView menu;
            for (SysRole role : roles) {
                menu = new TreeView();
                //按钮
                menu.setType("1");
                List<SysMenu> permissions = menuService.findRolesMenu(menu,role.getRolePk().toString());
                permissionAll.addAll(permissions);
            }
            ShiroCache.setAttribute(ShiroCache.LOGIN_PERMISSION,permissionAll);
            return info;
        }else {
            return null;
        }
    }

    /**
     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("##### 权限认证 #####");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(ShiroCache.getRoles());
        info.addStringPermissions(ShiroCache.getPermissions());
        return info;
    }

    private Staff tokenToUser(UsernamePasswordToken authcToken) {
        Staff user = new Staff();
        user.setStaff_loginname(authcToken.getUsername());
        //先转换为string
        String password = new String(((char[]) authcToken.getCredentials()));
        //将转换为string的字符串使用md5加密
        user.setStaff_password(Md5Utils.ToMd5(password,1));
        return user;
    }


}

八,相关的辅助类 ShiroCache

package com.weixun.admin.shiro;

import com.weixun.comm.model.vo.Staff;
import com.weixun.model.SysMenu;
import com.weixun.model.SysRole;
import com.weixun.model.SysStaff;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by zlx on 2017/12/13 0013.
 */
public class ShiroCache {

    public static final String LOGIN_USER = "loginUser";
    public static final String LOGIN_ROLE = "userRole";
    public static final String LOGIN_PERMISSION = "rolePermission";

    public static void setAttribute(Object key, Object value) {
        Subject account = SecurityUtils.getSubject();
        if (null != account) {
            Session session = account.getSession();
            if (null != session) {
                session.setAttribute(key, value);
            }
        }
    }

    public static Object getAttribute(Object key) {
        Subject account = SecurityUtils.getSubject();
        if (null != account) {
            Session session = account.getSession();
            if (null != session) {
                return session.getAttribute(key);
            }
        }
        return null;
    }

    public static Object removeAttribute(Object key) {
        Subject account = SecurityUtils.getSubject();
        if (null != account) {
            Session session = account.getSession();
            if (null != session) {
                return session.removeAttribute(key);
            }
        }
        return null;
    }

    public static boolean hasLogin() {
        return getAttribute(LOGIN_USER) != null ? true : false;
    }


    public static Staff getLoginUser() {
        return (Staff) getAttribute(LOGIN_USER);
    }
    public static void setLoginUser(Staff user) {
        setAttribute(LOGIN_USER, user);
    }

    public static Map<String,Object> getUserAll(){
        Subject account = SecurityUtils.getSubject();
        if (null != account) {
            Session session = account.getSession();
            if (null != session) {
                Map<String,Object> map = new HashMap<String,Object>();
                map.put(LOGIN_USER,session.getAttribute(LOGIN_USER));
                map.put(LOGIN_ROLE,session.getAttribute(LOGIN_ROLE));
                map.put(LOGIN_PERMISSION,session.getAttribute(LOGIN_PERMISSION));
                return map;
            }
        }
        return null;
    }

    public static void userLogout() {
        Subject account = SecurityUtils.getSubject();
        if (null != account) {
            Session session = account.getSession();
            if (null != session) {
                session.removeAttribute(LOGIN_USER);
                session.removeAttribute(LOGIN_ROLE);
                session.removeAttribute(LOGIN_PERMISSION);
            }
            account.logout();
        }
    }

    /**
     * 获取所有角色权限
     * @return
     */
    public static List<String> getRoles(){
        List<SysRole> roles = (List<SysRole>) getAttribute(LOGIN_ROLE);
        if(roles!=null){
            List<String> list = new ArrayList<>();
            for (SysRole role : roles) {
                list.add(role.getRoleName());
            }
            return list;
        }
        return  null;
    }

    /**
     * 获取所有 资源权限路径
     * @return
     */
    public static List<String> getPermissions(){
        List<SysMenu> permissions = (List<SysMenu>)getAttribute(LOGIN_PERMISSION);
        if(permissions!=null){
            List<String> list = new ArrayList<>();
            for (SysMenu permission : permissions) {
                list.add(permission.getMenuUrl());
            }
            return list;
        }
        return  null;
    }

    /**
     * 获取用户基础信息
     * @return
     */
    public static Map<String,Object> getUserInfo(){
        SysStaff user = (SysStaff) getAttribute(ShiroCache.LOGIN_USER);
        List<SysRole> roles = (List<SysRole>) getAttribute(LOGIN_ROLE);

        List<String> roleList = new ArrayList<String>();
        for (int i = 0; i < roles.size(); i++) {
            roleList.add(roles.get(i).getRoleName());
        }
        //用户基础信息
        Map<String, Object> userInfo = new HashMap<String, Object>();
//        userInfo.put("userAccount", user.getLoginAccount());
//        userInfo.put("userName", user.getUserName());
//        userInfo.put("userHead", user.getUserHead());
//        userInfo.put("userBirthday", user.getUserBirthday());
//        userInfo.put("userEmail", user.getUserEmail());
//        userInfo.put("userPhone", user.getUserPhone());
//        userInfo.put("userSex", user.getUserSex());
        userInfo.put("roles", roleList);
        return userInfo;
    }

    /**
     * 根据权限菜单生成树形菜单
     */
//    public static List<MenuTree> getMenus(){
//        List<SysPermission> permissions = (List<SysPermission>) ShiroCache.getAttribute(ShiroCache.LOGIN_PERMISSION);
//        Map<String,MenuTree> menuMap = new HashMap<String,MenuTree>();//一级菜单
//        List<MenuTree> items = new ArrayList<MenuTree>();//二级菜单
//        for (SysPermission per:permissions) {
//            String menuCode = per.getMenuCode();
//            String parentCode = per.getParentMenucode();
//            String menuType = per.getMenuType();
//            if("1".equals(menuType)){
//                MenuTree treeItem = new MenuTree();
//                treeItem.setMenuCode(per.getMenuCode());
//                treeItem.setMenuName(per.getMenuName());
//                treeItem.setClassIcon(per.getMenuClass());
//                treeItem.setDataUrl(per.getDataUrl());
//                treeItem.setSequence(per.getSequence());
//                if(StringUtil.isEmpty(parentCode)){
//                    menuMap.put(menuCode,treeItem);
//                }else{
//                    treeItem.setParentCode(per.getParentMenucode());
//                    items.add(treeItem);
//                }
//            }
//        }
//
//        for (MenuTree item:items){
//            MenuTree treeItem = menuMap.get(item.getParentCode());
//            if(treeItem!=null){
//                List<MenuTree> menus = treeItem.getMenus();
//                if(menus==null){
//                    menus = new ArrayList<MenuTree>();
//                    treeItem.setMenus(menus);
//                }
//                menus.add(item);
//            }
//        }
//        List<MenuTree> menus = new ArrayList<MenuTree>();
//        for (String key :menuMap.keySet()){
//            menus.add(menuMap.get(key));
//        }
//        return menus;
//    }
}

九,结合jfinal,shiro,layui实现的多模块源码地址如下:https://gitee.com/live.cn/wxcms.git

转载于:https://my.oschina.net/mayude/blog/1796798

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SSM项目整合Shiro的步骤如下: 1. 引入Shiro的依赖,可以在pom.xml文件中添加以下代码: ```xml <!--shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.9.0</version> </dependency> ``` 2. 在Spring配置文件中配置Shiro的相关信息,例如Shiro的安全管理器、Shiro的过滤器链等。可以在applicationContext.xml文件中添加以下代码: ```xml <!-- 配置Shiro的安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myRealm"/> </bean> <!-- 配置Shiro的自定义Realm --> <bean id="myRealm" class="com.example.shirodemo.realm.MyRealm"> <property name="credentialsMatcher" ref="hashedCredentialsMatcher"/> </bean> <!-- 配置Shiro的密码匹配器 --> <bean id="hashedCredentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="md5"/> <property name="hashIterations" value="2"/> </bean> <!-- 配置Shiro的过滤器链 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login"/> <property name="successUrl" value="/index"/> <property name="unauthorizedUrl" value="/unauthorized"/> <property name="filterChainDefinitions"> <value> /login = anon /logout = logout /** = authc </value> </property> </bean> ``` 3. 创建自定义Realm,继承org.apache.shiro.realm.Realm接口,并实现其中的方法。可以创建一个MyRealm类,代码如下: ```java public class MyRealm implements Realm { @Autowired private UserService userService; @Override public String getName() { return "myRealm"; } @Override public boolean supports(AuthenticationToken token) { return token instanceof UsernamePasswordToken; } @Override public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); String password = new String((char[]) token.getCredentials()); User user = userService.getUserByUsername(username); if (user == null) { throw new UnknownAccountException("用户名或密码错误!"); } if (!password.equals(user.getPassword())) { throw new IncorrectCredentialsException("用户名或密码错误!"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName()); return info; } } ``` 4. 在Controller中使用Shiro进行权限控制。可以在UserController中添加以下代码: ```java @Controller public class UserController { @RequestMapping("/user/list") @RequiresPermissions("user:list") public String list() { return "user/list"; } @RequestMapping("/user/add") @RequiresPermissions("user:add") public String add() { return "user/add"; } @RequestMapping("/user/edit") @RequiresPermissions("user:edit") public String edit() { return "user/edit"; } @RequestMapping("/user/delete") @RequiresPermissions("user:delete") public String delete() { return "user/delete"; } } ``` 以上是SSM项目整合Shiro的基本步骤,可以根据具体需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值