原文:https://michaelzx.github.io/2016/jfinal-shiro-integration/html
英文好的能够直接看官网教程,英文很差的能够看下开涛的博客《跟我学Shiro》系列java
在看教程以前,最好了解想一些shiro的概念:Apache Shiro Terminologygit
一开始的时候,我本身搞了一个JFinal的全局Interceptor
,用来作Shiro
的权限判断。缘由是一开始不了解Shiro,不知道运用Shiro
的Filter
,理所固然地从JFinal
的层面进行思考。github
固然,在JFinal
的Interceptor
中也是能够作一些权限的判断的,不过顺序上面先要通过Shiro
的Filter
,而后才会到JFinal
的Filter
,真正进入JFinal
。web
能够直接拿来用,能够作参考,我在实践的时候,也参考了很多ajax
项目地址:http://git.oschina.net/myaniu/jfinalshiroplugin数据库
项目地址:http://git.oschina.net/jayqqaa12/JFinal_Authorityexpress
项目地址:https://github.com/Dreampie/jfinal-shiroapache
部分现成方案中使用了Plugin、Interceptor、Annotation的方式来集成Shrio,我的不是很是喜欢,我的感受有2个痛点:缓存
一、须要好多代码去实现Plugin、Interceptor、Annotation,虽然别人写好了,可是你仍是得去了解源码,万一有个坑呢:),毕竟不是什么大而成熟且运用普遍的解决方案
二、你项目里有这么多代码,想一想一下
你得去找,去改,从新编译,从新部署。无论你疯不疯,反正我感受我会疯的。
既然Shiro是专业的那就让它作好该作的。
自定义Realm
+自定义filters
+url表达式
,来实现自定义的验证方式我如今使用是
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.4</version> </dependency>
<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> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
<listener> 节点定义了一个ServletContextListener,在web应用程序启动的生时候启动Shiro环境(包括shiro的SecurityManager)默认状况下, 这个listener会自动去找咱们的WEB-INF/classes/shiro.ini。
<filter> 节点定义了主要的ShiroFilter.这个filter被要求去过滤全部进入web应用程序的请求,所以shiro能够在一个请求到达应用程序以前进行必要的身份验证和访问控制。
<filter-mapping> 节点确保全部请求类型经过被ShiroFilterare提出(filed)filter-mapping节点通常是不指定dispatcher元素的,可是shiro须要它们都被定义,以便它可以过滤全部可能被web应用执行的不一样请求类型。
如下是我经常使用的shiro.ini
[main] #sessionId相关设定 sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookie sessionIdCookie.name=jshop-admin-web sessionIdCookie.path=/ sessionIdCookie.maxAge=1800 sessionIdCookie.httpOnly=true #sessionManager sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager sessionManager.sessionIdCookie = $sessionIdCookie sessionManager.sessionIdCookieEnabled = true sessionManager.globalSessionTimeout = 3600000 #Realm dbRealm = com.yourdomain.module.shiro.Realm #Cache cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager #--------------- securityManager.sessionManager=$sessionManager securityManager.realm = $dbRealm securityManager.cacheManager = $cacheManager #[filters] 这个已经被取消,并入main中 #自定义验证过滤器 app_auth= com.yourdomain.module.shiro.AuthorizeFilter #登陆页面 app_auth.loginUrl = /auth/login #未受权页面 app_auth.unauthorizedUrl=/auth/unauthorized [urls] /test/** = anon /public/** = anon /uploads/** = anon /passport/* = anon /** = app_auth
这俩个彷佛没啥好说的。
它定义了一个新的缓存管理实例. 缓存在Shiro的构架体系中是一个很是重要的部分 - 它减小了和数据存贮之间持续往返的通信。这个例子是使用了在单个JVM上比较好使的MemoryConstrainedCacheManager。若是对你的应用是部署在多个服务器(好比服务器集群)的话,你将会想使用一个集群缓存管理器的实现来替代。
它是很关键的一个地方,这是是须要本身实现的。它做为shiro的一个组件,可让shiro访问到你的系统中的用户、角色、权限等数据。
这是很是重要的一个节点,来配置哪些路径映射哪些过滤器来进行鉴权,能够用逗号分开,配置多个过滤器。
这里的anon是shiro内置的一个过滤器,表示不须要进行鉴权。固然仍是不少的shiro内置鉴权过滤器.在后面简单介绍下
1.2之后,filters被并入[main]节点,若是继续保留也没事儿,只是会出个警告而已。app_auth是我本身实现的一个filter系统中主要使用这个filter进行鉴权。
com.yourdomain.module.shiro.Realm
public class Realm extends AuthorizingRealm
主要是在登陆的时候,进行用户身份验证
/** * 获取用户验证信息 * @param authcToken 所需验证的token * @return null or 身份信息 * @throws AuthenticationException 验证异常 */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; SysAdmin admin = AdminService.getByUsername(token.getUsername()); if (admin != null) { if(!admin.getPassword().equals(String.valueOf(token.getPassword()))){ throw new AuthenticationException("密码错误"); } Db.update("update sys_admin set loginTime=?,loginCount=loginCount+1 where id=?",new Date(),admin.getId()); return new SimpleAuthenticationInfo(admin, admin.getPassword(),admin.getUsername()); } else { throw new AuthenticationException("用户不存在"); } }
在第一次鉴权的时候进行调用,获取并保存到chache中(没有配置cache是否是每次都得调用?)
/** * 获取用户受权信息 * @param principals 用户身份 * @return null or 受权信息 */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SysAdmin userInPrincipal = (SysAdmin) principals.getPrimaryPrincipal(); //根据用户获取权限 List<String> stringPermissions = AdminService.getPermissions(userInPrincipal.getId()); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //stringPermissions结构: //user //user:list //user:add //user:edit //... info.addStringPermissions(stringPermissions); return info; }
我这里返回的:字符串权限表达式(字符串通配符权限),对于各自所对应的资源(主要就是url路径),我是保存在数据库中,方便进行配置,而后再加上缓存。在自定义filter中将url转换成对应的表达式,而后进行鉴权。
public void login() { if(Boolean.FALSE.equals(validateCaptcha("captcha"))){ renderJson(CommonService.ajaxError("验证码错误")); return; } String username = getPara("username"); String password = HashKit.md5(getPara("password")); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); renderJson(CommonService.ajaxSuccess()); } catch (AuthenticationException e) { //虽然在realm中有具体的错误信息,可是安全起见,统一返回登陆失败 renderJson(CommonService.ajaxError("登录失败")); } }
public void logout() { Subject subject = SecurityUtils.getSubject(); subject.logout(); redirect("/passport/login"); }
是否是感受很easy:)
com.yourdomain.module.shiro.AuthorizeFilter
public class AuthorizeFilter extends AuthorizationFilter
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { //-----------------用户验证------------------ Subject currentUser = getSubject(request, response); if (!currentUser.isAuthenticated()) return false; //-----------------获取资源权限表达式------------- SysAdmin user = (SysAdmin) currentUser.getPrincipal(); //request中加入attribute便于controller调用admin的信息 request.setAttribute("admin",user); // 根据actionKey分析出权限表达式 HttpServletRequest hsr = ((HttpServletRequest) request); String root = hsr.getContextPath(); String URI = hsr.getRequestURI(); String actionKey = URI.replace(root,""); if("".equals(actionKey)) actionKey="/"; RoleService roleService = new RoleService(); String expression = roleService.getActionKeyExpression(actionKey); //-----------------进行鉴权------------- if (user==null) return false; else if(user.getStr("username").equals("superadmin")){ //超级管理员具备全部权限 return true; }else if(expression==null){ return false; }else if(currentUser.isPermitted(expression)){ //鉴权 return true; }else{ return false; } }
你能够按照你本身的方式来构建一套,我相信对于大多数人应该不成问题,由于这个已经不属于shiro的范畴了。本身搞几个表,搞几个配置界面,作下缓存策略等等。
有时候你的应用也许须要在界面上进行鉴权,好比按钮啥的,这时候就可能须要扩展模板引擎的函数或者标签。
能够参考下 《beetl 和 shrio 结合》 http://my.oschina.net/xiandafu/blog/143109
只要解决如何扩展模板引擎的函数或者标签,其余,我想应该都是雷同的吧。