续上一篇《JFianl整合Shiro(一)》
我准备在这里,具体的描述下JFianl整合Shiro的基本流程。
Maven Dependency
我现在使用是
1 2 3 4 5
| <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.4</version> </dependency>
|
配置web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <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>
|
节点定义了一个ServletContextListener,在web应用程序启动的生时候启动Shiro环境(包括shiro的SecurityManager)默认情况下, 这个listener会自动去找我们的WEB-INF/classes/shiro.ini。
节点定义了主要的ShiroFilter.这个filter被要求去过滤所有进入web应用程序的请求,因此shiro可以在一个请求到达应用程序之前进行必要的身份验证和访问控制。
节点确保所有请求类型通过被ShiroFilterare提出(filed)filter-mapping节点一般是不指定dispatcher元素的,但是shiro需要它们都被定义,以便它能够过滤所有可能被web应用执行的不同请求类型。
添加shiro.ini文件
以下是我常用的shiro.ini
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| [main]
sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookie sessionIdCookie.name=jshop-admin-web sessionIdCookie.path=/ sessionIdCookie.maxAge=1800 sessionIdCookie.httpOnly=true
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager sessionManager.sessionIdCookie = $sessionIdCookie sessionManager.sessionIdCookieEnabled = true sessionManager.globalSessionTimeout = 3600000
dbRealm = com.yourdomain.module.shiro.Realm
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.sessionManager=$sessionManager securityManager.realm = $dbRealm securityManager.cacheManager = $cacheManager
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
|
sessionIdCookie、sessionManager
这俩个似乎没啥好说的。
cacheManager
它定义了一个新的缓存管理实例. 缓存在Shiro的构架体系中是一个非常重要的部分 - 它减少了和数据存贮之间持续往返的通讯。这个例子是使用了在单个JVM上比较好使的MemoryConstrainedCacheManager。如果对你的应用是部署在多个服务器(比如服务器集群)的话,你将会想使用一个集群缓存管理器的实现来替代。
realm
它是很关键的一个地方,这是是需要自己实现的。它作为shiro的一个组件,可以让shiro访问到你的系统中的用户、角色、权限等数据。
[urls]
这是非常重要的一个节点,来配置哪些路径映射哪些过滤器来进行鉴权,可以用逗号分开,配置多个过滤器。
这里的anon是shiro内置的一个过滤器,表示不需要进行鉴权。当然还是很多的shiro内置鉴权过滤器.在后面简单介绍下
[filters]
1.2以后,filters被并入[main]节点,如果继续保留也没事儿,只是会出个警告而已。app_auth是我自己实现的一个filter系统中主要使用这个filter进行鉴权。
shiro的内置filters
- anon
org.apache.shiro.web.filter.authc.AnonymousFilter
- authc
org.apache.shiro.web.filter.authc.FormAuthenticationFilter
- authcBasic
org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
- logout
org.apache.shiro.web.filter.authc.LogoutFilter
- noSessionCreation
org.apache.shiro.web.filter.session.NoSessionCreationFilter
- perms
org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
- port
org.apache.shiro.web.filter.authz.PortFilter
- rest
org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
- roles
org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
- ssl
org.apache.shiro.web.filter.authz.SslFilter
- user
org.apache.shiro.web.filter.authc.UserFilter
自定义realm
com.yourdomain.module.shiro.Realm
需要继承shiro的AuthorizingRealm
1
| public class Realm extends AuthorizingRealm
|
需要实现2个抽象方法
doGetAuthenticationInfo
主要是在登录的时候,进行用户身份验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
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("用户不存在"); }
}
|
doGetAuthorizationInfo
在第一次鉴权的时候进行调用,获取并保存到chache中(没有配置cache是不是每次都得调用?)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SysAdmin userInPrincipal = (SysAdmin) principals.getPrimaryPrincipal(); List<String> stringPermissions = AdminService.getPermissions(userInPrincipal.getId()); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(stringPermissions); return info; }
|
我这里返回的:字符串权限表达式(字符串通配符权限),对于各自所对应的资源(主要就是url路径),我是保存在数据库中,方便进行配置,然后再加上缓存。在自定义filter中将url转换成对应的表达式,然后进行鉴权。
登录、注销
登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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) { renderJson(CommonService.ajaxError("登陆失败")); } }
|
注销
1 2 3 4 5
| public void logout() { Subject subject = SecurityUtils.getSubject(); subject.logout(); redirect("/passport/login"); }
|
是不是感觉很easy:)
自定义filter
com.yourdomain.module.shiro.AuthorizeFilter
继承shiro的AuthorizationFilter
1
| public class AuthorizeFilter extends AuthorizationFilter
|
实现抽象方法isAccessAllowed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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.setAttribute("admin",user); 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; }
}
|
- 在这里,我根据actionKey分析出所对应的权限表达式
- 对于用户superadmin,默认具有所有权限,等同于,不需要鉴权
- 其他用户根据权限表达式,来进行鉴权
关于用户、角色、url资源及对应的权限表达式
你可以按照你自己的方式来构建一套,我相信对于大多数人应该不成问题,因为这个已经不属于shiro的范畴了。自己搞几个表,搞几个配置界面,做下缓存策略等等。
模板引擎中使用扩展函数(标签)
有时候你的应用也许需要在界面上进行鉴权,比如按钮啥的,这时候就可能需要扩展模板引擎的函数或者标签。
可以参考下 《beetl 和 shrio 结合》 http://my.oschina.net/xiandafu/blog/143109
只要解决如何扩展模板引擎的函数或者标签,其他,我想应该都是雷同的吧。