实现密码MD5加密,挤人(单用户登录),统计在线人数,记住我和利用redis实现session缓存和共享
1.Maven
org.apache.shiro
shiro-core
1.2.3
org.apache.shiro
shiro-spring
1.2.3
com.alibaba
druid
1.0.20
org.crazycake
shiro-redis
2.4.2.1-RELEASE
shiro-core
org.apache.shiro
2.shiro配置文件:ShiroConfiguration.java
我将配置文件内容分为四大部分,第一部分是最基础和必要的权限控制配置,其中里面有无加密和加密的选择,第二部分是开启cookie的记住我功能,第三是开启redis,实现session共享和缓存,第四是开启shiro注解。
package com.sun.demo.shiro;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
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.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
/*
*这个类就相当于spring整合shiro的spring-shiro.xml中对shiro的配置。
*/
@Configuration
class ShiroConfiguration {
//1.==========================================必要,基础的权限配置==========================================
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//设置securityManager
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//设置登录页面,authc未登录时跳转的界面
//可以写路由也可以写jsp页面的访问路径
//如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
shiroFilterFactoryBean.setLoginUrl("/login");
//设置登录成功跳转的页面
shiroFilterFactoryBean.setSuccessUrl("/pages/index.jsp");
//设置未授权跳转的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/pages/unauthorized.jsp");
//定义过滤器
LinkedHashMap filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/index", "authc");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/loginUser", "anon");
filterChainDefinitionMap.put("/admin", "roles[admin]");
filterChainDefinitionMap.put("/edit", "perms[delete]");
filterChainDefinitionMap.put("/nurse", "roles[nurse]");
filterChainDefinitionMap.put("/doctor", "roles[doctor]");
//游客,开发权限
filterChainDefinitionMap.put("/guest/**", "anon");
//用户,需要角色权限 “user”
filterChainDefinitionMap.put("/user/**", "roles[user]");
//管理员,需要角色权限 “admin”
filterChainDefinitionMap.put("/admin/**", "roles[admin]");
//开放登陆接口
filterChainDefinitionMap.put("/login", "anon");
//其余接口一律拦截
//主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 定义安全管理器securityManager,注入自定义的realm,在这里对输入的密码和数据库密码经过realm设置的加密方式来进行对照策略(默认是相等)
* @param userRealm
* @return
*/
@Bean(name = "defaultWebSecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
//将缓存注入安全管理器,就不会反复执行 realm的授权方法了;只要实现了shiro的cache接口、CacheManager接口就可以用来注入安全管理器
//shiro自带的一个内存缓存,本质是hashmap,MemoryConstrainedCacheManager(),试验没问题,非常轻,简单的登录用这个
defaultWebSecurityManager.setCacheManager(cacheManager());
// 自定义session管理 使用redis,nigix试验分布式,确实 做到了session共享
defaultWebSecurityManager.setSessionManager(sessionManager());
//注入记住我cookie管理器;
defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
return defaultWebSecurityManager;
}
// 无论是加密还是不加密,必选之一
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<不使用MD5加密
// @Bean(name = "userRealm")
// public UserRealm getUserRealm(){
// return new UserRealm();
// }
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<使用MD5加密 start
@Bean("userRealm")
public UserRealm getUserRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
UserRealm authRealm = new UserRealm();
authRealm.setAuthorizationCachingEnabled(false);
//设置加密格式
authRealm.setCredentialsMatcher(matcher);
return authRealm;
}
/**
* 密码校验规则HashedCredentialsMatcher
* 这个类是为了对密码进行编码的 ,
* 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
* 这个类也负责对form里输入的密码进行编码
* 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式为MD5
credentialsMatcher.setHashAlgorithmName("MD5");
//加密次数
credentialsMatcher.setHashIterations(1024);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>使用MD5加密end
/**
* lifecycleBeanPostProcessor是负责生命周期的 , 初始化和销毁的类
* (可选)我这里写了但是没用
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
//2.===========================================开启cookie的记住我===========================================
/**
* cookie对象;
* @return
*/
public SimpleCookie rememberMeCookie(){
System.out.println("cookie11111");
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//
simpleCookie.setMaxAge(2592000);
return simpleCookie;
}
/**
* cookie管理对象;记住我功能
* @return
*/
public CookieRememberMeManager rememberMeManager(){
System.out.println("cookieManager11111");
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
//3.==================================redis缓存,实现session,认证缓存==================================
/**
* 配置shiro redisManager
* 网上的一个 shiro-redis 插件,实现了shiro的cache接口、CacheManager接口就
* @return
*/
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost("localhost");
redisManager.setPort(6379);
redisManager.setExpire(18000);// 配置过期时间
// redisManager.setTimeout(timeout);
redisManager.setPassword("123456");
return redisManager;
}
/**
* Redis集群使用RedisClusterManager,单个Redis使用RedisManager
* cacheManager 缓存 redis实现
* 网上的一个 shiro-redis 插件
* @return
*/
@Bean
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
*/
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* shiro session的管理
*/
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
//4.==========================================Shiro注解 ==================================================
/**
* 开启Shiro注解(如@RequiresRoles,@RequiresPermissions),
* 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启aop注解支持
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
3.自定义realm (认证和授权) :UserRealm.java
里面授权和认证具体内容按照自己的项目进行适当更改,只是展示授权和认证形式
package com.sun.demo.shiro;
import com.sun.demo.service.Impl.UserServiceImpl;
import com.sun.demo.service.UserService;
import com.sun.demo.entity.Permission;
import com.sun.demo.entity.Role;
import com.sun.demo.entity.User;
import com.sun.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
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.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* 自定义realm
*/
public class UserRealm extends AuthorizingRealm{
@Autowired
private UserServiceImpl userService;
/**
* 为用户授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取前端输入的用户信息,封装为User对象
User userweb = (User) principals.getPrimaryPrincipal();
//获取前端输入的用户名
String username = userweb.getUsername();
//根据前端输入的用户名查询数据库中对应的记录
User user = userService.findByUsername(username);
//如果数据库中有该用户名对应的记录,就进行授权操作
if (user != null){
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//因为addRoles和addStringPermissions方法需要的参数类型是Collection
//所以先创建两个collection集合
Collection rolesCollection = new HashSet();
Collection perStringCollection = new HashSet();
//获取user的Role的set集合
Set roles = user.getRoles();
//遍历集合
for (Role role : roles){
//将每一个role的name装进collection集合
rolesCollection.add(role.getName());
//获取每一个Role的permission的set集合
Set permissionSet = role.getPermissions();
//遍历集合
for (Permission permission : permissionSet){
//将每一个permission的name装进collection集合
perStringCollection.add(permission.getName());
}
//为用户授权
info.addStringPermissions(perStringCollection);
}
//为用户授予角色
info.addRoles(rolesCollection);
return info;
}else{
return null;
}
}
/**
* 认证登录
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//=========================未加密版==========================
//token携带了用户登录的信息
// UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//获取前端输入的用户名
//String username = usernamePasswordToken.getUsername();
//根据前端输入的用户名查询数据库中的记录
// User user = userService.findByUsername(username);
//校验密码,验证登录
// return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());
//=========================MD5加密版=========================
//token携带了用户信息
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//获取前端输入的用户名
String userName = usernamePasswordToken.getUsername();
//根据用户名查询数据库中对应的记录
User user = userService.findByUsername(userName);
if (null == user) {
throw new UnknownAccountException("未找到该用户!");
}
System.out.println(user.getUsername()+"用户登陆了,user = "+user.toString());
//单用户登录,在线人数
//处理session
DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
DefaultWebSessionManager sessionManager = (DefaultWebSessionManager) securityManager.getSessionManager();
//获取当前已登录的用户session列表
Collection sessions = sessionManager.getSessionDAO().getActiveSessions();
User temp;
int allUser=sessions.size();
for(Session session : sessions){
//清除该用户以前登录时保存的session,强制退出
Object attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (attribute == null) {
continue;
}
temp = (User) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
if(userName.equals(temp.getUsername())) {
allUser--;
sessionManager.getSessionDAO().delete(session);
}
}
System.out.println("现在登录人数为:"+allUser);
//当前realm对象的name
String realmName = getName();
//盐值
ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername());
//封装用户信息,构建AuthenticationInfo对象并返回,里面是输入的信息和数据库信息,加密策略,盐,到安全管理器securityManager
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, user.getPassword(), credentialsSalt, realmName);
return authcInfo;
}
}
4.增加session的监控和管理(可选,增强)
/**
* shiro session的管理
*/
public DefaultWebSessionManager sessionManager() {
Collection listeners = new ArrayList();
listeners.add(sessionListener());
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
//我写的SessionListener没啥子大的作用,没有写太多功能增强,以后可以扩展该类
sessionManager.setSessionListeners(listeners);
//修改Cookie名称,由JSESSIONID改为我们定义的sid,有利于与Tomcat 、Jetty默认的JSESSIONID 冲突
sessionManager.setSessionIdCookieEnabled(true);
sessionManager.setSessionIdCookie(sessionIdCookie());
//去掉shiro登录时url里的JSESSIONID,shiro版本1.3.2及以后才可以设置
sessionManager.setSessionIdUrlRewritingEnabled(false);
//全局会话超时时间(单位毫秒)
sessionManager.setGlobalSessionTimeout(60 * 30 * 1000);
//是否开启删除无效的session对象 默认为true
sessionManager.setDeleteInvalidSessions(true);
//是否开启定时调度器进行检测过期session 默认为true
sessionManager.setSessionValidationSchedulerEnabled(true);
return sessionManager;
}
/**
* 配置保存sessionId的cookie
* 注意:这里的cookie 不是上面的记住我 cookie 记住我需要一个cookie session管理 也需要自己的cookie
* @return
*/
@Bean("sessionIdCookie")
public SimpleCookie sessionIdCookie(){
SimpleCookie simpleCookie = new SimpleCookie("sid");
//maxAge=-1表示浏览器关闭时才失效此Cookie
//shiro默认会去修改cookie的Max-age=0,让浏览器1小时后把cookie删掉
//这样用户访问我们网站就会报错:没登陆!,但实际redis里面还是存在该session,只是浏览器端对应的cookie被删了
simpleCookie.setMaxAge(-1);
return simpleCookie;
}
/**
* 配置session监听
* @return
*/
@Bean("sessionListener")
public ShiroSessionListener sessionListener(){
ShiroSessionListener sessionListener = new ShiroSessionListener();
return sessionListener;
}
ShiroSessionListener.class
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author: wangsaichao
* @date: 2018/5/15
* @description: 配置session监听器
*/
public class ShiroSessionListener implements SessionListener{
/**
* 统计在线人数
* juc包下线程安全自增
*/
private final AtomicInteger sessionCount = new AtomicInteger(0);
/**
* 会话创建时触发
* @param session
*/
@Override
public void onStart(Session session) {
//会话创建,在线人数加一
System.out.println("会话创建:" + session.getId());
sessionCount.incrementAndGet();
}
/**
* 退出会话时触发
* @param session
*/
@Override
public void onStop(Session session) {
//会话退出,在线人数减一
System.out.println("会话停止:" + session.getId());
sessionCount.decrementAndGet();
}
/**
* 会话过期时触发
* @param session
*/
@Override
public void onExpiration(Session session) {
//会话过期,在线人数减一
System.out.println("会话过期:" + session.getId());
sessionCount.decrementAndGet();
}
/**
* 获取在线人数使用
* @return
*/
public AtomicInteger getSessionCount() {
return sessionCount;
}
}
5.带盐的MD5加密代码
注册和测试时可以用
package com.sun.demo.test;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import org.springframework.util.DigestUtils;
import java.math.BigInteger;
import java.security.MessageDigest;
public class MD5 {
public static void main(String[] args) {
String hashAlgorithName = "MD5";
String password = "123";
int hashIterations = 1024;//加密次数
ByteSource credentialsSalt = ByteSource.Util.bytes("zx");
Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
System.out.println(obj);
}
}
注意:
1.因为用了redis缓存session,所以相关的实体类必须可序列化 (implements Serializable)
2.配置shiro redisManager时,使用shiro-redis 插件,它所所依赖的包会和热部署的包冲突,出现从redis取对象反序列化失败,出现java.lang.ClassCastException 两个一样的类不能强转。两种解决方案:
一是放弃热部署,删掉pom.xml中相关依赖,二是添加配置文件解决(我选了这个):spring-devtools.properties,可以放在resources/META-INF下
restart.include.mapper=/mapper-[\\w-\\.]+jar
restart.include.pagehelper=/pagehelper-[\\w-\\.]+jar
# 因为我项目中引用了 org.crazycake:shiro-redis ,所以要引用下面这个配置
restart.include.shiro=/shiro-[\\w-\\.]+jar
3.Shiro的认证注解处理是有内定的处理顺序的,如果有个多个注解的话,前面的通过了会继续检查后面的,若不通过则直接返回,处理顺序依次为(与实际声明顺序无关)
4.shiro注解
@RequiresAuthentication
表示当前Subject已经通过login 进行了身份验证;即Subject. isAuthenticated()返回true。
@RequiresUser
表示当前Subject已经身份验证或者通过记住我登录的。
@RequiresGuest
表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)
@RequiresRoles(value={“admin”})
@RequiresRoles({“admin“})
表示当前Subject需要角色admin 和user。
@RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR)
表示当前Subject需要权限user:a或user:b。
5.需要注意的是,退出登录时需要调用Subject.logout()方法,该方法会自动删除redis中的session和cache缓存。
6.使用shiro-redis做Session共享后,跟踪源码发现在修改角色名称后AuthorizationInfo中的角色名称依然是修改之前的。所以就需要用户退出后重登才会更新认证信息。
7.如果说想在注册成功或者修改密码成功后,即数据库已经更新了。如果想要直接登陆当前已经注册的账号或者修改用户信息的账户,只需要利用更新后的数据库中的账号密码重新登录(密码,无论你是是否加密的,都只需要明文的,也就是用户输的密码,注意密码要加toCharArray)。
用户无需操作,也感受不到,但是其实已经重新登录过了,后台已经重新登录了。如果你使用替身,记得也要更新session中的user属性
UsernamePasswordToken token = new UsernamePasswordToken();
token.setUsername(username);
token.setPassword(password.toCharArray());
SecurityUtils.getSubject().login(token);
下面这种(也就是登录时用到的),也可以
UsernamePasswordToken token = new UsernamePasswordToken(username, password,rememberMe);
// 从SecurityUtils里边创建一个 subject
Subject subject = SecurityUtils.getSubject();
// 执行认证登陆
subject.login(token);
8.spring集成shiro后对request和session的影响
request和session 已经被shiro包装过了,封装了httpRequest和httpSession,但是操作都没啥差别。
spring整合shiro后,可以通过两种方式获取到session:
//方法里携带
public String getUserByHttpSession(HttpSession session) {
User user = (User) session.getAttribute("user");
System.out.println(user.getUsername());
return "getUserByHttpSession";
}
//通过HttpServletRequest获取session
Session session = request.getSession();
//通过shiro获取session
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
依旧使用request,response,session:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
使用redis共享的session,就会由sessionDao管理,不用管有什么区别,正常使用即可
但是有点不同的是如何获取当前用户?
1.从shiro的subject获取,这是真正的用户
后台
User user = (User)SecurityUtils.getSubject().getPrincipal();
前台
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
2.替身。你可能会发现使用上面的可能有点不方便或者不习惯,我们可以使用“替身”,在登陆时在session中增加名为user的属性,值为此用户,这个只是替身,因为session里的真正用户无法直接获取getAttribute();
// 执行认证登陆
subject.login(token);
session.setAttribute("user", subject.getPrincipal());
我们查看一下session的数据
------org.apache.shiro.subject.support.DefaultSubjectContext_AUTHENTICATED_SESSION_KEY:true--------
------shiroSavedRequest:org.apache.shiro.web.util.SavedRequest@1a13a25--------
//替身
------user:User{uid=4, username='szw', password='f1de8fd2e48e32b39701c6df0cdd4557', email='null', roles=[Role{rid=4, name='NurseDefault'}]}--------
//真正的用户
------org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY:User{uid=4, username='szw', password='f1de8fd2e48e32b39701c6df0cdd4557', email='null', roles=[Role{rid=4, name='NurseDefault'}]}--------
相关知识:
为啥在shiro中使用redis来实现session共享
1.加快session查询和权限认证速度,减少服务器压力
2.当我们的用户量到达一定程度的时候,单机的服务器已经支撑不了目前的访问量,我们肯定需要对服务器做集群(分布式)来提高应用的负载能力.比如我们使用Nginx+Tomcat来实现集群,如果使用的是轮询的测试,我肯定需要涉及到Session共享的问题,解决Session共享有很多种方案,我们选择的是Shiro+Redis来实现。因为Shiro中本身就提供了sessionManager和sessionDAO,我们可以把shiro和redis集成起来,把session持久化到Redis中,然后需要使用的时候从Redis中获取对应的session.
不错的相关文章
shiro注解
SpringBoot整合Shiro,权限的动态加载、更新,Shiro-Redis实现分布式Session共享,挤人功能
springboot+shiro+redis项目整合
spring boot 集成shiro和redis(对redis更加深入的处理)
session共享
springboot整合shiro,redis缓存session
springmvc集成shiro后,session、request姓汪还是姓蒋?(很不错,发现shiro对spring的影响)
springboot整合shiro-session管理(六)(有对session更深的利用和处理)
shiro获取登陆用户和修改用户信息的方法