文章的进阶并不是对源码进行一通分析,只是在入门篇上进行更深入的思考,并在实际应用中遇到的可能性比较大的问题,此文章的项目代码是在入门篇基础上添加的。
1、文章的主要内容介绍
项目需求中往往会遇到这种情况:有A,B,C三个链接,有无数个用户,有admin,simple,reader三个角色,每个用户可能拥有一个角色或多个角色。这个描述应该可以理解吧,不理解的话是不是项目经验太少了一点~~。OK,现在需求是这样的,A链接只能有admin角色访问,B链接只能有simple角色访问,C链接admin,simple角色都可访问。那么此时该如何做呢。
1.1 先解决“A链接只能有admin角色访问,B链接只能有simple角色访问”这个问题,问题点就在于如何配置不同的路径让不同的角色去访问,下面代码就是入门篇的configration的代码,但在配置过滤路径时增加了过滤规章。
ShiroConfiguration类
/**
* shiro 配置类
* @author tianguifang
*
*/
@Configuration
public class ShiroConfiguration {
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myRealm());
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
@Bean
public DefaultWebSessionManager getSessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(getRedisSession());
sessionManager.setDeleteInvalidSessions(true);// 删除过期的session
sessionManager.setSessionValidationSchedulerEnabled(true);// 是否定时检查session
return sessionManager;
}
@Bean
public UserRealm myRealm(){
return new UserRealm();
}
public CustomRolesAuthorizationFilter getRolesAuthorization(){
return new CustomRolesAuthorizationFilter();
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, Filter> map = new HashMap<>();
map.put("rolesOrFilter",getRolesAuthorization());
shiroFilterFactoryBean.setFilters(map);
// 权限控制map.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/user/login","anon");
filterChainDefinitionMap.put("/user/test01","authc");
filterChainDefinitionMap.put("/user/test02","roles[simple]");
filterChainDefinitionMap.put("/user/test03","roles[admin]");
// filterChainDefinitionMap.put("/user/test01","rolesOrFilter[admin]");
// filterChainDefinitionMap.put("/user/test02","rolesOrFilter[admin,simple]");
// filterChainDefinitionMap.put("/user/test03","rolesOrFilter[admin]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* cookie对象;
* @return
*/
public SimpleCookie rememberMeCookie(){
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//<!-- 记住我cookie生效时间30天 ,单位秒;-->
simpleCookie.setMaxAge(60);
return simpleCookie;
}
/**
* cookie管理对象;记住我功能
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 默认AES算法 下面三行代码是用来获取秘钥的
// KeyGenerator keygen = KeyGenerator.getInstance("AES");
// SecretKey deskey = keygen.generateKey();
// System.out.println(org.apache.shiro.codec.Base64.encodeToString(deskey.getEncoded()));
cookieRememberMeManager.setCipherKey(Base64.getDecoder().decode("QxxW3vZvJtHjS4wknND81g=="));
return cookieRememberMeManager;
}
}
下面附一张名词说明图,此图源于网上,来源现在找不到了,实在没法标注地址。
此时来看是不是很简单,什么路径需要什么角色来访问只需配置 roles[角色] 即可。但这个只是一个用户只有一个角色,如果一个用户拥有两个角色如何配置呢?
1.2 一个用户拥有多个角色如何满足需求。
1.2.1 roles[角色] 这种写法可以这么写 roles[admin,simple] 也就是说可以填多个角色进去,是个数组。这个看起来满足了需求,实则不行。下面还是看一下源码对角色的解析判断是怎么样的。
第一步
第二步
源码中从第一步到第二步中间的一些接口回调什么的就不粘了,但从第二步的hasRole可以看出,这个判断是 and的关系,这意味着什么呢,意味着如果A连接配置roles[simple,admin] 那么A链接需要同时具有simple和admin两个角色的人才可以访问,但需求是只要拥有admin角色即可访问,现在用户拥有了两个角色中包括admin,应该要让其可以访问的,此刻该怎么办呢?
2.1、重写AuthorizationFilter过滤器,将and的判断改成or的判断即可。
CustomRolesAuthorizationFilter
/**
* @author WYH
* @date 2019/4/2 14:22
*/
@Repository
public class CustomRolesAuthorizationFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) throws Exception {
System.out.println("进来了。。。。");
Subject subject = getSubject(servletRequest,servletResponse);
String[] rolesArray = (String[]) mappedValue;
//没有角色限制,有权限访问
if (rolesArray == null || rolesArray.length == 0) {
return true;
}
for (int i = 0; i < rolesArray.length; i++) {
//若当前用户是rolesArray中的任何一个,则有权限访问
if (subject.hasRole(rolesArray[i])) {
return true;
}
}
return false;
}
}
2.2、重写完这个过滤器之后只需要将过滤器告诉shiro,现在我要重写这个了,你不要用之前的那个判断了。怎么告诉呢,很简单,看下面代码:
Map<String, Filter> map = new HashMap<>();
map.put("rolesOrFilter",getRolesAuthorization());
shiroFilterFactoryBean.setFilters(map);
这三行代码是写在ShiroConfiguration 类里面的shiroFilter这个方法里面,可自行往上找到此类。
2.3、shiro框架默认的使用roles,此时重写了AuthorizationFilter,在2.2里面可以看出起的名字为“rolesOrFilter”,此时就需要将之前的roles[角色]修改为rolesOrFilter[角色]。