需要的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