springboot+shiro进阶篇

文章的进阶并不是对源码进行一通分析,只是在入门篇上进行更深入的思考,并在实际应用中遇到的可能性比较大的问题,此文章的项目代码是在入门篇基础上添加的。

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[角色]。

猜你喜欢

转载自blog.csdn.net/qq_34297563/article/details/88977181