目录
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 管理器中 刚刚以及配置过