shiro集成 spring-加密md5配置--权限管理-shiro中的session 等等!!

目录

1.shiro集成 spring

1.1 导入依赖的包

1.2 web.xml配置

2. 开发自定义Realm

2.1 创建测试数据表

2.3 编写后台支持服务

2.4 编写自定义的Realm

2.5配置文件 放入spring 中

2.6登录Controller

3.权限管理 登录后

1. 自定义Realm 此处上面的Realm中已有代码

2.权限标签

3. 使用注解控制权限

3.1 springMVC配置

4.2 注解权限验证失败不跳转路径问题

4. shiro中的缓存

1.1 引入需要的第三方包

1.2 ehcache配置文件

1.2 配置spring的配置文件

2. shiro中的session

2.1 shiro中的session特点和常用方法

2.2 如何使用

2.3 Session监听

2.4 SessionDao 将数据报错数据库 让后同步数据


1.shiro集成 spring

1.1 导入依赖的包

导入shiro需要的依赖包

<shiro-version>1.2.5</shiro-version>
​
<dependency>    
    <groupId>org.apache.shiro</groupId>    
    <artifactId>shiro-core</artifactId>    
    <version>${shiro-version}</version>
</dependency>
    <dependency>    
    <groupId>org.apache.shiro</groupId>    
    <artifactId>shiro-web</artifactId>    
    <version>${shiro-version}</version>
    </dependency>
<dependency>    
    <groupId>org.apache.shiro</groupId>    
    <artifactId>shiro-spring</artifactId>    
    <version>${shiro-version}</version>
</dependency>

1.2 web.xml配置

<!-- shiro -->
<filter>    
    <filter-name>shiroFilter</filter-name>    
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>    
    <filter-name>shiroFilter</filter-name>    
    <url-pattern>/*</url-pattern>
</filter-mapping>

2. 开发自定义Realm

2.1 创建测试数据表

获取地址:shiro测试数据 密码:3kvh

创建好 实体 model

 

2.3 编写后台支持服务

用户 mapper 接口 编写

package com.zking.shiro.mapper;
​
import com.zking.shiro.model.User;
import org.springframework.stereotype.Repository;
​
import java.util.Set;
​
@Repository
public interface IUserMapper {
    /**
     * 用户登录功能
     * @param us
     * @return
     */
  User login(User us);
​
    /**
     *  获取用户信息
     * @param userName
     * @return
     */
  Set<String> getRolesByUserName(String userName);
​
    /**
     * 通过用户名称获取权限信息
     * @param userName
     * @return
     */
    Set<String> getPermissionsByUserName(String userName);
}
​

用户 mybatis xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zking.shiro.mapper.IUserMapper">
<!--  用户登录功能 -->
    <select id="login" resultType="com.zking.shiro.model.User">
 select user_id userId,username userName,password ,salt,locked,create_datetime createDatetime from t_sys_user
       <where>
            username = #{userName}
       </where>
    </select>
​
    <select id="getRolesByUserName" resultType="java.lang.String">
        SELECT t2.role_name
        FROM `t_sys_user` t join t_sys_user_role t1 on t.user_id =t1.user_id join t_sys_role t2 on t1.role_id = t2.role_id
       <where>
            t.username= #{userName}
       </where>
    </select>
​
​
    <select id="getPermissionsByUserName" resultType="java.lang.String">
        SELECT t4.permission
        FROM `t_sys_user` t join t_sys_user_role t1 on t.user_id =t1.user_id join t_sys_role t2 on t1.role_id = t2.role_id join
             t_sys_role_permission t3 on t2.role_id = t3.role_id join t_sys_permission
            t4 on t3.permission_id=t4.permission_id
        <where>
            t.username= #{userName}
        </where>
    </select>
​
​
</mapper>

编写 service 省略

2.4 编写自定义的Realm

shiro中的Realm UML:

package com.zking.shiro.shiro;
​
import com.zking.shiro.model.User;
import com.zking.shiro.service.IUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
​
import java.util.Objects;
​
​
public class CustomerRealm extends AuthorizingRealm {
    @Autowired
    private IUserService userService;
​
    /**
     * 提供授权信息
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        
        String username =principalCollection.getPrimaryPrincipal().toString();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //放入用户信息 用户身份
        info.setRoles(userService.getRolesByUserName(username));
        //获取用户所拥有的权限
        info.setStringPermissions(userService.getPermissionsByUserName(username));
        return info;
    }
​
    /**
     * 提供认证信息
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String username =authenticationToken.getPrincipal().toString();
        User user = new User();
        user.setUserName(username);
        User db_login = userService.login(user);
        if(Objects.isNull(db_login)){
         throw new UnknownAccountException("用户 :" +username +"不存在");
        }
        //这里 可以写死 为 1  此处在用户实体中定义了一个常量
       if(db_login.getLocked() == User.USER_LOCKED){
           throw new LockedAccountException("用户 :" +username +"被锁定");
       }
        //此处考虑了加密 所以我们需要准备 md5 的加密
        //告诉 shiro 我的md5 加密盐为 ByteSource.Util.bytes(db_login.getSalt())
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(db_login.getUserName(), db_login.getPassword(), ByteSource.Util.bytes(db_login.getSalt()),this.getName());
        return simpleAuthenticationInfo;
    }
}
 

2.5配置文件 放入spring 中

​
  <!-- 自定义的Realm -->
    <bean id="customerRealm" class="com.zking.shiro.shiro.CustomerRealm">
        <property name="credentialsMatcher">
            <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--指定hash算法为MD5-->
                <property name="hashAlgorithmName" value="md5"/>
                <!--指定散列次数为1024次-->
                <property name="hashIterations" value="1024"/>
                <!--true指定Hash散列值使用Hex加密存. false表明hash散列值用用Base64-encoded存储-->
                <property name="storedCredentialsHexEncoded" value="true"/>
            </bean>
        </property>
    </bean>
​
 <!--  注册 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="customerRealm"/>
</bean>
​
​
    <!--Shiro核心过滤器-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- Shiro的核心安全接口,这个属性是必须的 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 身份验证失败,跳转到登录页面 -->
        <property name="loginUrl" value="/"/>
        <!-- 身份验证成功,跳转到指定页面 -->
        <!--<property name="successUrl" value="/index.jsp"/>-->
        <!-- 权限验证失败,跳转到指定页面 -->
        <!--<property name="unauthorizedUrl" value="/user/noauthorizeUrl"/>-->
        <!-- Shiro连接约束配置,即过滤链的定义 -->
        <property name="filterChainDefinitions">
            <value>
                <!--
                注:anon,authcBasic,auchc,user是认证过滤器
                    perms,roles,ssl,rest,port是授权过滤器
                -->
                <!--anon 表示匿名访问,不需要认证以及授权-->
                <!--authc表示需要认证 没有进行身份认证是不能进行访问的-->
                <!--roles[admin]表示角色认证,必须是拥有admin角色的用户才行-->
                /user/login=anon
                /book/**=authc
                /common/**=authc
                <!-- /css/**           = anon
                 /images/**            = anon
                 /js/**                = anon
                 /                     = anon
                 /user/logout          = logout
                 /user/**              = anon
                 /userInfo/**          = authc
                 /dict/**              = authc
                 /console/**           = roles[admin]
                 /**                   = anon-->
            </value>
        </property>
    </bean>
​
    <!-- Shiro生命周期,管理shiro bean的生命周期 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
​

md5加密

注: MD5 MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5算法是不可逆的,即便得到了加密以后的密文,也不可能通过解密算法反算出明文。 用途: 1)密码管理,如用户密码的MD5加密后再保存数据库 2)电子签名,MD5 算法还可以作为一种电子签名的方法来使用,使用 MD5算法就可以为任何文件(不管其大小、格式、数量)产生一个独一无二的“数字指纹”,借助这个“数字指纹”,通过检查文件前后 MD5 值是否发生了改变,就可以知道源文件是否被改动。(回想一下JWT的令牌签名)

2.6登录Controller

package com.zking.shiro.controller;
​
import com.sun.prism.impl.shape.ShapeUtil;
import com.zking.shiro.model.User;
import com.zking.shiro.service.IUserService;
import com.zking.shiro.utils.RetrunData;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
​
import javax.servlet.http.HttpSession;
import java.util.HashMap;
​
@Controller
/* 访问时 需要添加 user */
//@RequestMapping("/user")
public class UserController {
    @RequestMapping("/")
    public String index(){
        return "login";
    }
   @RequestMapping("/user/login")
    public String login(User us, Model model, HttpSession session){
        Subject subject= SecurityUtils.getSubject();
        UsernamePasswordToken passwordToken =new            UsernamePasswordToken(us.getUserName(), us.getPassword());
        try{
            subject.login(passwordToken);
        }catch (UnknownAccountException | LockedAccountException e){
            model.addAttribute("message",e.getMessage());
           return "login";
         }catch (AuthenticationException e){
            model.addAttribute("message","账号或密码错误");
            return "login";
        }
        session.setAttribute("user",us);
        return "index";
    }
​
}
​

登录界面 注意此处 可能 会有所不同

 

<%--
  Created by IntelliJ IDEA.
  User: lisensir
  Date: 2022/6/22
  Time: 22:20
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page pageEncoding="utf-8" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>登录页面</h1>
${message}
<form id="loginForm" action="<%=request.getContextPath()%>/user/login" method="post">
    <input type="text" id="userName" name="userName"/> <br/>
    <input type="text" id="password" name="password"/>  <br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>
​

3.权限管理 登录后

1. 自定义Realm 此处上面的Realm中已有代码

2.权限标签

2.1 引入标签库

<%@taglib prefix="shiro" uri="Taglib | Apache Shiro" %>

Shiro标签库 guest标签 :验证当前用户是否为“访客”,即未认证(包含未记住)的用户 user标签 :认证通过或已记住的用户 authenticated标签 :已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在 notAuthenticated标签 :未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户 principal 标签 :输出当前用户信息,通常为登录帐号信息 hasRole标签 :验证当前用户是否属于该角色 lacksRole标签 :与hasRole标签逻辑相反,当用户不属于该角色时验证通过 hasAnyRole标签 :验证当前用户是否属于以下任意一个角色 hasPermission标签 :验证当前用户是否拥有指定权限 lacksPermission标签 :与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过

2.2 使用标签控制权限

注:只是用于演示权限标签,如果有权限则显示功能连接,否则不显示,所以并没有指定具体的链接。

 

<%--
  Created by IntelliJ IDEA.
  User: lisensir
  Date: 2022/6/23
  Time: 11:09
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="http://shiro.apache.org/tags" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<s:hasRole name="admin">
<h1>管理员</h1>
</s:hasRole>
​
​
<s:hasPermission name="system:dict:view">
<li><a href="#">书本查询</a></li>
</s:hasPermission>
<s:hasPermission name="system:dict:add">
    <li><a href="#">书本新增</a></li>
</s:hasPermission>
<s:hasPermission name="bookmanager:book:edit">
    <li><a href="#">书本修改</a></li>
</s:hasPermission>
<s:hasPermission name="bookmanager:book:del">
    <li><a href="#">书本删除</a></li>
</s:hasPermission>
​
<a href="<%=request.getContextPath()%>/userRealm">测试</a>
​
</body>
</html>
​

权限需要根据数据库 查看

3. 使用注解控制权限

常用注解: @RequiresAuthenthentication:表示当前Subject已经通过login进行身份验证;即 Subject.isAuthenticated()返回 true @RequiresUser:表示当前Subject已经身份验证或者通过记住我登录的 @RequiresGuest:表示当前Subject没有身份验证或者通过记住我登录过,即是游客身份 @RequiresRoles(value = {"admin","user"},logical=Logical.AND):表示当前Subject需要角色admin和user @RequiresPermissions(value = {"user:delete","user:b"},logical = Logical.OR):表示当前Subject需要权限user:delete或者user:b

3.1 springMVC配置

开启注解,必须将Shiro注解的开启放置到spring-mvc.xml中(即放在springMVC容器中加载),否则Shiro注解开启无效

 
  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
      depends-on="lifecycleBeanPostProcessor">
    <property name="proxyTargetClass" value="true"></property>
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"/>
</bean>

 

在Controller中加入用于演示的请求处理器:

 

在index.jsp中加入功能连接,用于演示权限控制。

 

4.2 注解权限验证失败不跳转路径问题

问题原因:由于我们架构是用springmvc框架来搭建的所以项目的路径跳转是由springmvc 来控制的,也就是说我们在shiro里面的配置没有用

 

解决 springmvc中有一个org.springframework.web.servlet.handler.SimpleMappingExceptionResolver类就可以解决这个问题

<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="org.apache.shiro.authz.UnauthorizedException">
                unauthorized
            </prop>
        </props>
    </property>
  </bean>

编写一个用于提示没有权限的页面,页面名称为unauthorized,与上面的配置文件中配置的保持一致,、

<%--
  Created by IntelliJ IDEA.
  User: lkf
  Date: 2022/9/6
  Time: 19:13
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1> 权限不足 </h1>
</body>
</html>

4. shiro中的缓存

在权限验证时每次从数据库中获取权限数据显然是不合适的,更合适方式是将数据缓存到内存,以提高系统性能,shiro中可以方便的配置缓存,具体的配置如下:

1.1 引入需要的第三方包

 <!-- shiro依赖包 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>${shiro-version}</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>${shiro-version}</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro-version}</version>
</dependency>
​
        <!-- 缓存需要的包 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring-version}</version>
        </dependency>

1.2 ehcache配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
​
    <diskStore path="java.io.tmpdir"/>
    
    <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                  timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
​
​
    <!--name: Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap里)-->
   <!--  <cache name="stuCache" eternal="false" maxElementsInMemory="100"
           overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0"
           timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU"/> -->
</ehcache>

 

1.2 配置spring的配置文件

<!-- shiro 缓存-->
<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    <property name="configLocation" value="classpath:ehcache.xml"/>
    <property name="shared" value="true"></property>
</bean>
<bean id="shrioEhcache" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    <property name="cacheManager" ref="cacheManagerFactory"></property>
</bean>
//此安全管理器 上方配置文件中 已经带有 只需要 把下面的缓存配置  加进去即可
<!-- 将自定义的realm注入到安全管理器中 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="shiroRealm" />
    
    <!-- 为安全管理器配置缓存 -->
    <property name="cacheManager" ref="shrioEhcache"></property>
</bean>

2. shiro中的session

2.1 shiro中的session特点和常用方法

shiro的session和httpsession概念上是相同的,指定都是客户端和服务器之间的一次会话。 shiro的session的特点如下:

  • 不依赖于底层的容器(比如Tomcat),httpsession是依赖于底层容器的,这意味中shiro的session可以脱离容器使用

  • 提供了会话管理,会话监听,会话存储等功能, 可以支持会话过期,实现单点登录等。

常用的方法: Subject.getSession(true/false) 获取session,如果为参数为true,则没有session时会创建session,如果为false则没有session返回空 session.getId() 获取当前会话的ID号 session.getHost() 获取当权会话的主机地址 session.getTimeout() 获取过期时间 session.setTimeout() 设置当前时间 session.getStartTimestamp() 获取会话启动的时间戳 session.getLastAccessTime() 获取会话的最后访问时间 session.touch() 更新最后一次访问时间 session.stop() 销毁会话 session.setAttribute(kay,val) session.getAttribute(key) session.removeAttribute(key)

shiro中的session也提供了监听器,和HttpSessionListener类似,提供如下方法: onStart() -- 监听启动事件 onStop() -- 监听停止事件 onExpiration() -- 监听过期事件

2.2 如何使用

shiro中的session使用起来非常简单,通过设置在中的数据,可以在Shiro的Session中直接获取 通过 shiro中的session保存的数据 通过 HttpSession 也一样可以获取

  @PostMapping("/user")
    public String getUser(User user, Model model, HttpSession session) {
​
        session.setAttribute("uname", user.getUsername());
        //两者都同步数据
         System.out.println(session.getAttribute("us"));
        ...
    }
@Service
public class SessionService implements ISessionService {
​
    @Override
    public void testSession() {
        //在service中通过Subject获取session使用,这里的uname属性是通过HttpSession类设置的
        Session session = SecurityUtils.getSubject().getSession();
        System.out.println("===== session : " + session.getAttribute("uname"));
          SecurityUtils.getSubject().getSession().setAttribute("us","haoya");
    }
}

如何验证缓存配置的有效性:

  • 首先不配置缓存,在自定义的Realm中输出一些提示信息,启动服务后会发现每次调用登录,或其他配置了权限认证的功能,都会输出提示信息

  • 配置缓存,然后重新运行相同的通过,查看后台,加载一次后及不会再有输出

2.3 Session监听

用于监听session的创建,过期等事件,如果在需要时可以再session创建时做些初始化操作,或在过期时做些清理操作。

1) 创建一个自定义监听器

@Slf4j
public class SessionListener extends SessionListenerAdapter {
​
    @Override
    public void onStart(Session session) {
        log.info("Shiro session onStart .... ");
    }
​
    @Override
    public void onStop(Session session) {
        log.info("Shiro session onStop .... ");
    }
​
    @Override
    public void onExpiration(Session session) {
        log.info("Shiro session onExpiration ....");
    }
​
}

2)配置文件,在spring配置文件中做如下配置

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="shiroRealm" />
        <!-- 注入缓存管理器 -->
        <property name="cacheManager" ref="shrioEhcache"/>
        //只需要在 shiro的安全管理器中配置 下一条即可  
        <!-- session管理器 -->
        <property name="sessionManager" ref="sessionManager"/>
    </bean>
​
    <!-- session管理器 ,配置自定义监听器,同时需要将该sessionManager配置到securityManager中-->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="sessionListeners">
            <list>
                //此路径 需要配置你在及项目中过滤器
                <bean class="com.zking.shirodemo.listener.SessionListener"/>
            </list>
        </property>
    </bean>

2.4 SessionDao 将数据报错数据库 让后同步数据

 

首先先编写 数据访问层 mapper 接口定义

package com.zking.shiro.mapper;
​
import com.zking.shiro.model.SessionModel;
import org.springframework.stereotype.Repository;
​
@Repository
public interface ISessionMapper {
​
    void addSession(SessionModel session);
​
    SessionModel getSession(SessionModel session);
​
    void delSession(SessionModel session);
​
    void updateSession(SessionModel session);
​
​
}

xml 实现

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zking.shiro.mapper.ISessionMapper">
​
​
    <insert id="addSession">
        insert into t_sys_session(session_id,session) values (#{sessionid},#{session})
    </insert>
    <update id="updateSession">
        update  t_sys_session <set>
        <if test="session != null">
            session = #{session}
        </if>
    </set>
    <where>
        and session_id=#{sessionid}
    </where>
    </update>
​
    <delete id="delSession">
        delete from t_sys_session where session_id=#{sessionid}
    </delete>
    <select id="getSession" resultType="com.zking.shiro.model.SessionModel">
        select id,session_id sessionid,session from t_sys_session where session_id= #{sessionid}
    </select>
</mapper>

自行编写 service 层 !!!!

 

实现自定义SessionDao的步骤:

  • 编写自定义的SessionDao,集成自EnterpriseCacheSessionDao

  • 增删改查

  • 保存方式选择了json 下方有工具包 代码

package com.zking.shiro.listener;
​
import com.zking.shiro.model.SessionModel;
import com.zking.shiro.service.ISessionService;
import com.zking.shiro.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.springframework.beans.factory.annotation.Autowired;
​
import java.io.Serializable;
import java.util.Objects;
​
/**
 * 自定义Session持久化,将Shiro的Session数据保存到数据库中。
 * 完成该类的编写后,需要在spring配置文件中进行配置
 */
@Slf4j
public class DbSessionDao extends EnterpriseCacheSessionDAO {
    @Autowired
    private ISessionService service;
​
    @Override
    protected Serializable doCreate(Session session) {
        Serializable sid = super.doCreate(session);
        SessionModel sessionModel = new SessionModel();
        sessionModel.setSessionid(sid.toString());
        //把session转为json保存
        sessionModel.setSession(JsonUtils.toJson(session));
        log.debug("将session保存到数据库, sessionId = {}", sid);
        service.addSession(sessionModel);
        return sid;
    }
​
    @Override
    protected Session doReadSession(Serializable sessionId) {
​
        Session session = super.doReadSession(sessionId);
​
        //如果从内存中获取了session,则直接返回
        if (!Objects.isNull(session)) {
            log.debug("从内存中获取session,sessionId = " + sessionId + ", 直接返回");
            return session;
        }
​
        log.debug("从内存中没有获取到session,id={}, 将从数据库获取session", sessionId);
        SessionModel model = new SessionModel();
        model.setSessionid(sessionId.toString());
        SessionModel session1 = service.getSession(model);
​
        if(Objects.isNull(session1)) {
            log.debug("数据库中也没有找到id={}的session,将返回null");
            return session;
        }
        session  = (Session) JsonUtils.toJson(session1.getSession(),Session.class);
​
        return session;
    }
​
    @Override
    protected void doDelete(Session session) {
        SessionModel model=new SessionModel();
        model.setSessionid(session.getId().toString());
        log.debug("删除session,sessionId: " + session.getId().toString());
        service.delSession(model);
        super.doDelete(session);
    }
​
​
    @Override
    protected void doUpdate(Session session) {
​
        String sessionId = session.getId().toString();
        SessionModel tmpModel = new SessionModel();
        tmpModel.setSessionid(sessionId);
        SessionModel model = service.getSession(tmpModel);
​
        if(Objects.isNull(model)) {
            //检查 session 是否 有效 无效 就不加入数据库
            Object obj = session.getAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
​
            //数据库中是否有session,如果没有检查session无效,则直接返回,否则保存到数据库中
            if(Objects.isNull(obj) || !Boolean.parseBoolean(obj.toString())) {
                return ;
            }
​
            SessionModel saveModel = new SessionModel();
            saveModel.setSessionid(session.getId().toString());
            saveModel.setSession(JsonUtils.toJson(session));
            log.debug("session已经过验证,且在数据库中不存在,将session保存到数据库 ..... ");
            service.addSession(saveModel);
        } else {
            //如果session在数据库中已存在,则更新session
            model.setSession(JsonUtils.toJson(session));
            log.debug("session在数据库中已存在,将session更新到数据库 ..... ");
            service.updateSession(model);
        }
​
        //调用父类方法,更新session
        super.doUpdate(session);
    }
}
​

使用的json工具

依赖文件

 <!-- 导入解析 Json的包  -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.13.3</version>
    </dependency>

工具包

package com.zking.shiro.utils;
​
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
​
public final class  JsonUtils {
    private JsonUtils(){}
    private static ObjectMapper map = new ObjectMapper();
​
    /**
     * 将 对象转字符串
     *
     * @param obj
     * @return
     */
    public static String toJson(Object obj
    ) {
​
        String s = null;
        try {
            s = map.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
​
        return s;
    }
​
    /**
     * 将 json字符串 转为 对象
     *
     * @param json
     * @return
     */
    public static  <T> Object toJson(String json, T t) {
​
​
        try {
            return map.readValue(json, t.getClass());
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
​
}

在 spring 配置文件中配置 时效

 <!-- session管理器 ,配置自定义监听器,同时需要将该sessionManager配置到securityManager中-->
  
  <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="sessionListeners">
            <list>
                <bean class="com.zking.shiro.listener.SessionListener"/>
            </list>
        </property>
​
        <!-- 配置 session 关闭服务器有效 session持久化  -->
        <!-- 删除失效session -->
        <property name="sessionValidationSchedulerEnabled" value="true" />
        <!-- session失效时间(毫秒) -->
        <property name="globalSessionTimeout" value="1800000" />
        <property name="sessionDAO" ref="customShiroSessionDao" />
    </bean>

将session 管理器配置到shiro 管理器中 刚刚以及配置过

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值