spring boot & shiro

首先pom依赖

<!-- shiro核心包-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!-- shiro 引入缓存-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!-- shiro标签,,thymeleaf支持shrio标签需要额外引用这个包-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>1.2.1</version>
        </dependency>

 

1.初步的拦截;

首先定义一个shiroconfig,,还有Realm,用于身份信息权限信息的验证。

ShiroConfig
    //首先定义shiro过滤器工厂类
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问,我这里是过滤前缀是user的-->
        filterChainDefinitionMap.put("/userInfo/**", "authc");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授权界面;不止需要这个 还需要在异常拦截器里配置
        //shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    
    
    //第二:SecurityManager,Shiro的安全管理,主要是身份认证的管理,缓存管理
     @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
         securityManager.setCacheManager(ehCacheManager());   //加入缓存管理(不用即不加)
        return securityManager;
    }
    
    @Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); //加入自定义密码匹配(不用即不加)
        return myShiroRealm;
    }

第三:Realm,用于身份信息权限信息的验证。
    身份校验核心 MyShiroRealm extends AuthorizingRealm
实现两个方法
1 doGetAuthenticationInfo 主要是身份的校验
2 doGetAuthorizationInfo 设置权限信息


 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
        //获取用户的输入的账号.
        String username = (String)token.getPrincipal();
        //通过username从数据库中查找 User对象,如果找到,没找到.
        //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
        UserInfo userInfo = userInfoService.findByUsername(username);
        System.out.println("----->>userInfo="+userInfo);
        if(userInfo == null){
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo, //用户名
                userInfo.getPassword(),//密码
                ByteSource.Util.bytes(userInfo.getSalt()),
                getName()  //realm name
        );
        return authenticationInfo;
        
        //根据authenticationInfo进行校验,,如果没有自定义密码配置 则CredentialsMatcher.doCredentialsMatch(token, info)实现明文校验
    }
    如果自定义密码配置 在config中 @Bean(name = "hashedCredentialsMatcher") public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); // 采用MD5方式加密 hashedCredentialsMatcher.setHashAlgorithmName("MD5"); // 设置加密次数 hashedCredentialsMatcher.setHashIterations(1024); return hashedCredentialsMatcher; }

  

以上会进行拦截身份的校验

2.权限的校验

权限校验:
     @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserInfo userInfo  = (UserInfo)principals.getPrimaryPrincipal();
        for(SysRole role:userInfo.getRoleList()){
            authorizationInfo.addRole(role.getRole());
            for(SysPermission p:role.getPermissions()){
                authorizationInfo.addStringPermission(p.getPermission());
            }
        }
        return authorizationInfo;
    }
首先config里开启注解的支持 /** * 开启shiro aop注解支持. * 使用代理方式;所以需要开启代码支持; * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; }

//然后控制器里 @RequiresPermissions("userInfo:view")//权限管理;
@RequestMapping("/userList") @RequiresPermissions("userInfo:view")//权限管理; public String userInfo(){ System.out.println("1212---------------------------------------"); return "userInfo"; }
//这样的话权限不够会抛出异常,所以还需 配置异常拦截器
/** * 配置异常拦截器 权限不够的异常跳转页面 * @return */ @Bean(name="simpleMappingExceptionResolver") public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() { SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver(); Properties mappings = new Properties(); mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理 mappings.setProperty("UnauthorizedException","403"); r.setExceptionMappings(mappings); // None by default r.setDefaultErrorView("error"); // No default r.setExceptionAttribute("ex"); // Default is "exception" //r.setWarnLogCategory("example.MvcLogger"); // No default return r; }
  

3.如果thymeleaf想使用shiro标签 需要引用相应的pom

同时config中配置

@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}

html中引入相应标签库

<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

4.使用shiro 缓存,首先引入相应的pom

在config中

@Bean
public EhCacheManager ehCacheManager() {
    System.out.println("ShiroConfiguration.getEhCacheManager()");
    EhCacheManager cacheManager = new EhCacheManager();
    cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
    return cacheManager;
} 

AuthorizingRealm 身份校验的核心类,这里摘一段源码 看下这个大概是如何实现的

protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {

        if (principals == null) {
            return null;
        }

        AuthorizationInfo info = null;

        if (log.isTraceEnabled()) {
            log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
        }

        Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
        if (cache != null) {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
            }
            Object key = getAuthorizationCacheKey(principals);
            info = cache.get(key);
            if (log.isTraceEnabled()) {
                if (info == null) {
                    log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
                } else {
                    log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
                }
            }
        }


        if (info == null) {
            // Call template method if the info was not found in a cache
            info = doGetAuthorizationInfo(principals);
            // If the info is not null and the cache has been created, then cache the authorization info.
            if (info != null && cache != null) {
                if (log.isTraceEnabled()) {
                    log.trace("Caching authorization info for principals: [" + principals + "].");
                }
                Object key = getAuthorizationCacheKey(principals);
                cache.put(key, info);
            }
        }

        return info;
    }

不难看出,在获取权限的方法中,首先是先从缓存中读取AuthorizationInfo,如果缓存中不存在 才访问doGetAuthorizationInfo(获取权限并将之存放进缓存中)

5.权限更新的同时,清楚缓存里的权限

当时是看了很多网上的案例,发现都不可行,自己参照上述的源码,了解缓存大概是怎么实现的 代码如下

 public void reloadAuthorizing(){
       Subject subject = SecurityUtils.getSubject();
        Object key = getAuthorizationCacheKey(subject.getPrincipals());//看下当前是否有权限缓存
        clearCachedAuthorizationInfo(subject.getPrincipals());
    }

关于注解RequiresPermissions  

源码大概看了一部分,也参考了网上的例子 感觉人家讲的更好 我把参考的路径贴出来   http://blog.csdn.net/jin5203344/article/details/53187923

最后github地址          https://github.com/wuhao1994/testshiro

ShiroConfig//首先定义shiro过滤器工厂类@Bean    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {        System.out.println("ShiroConfiguration.shirFilter()");        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();        shiroFilterFactoryBean.setSecurityManager(securityManager);        //拦截器.        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();        // 配置不会被拦截的链接 顺序判断        filterChainDefinitionMap.put("/static/**", "anon");        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了        filterChainDefinitionMap.put("/logout", "logout");        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问,我这里是过滤前缀是user的-->        filterChainDefinitionMap.put("/userInfo/**", "authc");        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面        shiroFilterFactoryBean.setLoginUrl("/login");        // 登录成功后要跳转的链接        shiroFilterFactoryBean.setSuccessUrl("/index");        //未授权界面;不止需要这个 还需要在异常拦截器里配置        //shiroFilterFactoryBean.setUnauthorizedUrl("/403");        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);        return shiroFilterFactoryBean;    }//第二:SecurityManager,Shiro的安全管理,主要是身份认证的管理,缓存管理 @Bean    public SecurityManager securityManager(){        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();        securityManager.setRealm(myShiroRealm());        return securityManager;    }@Bean    public MyShiroRealm myShiroRealm(){        MyShiroRealm myShiroRealm = new MyShiroRealm();        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());        return myShiroRealm;    }

posted @ 2018-01-30 15:26  wuhaoyoung  阅读(368)  评论(0编辑  收藏  举报