=========默认鉴权========
过滤链中定义:
<!-- 过滤链定义 --> <property name="filterChainDefinitions"> <value> ... /pages/User/create.do* = perms[User:create] ... </value> </property>
这段配置的含义是:/pages/User/create.do*这样的请求路径,需要鉴权,且需要用户有“User:create”的权限字符串。
perms是拦截器的名字,默认实现类是:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
这个过滤器会得到配置中请求路径对应的权限字符串,如“User:create”,然后到realm中查找当前用户包含的权限,具体来说是调用reaml的回调函数:
/** * 鉴权回调函数,提取当事人的角色和权限 * principals 当事人 */ protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { //用户名 String username = (String) principals.fromRealm( getName()).iterator().next(); /*这些代码应该是动态从数据库中取出的,此处写死*/ if(username!=null&&username.equals("admin")){ SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // info.addRole("admin");//添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色 info.addStringPermission("User:create"); info.addStringPermission("/pages/index.jsp");//添加权限 info.addStringPermission("/pages/info.jsp");//添加权限 return info; } return null; }
此代码属于自定义Realm——public class CustomRealm extends AuthorizingRealm
代码中我们使用的是测试数据,直接往info里面添加角色字符串和权限字符串,我们也可以从数据库中获取,这不是本节重点。
现在关键要明白SecurityManager是从请求端得到应该有的权限字串,从Realm得到当事人具备的角色和权限字串,然后比对,比对成功说明鉴权成功,否则鉴权失败。
事实上,这种配置鉴权的方式,连自定义Realm都不需要,用户信息、角色信息、权限信息都可以配置:
[users] # user1 = sha256-hashed-hex-encoded password, role1, role2, ... user1 = 2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b, role1, role2, ... [roles] # 'admin' role has all permissions, indicated by the wildcard '*' admin = * # The 'schwartz' role can do anything (*) with any lightsaber: schwartz = lightsaber:* # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with # license plate 'eagle5' (instance specific id) goodguy = winnebago:drive:eagle5
这点可以参考官方文档。
===================自定义鉴权=================
往往我们的项目,特别是遗留项目都会设计几张表来存储用户信息和角色信息以及权限映射信息,所以Realm这块,一般要自定义。
而且通常的作法是直接用请求的URL作为权限字符串的,也就是不需要URL再去映射一些权限字符串,所以过滤器这块,我们可能也需要自定义。
自定义鉴权过滤器:
package javacommon.shiro; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter; /** * 基于URL的权限判断过滤器<p> * 我们自动根据URL产生所谓的权限字符串,这一项在Shiro示例中是写在配置文件里面的,默认认为权限不可动态配置<p> * URL举例:/User/create.do?***=*** -->权限字符串:/User/create.do * @author zhengwei lastmodified 2013年8月15日 * */ public class URLPermissionsFilter extends PermissionsAuthorizationFilter{ /** *@param mappedValue 指的是在声明url时指定的权限字符串,如/User/create.do=perms[User:create].我们要动态产生这个权限字符串,所以这个配置对我们没用 */ public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { return super.isAccessAllowed(request, response, buildPermissions(request)); } /** * 根据请求URL产生权限字符串,这里只产生,而比对的事交给Realm * @param request * @return */ protected String[] buildPermissions(ServletRequest request) { String[] perms = new String[1]; HttpServletRequest req = (HttpServletRequest) request; String path = req.getServletPath(); perms[0] = path;//path直接作为权限字符串 /*String regex = "/(.*?)/(.*?)\.(.*)"; if(url.matches(regex)){ Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(url); String controller = matcher.group(1); String action = matcher.group(2); }*/ return perms; } }
可以看出我们直接将请求路径作为权限字符串,过滤器会去调用Realm,所以自定义Realm中也要为用户添加同样格式的权限字符串
info.addStringPermission("/pages/index.jsp");//添加权限,admin可访问这个路径
最后再来看看全局Filter的配置:
<!-- Shiro Filter 拦截器相关配置 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> ... <property name="filters"> <util:map> <entry key="authc" value-ref="myAuthenFilter" /> <entry key="perms" value-ref="URLPermissionsFilter" /> </util:map> </property> <!-- 过滤链定义 --> <property name="filterChainDefinitions"> <value> /login.jsp = authc /pages/* = authc,perms /logout.do = logout ... </value> </property> </bean> ... <!-- 自定义鉴权拦截器 --> <bean id="URLPermissionsFilter" class="javacommon.shiro.URLPermissionsFilter" />
这段配置意味着访问/pages/*都需要鉴权,而鉴权使用的是自定义的拦截器。
测试:
以admin身份登录并访问pages/info.jsp,没问题,因为它拥有这个权限。
访问pages/NB.jsp,因为这个路径没有添加到admin的权限中,所以鉴权失败,将跳转到unauthorizedUrl指定的路径。
小节:
对于客户要求程序开发者自己管理权限,而且不需要动态配置的情况,使用默认配置法,非常简单。
当需要动态管理权限,那就要自定义Realm和Filter,关键在于请求URL--》权限字符串,Realm可返回一个用户拥有的权限字符串。
这些字符串应该能比对上。