登录 
欢迎来到陈冬冬(雪地里走)的学习天地 | 经验分享整理平台 | 陈冬冬
 服务器本次启动于:17天前 

Shiro最佳实践(八)Spring集成与Redis

2019-08-04 19:24:44 阅读:()

终于到这一篇了,也计划用Redis集成去存储用户会话实现分布式session管理为重点实现,在整合过程中个人严格遵循所有技术均采用比较新的版本,所以在此过程中废了相当大的劲儿,比如使用的Spring5的XML配置版,Spring-data-redis-2.0版本及lettuce5.1版本替代jedis实现等,所以本例中关键技术版本信息如下:

shiro 1.4.1

spring 5.0.4

spring-data-redis 2.0.5

jedis 2.9.1

lettuce 5.1.2

虽然在实践过程中较为费劲,但整理出来的这些技术版本相对比较新,可以说未来的几年肯定不会过时,然而这些东西都是作为丰富自己的知识储备,真正使用时肯定会基于SpringBoot最新版再去集成的,但是既然要弄必然是做到在个人能力范围的最优。

功能点包括自定义登录过滤器(自定义form表单、记住我、验证码、错误提示)、Redis集成(使用lettuce替代jedis、Redis-Cache)、Spring-data-redis2.0、自定义Realm认证、分布式Session管理,像登录采用Ajax、Redis中采用JSON序列化、限制单一登录等这些没有实现,采用默认的JDK实现,整个示例在集成Redis时参考了gitee上的项目hunt-admin,主要是集成Redis的SessionDao部分,另外这个项目里面登录时也采用了拖动时验证码,值得拥有,地址为:https://gitee.com/ouyangan/hunt-admin

自定义Realm认证

package cn.chendd.shiro.examples.commponent;

import ...

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

   
private static final long serialVersionUID = 818010090715013851L;

   
@Resource
   
private RedisTemplate<Object, Object> redisTemplate;

   
@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 , token.getCredentials() , this.getName());
       
SimpleAuthenticationInfo auth = new SimpleAuthenticationInfo(sysUser.getUserName() , token.getCredentials() , this.getName());
       
return auth;
    }

    //退出时移除Redis中的数据

    @Override
   
protected void doClearCache(PrincipalCollection principals) {
       
redisTemplate.delete("shiro-cache-" + principals.getPrimaryPrincipal().toString());
    }

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

shiro-spring配置文件

<?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: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"
>

    <
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"/>
       
<!-- 定义的是全局的