Spring boot整合shiro框架

 

ShiroConfiguration

package com.energy.common.config;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import com.energy.common.util.MyFormAuthenticationFilter;
import com.energy.common.util.MyShiroRealm;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

/**

  • shiro 配置类
  • @author lit

*/
@Configuration
public class ShiroConfiguration {

</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Logger logger = LoggerFactory.getLogger(ShiroConfiguration.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类,
 * 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。
 * 主要是AuthorizingRealm类的子类,以及EhCacheManager类。
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean(name </span>= "lifecycleBeanPostProcessor"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    logger.info(</span>"ShiroConfiguration.getLifecycleBeanPostProcessor()"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> LifecycleBeanPostProcessor();
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * HashedCredentialsMatcher,这个类是为了对密码进行编码的,防止密码在数据库里明码保存,
 * 当然在登陆认证的生活,这个类也负责对form里输入的密码进行编码。
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> @Bean(name = "hashedCredentialsMatcher")
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> public HashedCredentialsMatcher hashedCredentialsMatcher() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> HashedCredentialsMatcher credentialsMatcher = new
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> HashedCredentialsMatcher();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> credentialsMatcher.setHashAlgorithmName("MD5");
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> credentialsMatcher.setHashIterations(2);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> credentialsMatcher.setStoredCredentialsHexEncoded(true);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> return credentialsMatcher;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> }</span>
<span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm, 负责用户的认证和权限的处理
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean(name </span>= "myShiroRealm"<span style="color: rgba(0, 0, 0, 1)">)
@DependsOn(</span>"lifecycleBeanPostProcessor"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> MyShiroRealm myShiroRealm() {
    logger.info(</span>"ShiroConfiguration.myShiroRealm()"<span style="color: rgba(0, 0, 0, 1)">);
    MyShiroRealm myShiroRealm </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MyShiroRealm();
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> myShiroRealm;
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来,
 * 然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean(name </span>= "ehCacheManager"<span style="color: rgba(0, 0, 0, 1)">)
@DependsOn(</span>"lifecycleBeanPostProcessor"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> EhCacheManager ehCacheManager() {
    logger.info(</span>"ShiroConfiguration.ehCacheManager()"<span style="color: rgba(0, 0, 0, 1)">);
    EhCacheManager cacheManager </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> EhCacheManager();
    cacheManager.setCacheManagerConfigFile(</span>"classpath:config/ehcache-shiro.xml"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> cacheManager;
}

@Bean(name </span>= "rememberMeCookie"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> SimpleCookie rememberMeCookie() {
    logger.info(</span>"ShiroConfiguration.rememberMeCookie()"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe</span>
    SimpleCookie simpleCookie = <span style="color: rgba(0, 0, 255, 1)">new</span> SimpleCookie("rememberMe"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> &lt;!-- 记住我cookie生效时间30天 ,单位秒;--&gt;</span>
    simpleCookie.setMaxAge(259200<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> simpleCookie;
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * cookie管理对象;
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean(name </span>= "rememberMeManager"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> CookieRememberMeManager rememberMeManager() {
    logger.info(</span>"ShiroConfiguration.rememberMeManager()"<span style="color: rgba(0, 0, 0, 1)">);
    CookieRememberMeManager cookieRememberMeManager </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> CookieRememberMeManager();
    cookieRememberMeManager.setCookie(rememberMeCookie());
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> cookieRememberMeManager;
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类。
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean(name </span>= "securityManager"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> DefaultWebSecurityManager securityManager() {
    logger.info(</span>"ShiroConfiguration.getDefaultWebSecurityManager()"<span style="color: rgba(0, 0, 0, 1)">);
    DefaultWebSecurityManager securityManager </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DefaultWebSecurityManager();
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置realm.</span>

securityManager.setRealm(myShiroRealm());
// <!-- 用户授权 / 认证信息 Cache, 采用 EhCache 缓存 -->
securityManager.setCacheManager(ehCacheManager());
//注入记住我管理器;
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * ShiroFilterFactoryBean,是个factorybean,为了生成ShiroFilter。
 * 它主要保持了三项数据,securityManager,filters,filterChainDefinitionManager。
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> ShiroFilterFactoryBean shiroFilter() {
    logger.info(</span>"ShiroConfiguration.shiroFilter()"<span style="color: rgba(0, 0, 0, 1)">);
    ShiroFilterFactoryBean shiroFilterFactoryBean </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ShiroFilterFactoryBean();
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 必须设置SecuritManager</span>

shiroFilterFactoryBean.setSecurityManager(securityManager());

    Map</span>&lt;String, Filter&gt; filtersMap =<span style="color: rgba(0, 0, 0, 1)"> shiroFilterFactoryBean.getFilters();
    filtersMap.put(</span>"authc", myFormAuthenticationFilter());<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自定义拦截器</span>

shiroFilterFactoryBean.setFilters(filtersMap);

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 拦截器</span>
    Map&lt;String, String&gt; filterChainDefinitionMap = <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedHashMap&lt;String, String&gt;<span style="color: rgba(0, 0, 0, 1)">();
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 配置退出过滤器,其中的具体代码Shiro已经替我们实现了</span>
    filterChainDefinitionMap.put("/logout", "logout"<span style="color: rgba(0, 0, 0, 1)">);
    filterChainDefinitionMap.put(</span>"/css/**", "anon"<span style="color: rgba(0, 0, 0, 1)">);
    filterChainDefinitionMap.put(</span>"/js/**", "anon"<span style="color: rgba(0, 0, 0, 1)">);
    filterChainDefinitionMap.put(</span>"/img/**", "anon"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> &lt;!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 --&gt;:这是一个坑呢,一不小心代码就不好使了;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> &lt;!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--&gt;</span>
    filterChainDefinitionMap.put("/logout", "logout"<span style="color: rgba(0, 0, 0, 1)">);
    filterChainDefinitionMap.put(</span>"/index", "authc"<span style="color: rgba(0, 0, 0, 1)">);
    filterChainDefinitionMap.put(</span>"/**", "authc"<span style="color: rgba(0, 0, 0, 1)">);

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果不设置默认会自动寻找工程根目录下的"/login"页面</span>
    shiroFilterFactoryBean.setLoginUrl("/login"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 登录成功后要跳转的链接</span>
    shiroFilterFactoryBean.setSuccessUrl("/index"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 未授权界面;</span>
    shiroFilterFactoryBean.setUnauthorizedUrl("/403"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 加载shiroFilter权限控制规则</span>

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;

}

@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> MyFormAuthenticationFilter myFormAuthenticationFilter() {
    MyFormAuthenticationFilter myFormAuthenticationFilter </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MyFormAuthenticationFilter();
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> myFormAuthenticationFilter;
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean
@DependsOn(</span>"lifecycleBeanPostProcessor"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    logger.info(</span>"ShiroConfiguration.defaultAdvisorAutoProxyCreator()"<span style="color: rgba(0, 0, 0, 1)">);
    DefaultAdvisorAutoProxyCreator daap </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DefaultAdvisorAutoProxyCreator();
    daap.setProxyTargetClass(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> daap;
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * AuthorizationAttributeSourceAdvisor,shiro里实现的Advisor类,
 * 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
 * 老实说,这里注入securityManager,我不知道有啥用,从source上看不出它在什么地方会被调用。
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
    logger.info(</span>"ShiroConfiguration.authorizationAttributeSourceAdvisor()"<span style="color: rgba(0, 0, 0, 1)">);
    AuthorizationAttributeSourceAdvisor aasa </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AuthorizationAttributeSourceAdvisor();
    aasa.setSecurityManager(securityManager());
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> aasa;
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean(name </span>= "shiroDialect"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> ShiroDialect shiroDialect() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ShiroDialect();
}

}

thymleaf 使用 shiro 标签,需要引入

        <dependency>  
            <groupId>com.github.theborakompanioni</groupId>  
            <artifactId>thymeleaf-extras-shiro</artifactId>  
            <version>1.2.1</version>  
        </dependency>  

 

注:问题:https://www.oschina.net/question/250720_195683

FilterChain 修改如下:

        // 拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // <!-- 过滤链定义,从上向下顺序执行,一般将 /** 放在最为下边 -->: 这是一个坑呢,一不小心代码就不好使了;
        // <!-- authc: 所有 url 都必须认证通过才可以访问; anon: 所有 url 都都可以匿名访问 -->
        // 配置退出过滤器, 其中的具体代码 Shiro 已经替我们实现了    
        filterChainDefinitionMap.put("/", "authc");
        filterChainDefinitionMap.put("/index", "authc");
        filterChainDefinitionMap.put("/logout", "logout");
        //filterChainDefinitionMap.put("/**", "anon");    
        filterChainDefinitionMap.put("/sys/**", "authc");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/vendors/**", "anon");
        filterChainDefinitionMap.put("/fonts/**", "anon");
        // 加载 shiroFilter 权限控制规则
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

 

参考博客:Apache Shiro 和 Spring boot 的结合使用

参考博客:Spring Boot Shiro 权限信息缓存处理, 记住我,thymleaf 使用 shiro 标签