Shiro学习第六天
有时候在实际业务过程中Shiro框架本身给我们提供的过滤条件可能不是我们想要的,那么我们又该如何自定义自己的过滤条件呢???
<property name="filterChainDefinitions">
<value>
/login=anon
/gologin=anon
/index = authc
/role=authc,roles[admin]
/menu/** = authc,roles[admin,test]
</value>
</property>
这段代码是我们在配置文件当中配置的,假如说我们要访问menu菜单下的一些页面及其子页面我们就必须得同时具备admin和test两种角色,如果我们只具备一种单一角色我们是不能访问的,因为在Shiro filterChainDefinitions中 roles默认是and, 比如:roles[admin,test],表示同时需要“admin”和“test” 2个角色(权限)才通过认证,缺一不可。但是在实际业务中,一个端口往往同时对几个角色开放。比如管理员账号拥有访问所有端口的权限。那么此时的and显然是不合时宜的,与之替代的是or。
一、重新实现AuthorizationFilter类
/**
*
* @author liujiang
* @Description:支持多角色验证
* 由于Shiro filterChainDefinitions中 roles默认是and,
* = user,roles[system,general]
* 比如:roles[test,admin] ,表示同时需要“test”和“admin” 2个角色才通过认证
* 但是在实际业务中,一个端口的权限往往对多个角色开放(比如管理员可以使用一切权利)
*
* 2019年6月19日
*
*/
public class MyShiroFilter extends AuthorizationFilter {
//
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object object) throws Exception {
// TODO Auto-generated method stub
//拿到当个会话的subject
Subject subject=getSubject(request, response);
String[] roles=(String[])object;
if(roles==null || roles.length==0){
return true;
}
for(String role:roles){
if(subject.hasRole(role)){
return true;
}
}
return false;
}
}
二:在spring和shiro整合的配置文件中进行配置
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/gologin"/>
<property name="successUrl" value="/index"/>
<property name="unauthorizedUrl" value="/error"/>
<property name="filterChainDefinitions">
<value>
/login=anon
/gologin=anon
/index = authc
/role=authc,roles[admin]
/menu/** = authc,roles[admin,test]
</value>
</property>
<property name="filters">
<map>
<entry key="roles">
<bean class="com.shiro.test.mvc.filter.MyShiroFilter"/>
</entry>
</map>
</property>
</bean>
这样我们就实现了一个自定义的过滤规则。
下面我们再说说如何动态添加验证规则,在实际开发过程中我们有些权限是从数据库读取出来自动添加给用户的,而不是通过配置文件进行手动配置的而Shiro filterChainDefinitions配置的权限信息只在容器启动的第一次有效,之后如果更改就必须得重新启动容器 ,造成了一定的局限性。
一:自定义MyShiroFilterFactoryBean继承ShiroFilterFactoryBean因为我们容器在启动时是从ShiroFilterFactoryBean中读取验证信息进行安全拦截的,关于这部分是如何工作的这里可参考一位牛人的解析https://blog.csdn.net/u012345283/article/details/44199791非常清楚。
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.shiro.config.Ini;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
public class MyShiroFilterFactoryBean extends ShiroFilterFactoryBean {
//0代表占位符
private static final String ROLE_STRING = "roles[{0}]";
private String filterChainDefinitions;
@Override
public void setFilterChainDefinitions(String definitions) {
filterChainDefinitions = definitions;
Ini ini = new Ini();
ini.load(definitions);
Ini.Section section = ini.getSection("urls");
if (CollectionUtils.isEmpty(section)) {
section = ini.getSection("");
}
//模拟从数据库中读取的数据
Map<String, String[]> permsMap = new HashMap<String, String[]>();
permsMap.put("/dotest1", new String[]{"test"});
permsMap.put("/dotest2", new String[]{"test", "guest"});
//permsMap.put("/dotest3.html", new String[]{"admin"});
for (String url : permsMap.keySet()) {
String[] roles = permsMap.get(url);
StringBuilder sb = new StringBuilder();
for (String role : roles) {
sb.append(role).append(",");
}
//截取掉最后一个","
String str = sb.substring(0, sb.length() - 1);
//roles[test,guest]
//将上面定义的常量子符串中的占位符0替换成我们添加的角色信息
section.put(url, MessageFormat.format(ROLE_STRING, str));
}
this.setFilterChainDefinitionMap(section);
}
//在执行过程中动态添加
public void update() {
synchronized (this) {
try {
AbstractShiroFilter shiroFilter =
(AbstractShiroFilter) this.getObject();
PathMatchingFilterChainResolver resolver =
(PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
DefaultFilterChainManager manager = (DefaultFilterChainManager) resolver.getFilterChainManager();
manager.getFilterChains().clear();
this.getFilterChainDefinitionMap().clear();
this.setFilterChainDefinitions(" /dotest3= authc,roles[admin,test]");
Map<String, String> chains = this.getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
Iterator var12 = chains.entrySet().iterator();
while (var12.hasNext()) {
Map.Entry<String, String> entry = (Map.Entry) var12.next();
String url = (String) entry.getKey();
String chainDefinition = (String) entry.getValue();
manager.createChain(url, chainDefinition);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
二:在配置文件中将ShiroFilterFactoryBean替换成我们自定义的MyShiroFilterFactoryBean,这时容器启动时就会从我们自定义的过滤器中去读取安全验证信息。
不再赘述,上述配置文件自行替换成自己的
<bean id="shiroFilter" class="com.shiro.test.mvc.filter.MyShiroFilterFactoryBean">
三:在控制器中写代码 进行验证
@RequestMapping("do{path}")
public String page(@PathVariable(name="path") String path,Model model){
model.addAttribute("path",path);
return "test";
}
可以发现当我们使用test用户登陆时,都dotest1和dotest2都可以访问,用guest用户登陆时只能访问dotest2不能访问dotest1,其他所有do开头的都可以访问。
@Autowired
private MyShiroFilterFactoryBean shiroFilterFactoryBean;
@RequestMapping("update")
public String update() {
shiroFilterFactoryBean.update();
return "index";
}
当我们调用update方 ↵
用guest用户访问,显示权限不足,而test和admin用户可以访问,至此我们就完成了动态添加验证信息的过程。