spring boot & shiro

孤者浪人 提交于 2020-02-04 16:38:44

首先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中配置

@Beanpublic 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;    }

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!