原文:http://zhangxiao.org/2016/jfinal-shiro-integration2/
续上一篇《JFianl整合Shiro(一)》
我准备在这里,具体的描述下JFianl整合Shiro的基本流程。
Maven Dependency
我现在使用是
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.4</version>
</dependency>
配置web.xml
<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文件
以下是我常用的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
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
public class Realm extends AuthorizingRealm
需要实现2个抽象方法
doGetAuthenticationInfo
主要是在登录的时候,进行用户身份验证
/**
* 获取用户验证信息
* @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("用户不存在");
}
}
doGetAuthorizationInfo
在第一次鉴权的时候进行调用,获取并保存到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:)
自定义filter
com.yourdomain.module.shiro.AuthorizeFilter
继承shiro的AuthorizationFilter
public class AuthorizeFilter extends AuthorizationFilter
实现抽象方法isAccessAllowed
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;
}
}
- 在这里,我根据actionKey分析出所对应的权限表达式
- 对于用户superadmin,默认具有所有权限,等同于,不需要鉴权
- 其他用户根据权限表达式,来进行鉴权
关于用户、角色、url资源及对应的权限表达式
你可以按照你自己的方式来构建一套,我相信对于大多数人应该不成问题,因为这个已经不属于shiro的范畴了。自己搞几个表,搞几个配置界面,做下缓存策略等等。
模板引擎中使用扩展函数(标签)
有时候你的应用也许需要在界面上进行鉴权,比如按钮啥的,这时候就可能需要扩展模板引擎的函数或者标签。
可以参考下 《beetl 和 shrio 结合》 http://my.oschina.net/xiandaf...
只要解决如何扩展模板引擎的函数或者标签,其他,我想应该都是雷同的吧。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。