Shiro最佳实践(七)Spring集成与EhCache

Apache Shiro
placeholder image
admin 发布于:2019-08-04 18:01:16
阅读:loading

就笔者现在而言,深入与Spring集成才是最终的方向,同时将常用的一系列授权相关功能给做到期望的程度,所以在于spring集成时考虑了实现以下功能点:

1、登录/退出;

     登录支持自定义登录/退出,验证码支持(本例中验证码只是模拟,并未使用图片),记住我,登录失败错误提示等。

2、权限控制;

     权限功能模拟从数据库中获取,权限控制及未授权页面显示;

3、用户授权缓存;

     集成EhCache实现用户授权信息缓存;

自定义认证Realm

package cn.chendd.shiro.examples.commponent;

import ...

/**
 * @author chendd
 * @date 2019/6/15 1:22
 * 
自定义认证实现
 
*/
public class EncryptionRealmEhCache extends AuthorizingRealm implements Serializable {

    
private static final long serialVersionUID 818010090715013851L;

    
@Resource
    
private ISysUserService sysUserService;

    
@Override
    
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo author = 
new SimpleAuthorizationInfo();
        Set<String> roles = 
new HashSet<>();
        roles.add(
"userManager");
        author.setRoles(roles);
        Set<String> perms = 
new HashSet<>();
        perms.add(
"user:queryList");
        author.addStringPermissions(perms);
        
return author;
    }

    
@Override
    
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        String userName = userToken.getUsername();
        SysUser sysUser = 
sysUserService.getSysUserByUserName(userName);
        
if(sysUser == null){
            
throw new UnknownAccountException();
        }
        String passWord = sysUser.getPassWord();
        
if(! "ENABLE".equals(sysUser.getStatus())){
            
throw new LockedAccountException();
        }
        String encodePassword = encodePassword(
new String(userToken.getPassword()) , userName);
        
if(!encodePassword.equals(passWord)){
            
throw new IncorrectCredentialsException();
        } 
else if(false){
            
//密码错误次数过多
            
throw new ExcessiveAttemptsException();
        }
        SimpleAuthenticationInfo auth = 
new SimpleAuthenticationInfo(sysUser.getUserName() , token.getCredentials() , this.getName());
        
return auth;
    }

    
public static String encodePassword(Object source, String salt) {
        Md5Hash md5 = 
new Md5Hash(source, salt, 7);
        
return md5.toHex();
    }
}

spring-shiro参数配置

<?xml version="1.0" encoding="UTF-8"?>

 <beans xmlns="http://www.springframework.org/schema/beans"
      
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache"
      
xmlns:aop="http://www.springframework.org/schema/aop"
      
xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"
>

    <
aop:config proxy-target-class="true"></aop:config>

    <
bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <
property name="securityManager" ref="securityManager" />
    </
bean>

    <
bean id="customLoginFormFilter" class="cn.chendd.shiro.examples.commponent.CustomLoginFormFilter">
        <
property name="usernameParam" value="userName"/>
        <
property name="passwordParam" value="passWord"/>
        <
property name="rememberMeParam" value="remMe"/>
       
<!-- 可设置登录成功后的响应地址 -->
        <!--<property name="successUrl" value="/index" />-->
   
</bean>

    <
bean id="customLogoutFilter" class="cn.chendd.shiro.examples.commponent.CustomLogoutFilter"></bean>

    <
bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
       
<!--删除由直接关闭浏览器造成的无退出问题-->
       
<property name="deleteInvalidSessions" value="true" />
       
<!-- 定时执行session销毁 -->
       
<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
       
<!--<property name="sessionValidationInterval" value="10000" />--><!-- 该参数未发现明显作用 -->
        <!--
需要让此session可以使用该定时调度器进行检测 -->
       
<property name="sessionValidationSchedulerEnabled" value="true"/>
       
<!-- 定义的是全局的session会话超时时间,此cao作会覆盖web.xml文件中的超时时间配置 -->
       
<property name="globalSessionTimeout" value="#{30 * 60 * 1000}" />
       
<!-- 所有的session一定要将id设置到Cookie之中,需要提供有Cookie的cao作模版-->
       
<property name="sessionIdCookie">
            <
bean class="org.apache.shiro.web.servlet.SimpleCookie">
                <
constructor-arg value="shiro.session.id.cookie" />
            </
bean>
        </
property>
       
<!-- 定义sessionIdCookie模版可以进行cao作的启用 -->
       
<property name="sessionIdCookieEnabled" value="true"/>
       
<!-- 引用session缓存的相关配置 -->
        <!--<property name="cacheManager" ref="shiroRedisCacheManager" />-->
   
</bean>

    <
bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <
property name="realm" ref="encryptionRealm" />
       
<!--session过期策略-->
       
<property name="sessionManager" ref="sessionManager" />
       
<!--记住我-->
       
<property name="rememberMeManager" ref="rememberMeManager" />
       
<!-- 启用session的缓存 -->
       
<property name="cacheManager" ref="shiroEhCacheManager" />
     </
bean>

    <
bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <
property name="securityManager" ref="securityManager" />
        <
property name="loginUrl" value="/login" />
        <
property name="successUrl" value="/index" />
        <
property name="unauthorizedUrl" value="/unauth" />
        <
property name="filterChainDefinitions">
            <
value>
                /favicon.ico = anon
                /logout = logout
                /login = authc
                /** = authc
            </
value>
        </
property>
        <
property name="filters">
            <
map>
                <
entry key="authc" value-ref="customLoginFormFilter"></entry>
               
<!--<entry key="logout" value-ref="customLogoutFilter"></entry>-->
           
</map>
        </
property>
    </
bean>

    <
bean id="rememberCookies" class="org.apache.shiro.web.servlet.SimpleCookie">
        <
constructor-arg value="rememberMe"></constructor-arg>
        <
property name="httpOnly" value="true"></property>
        <
property name="maxAge" value="#{60*60*24}"></property>
    </
bean>

   
<!-- 配置记住我管理器 -->
   
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
       
<!-- 加密的base64,末尾拼接以A=结束,此处给的是88888888 -->
        <!--<property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('ODg4ODg4ODgAA==')}"/>-->
       
<property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('6ZmI6I2j5Y+R5aSn5ZOlAA==')}"/>
        <
property name="cookie" ref="rememberCookies"/>
    </
bean>

   
<!-- 解决shiro配置的没有权限访问时,unauthorizedUrl不跳转到指定路径的问题 -->
   
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <
property name="exceptionMappings">
            <
props>
                <
prop key="org.apache.shiro.authz.UnauthorizedException">/unauth</prop>
            </
props>
        </
property>
    </
bean>

   
<!-- session存储配置 -->
    <!--
定义Session ID生成管理器,默认session.getId生成的规则就是uuid -->
   
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />

   
<!-- 配置session的定时验证检测程序类,以让无效的session释放 -->
   
<bean id="sessionValidationScheduler"
         
class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
       
<!-- 设置session的失效扫描间隔,会清空掉过期失效session数据,单位为毫秒,默认3600000 -->
       
<property name="sessionValidationInterval" value="1800000"/>
       
<!-- 随后还需要定义有一个会话管理器的程序类的引用 -->
       
<property name="sessionManager" ref="sessionManager"/>
    </
bean>

    <
bean id="encryptionRealm" class="cn.chendd.shiro.examples.commponent.EncryptionRealmEhCache">
        <
property name="name" value="encryptionRealm" />
       
<!--<property name="credentialsMatcher" ref="credentialsMatcher" />-->
        <!--
启用用户权限的缓存,使用ehcache.xml中定义的名称 -->
        <!--<property name="authorizationCacheName" value="shiro-AuthorizationInfo" />-->
        <!--<property name="cacheManager" ref="shiroRedisCacheManager" />-->
   
</bean>
   
<!--<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="MD5" />
        <property name="hashIterations" value="7" />
    </bean>-->

    <!-- spring
继承ehcache的缓存配置  -->
   
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <
property name="configLocation" value="classpath:ehcache.xml" />
    </
bean>

   
<!-- shiro封装cacheManager -->
   
<bean id="shiroEhCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <
property name="cacheManager" ref="ehCacheManager" />
    </
bean>

   
<!-- spring 封装ehcache缓存管理器  -->
   
<bean id="springCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <
property name="cacheManager" ref="ehCacheManager" />
    </
bean>

   
<!-- 激活spring 缓存注解 -->
   
<cache:annotation-driven cache-manager="springCacheManager" />

</
beans>


示例页面

image.png

image.png

说明:登录账号信息有:chendd/chendd123和sunm/sunm123;

这里给出核心自定义Realm与spring-shiro.xml配置文件,完整示例请转至开篇或尾篇处下载。


 点赞


 发表评论

当前回复:作者

 评论列表


留言区