一 shiro整合原理
shiro整合redis分2个部分,1 session管理使用redis管理 2 cache缓存管理使用redis管理。如下通过2种方式实现
二 springboot自带的
- 加入pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
- 加入sessionmanager
@Configuration
@EnableRedisHttpSession(redisNamespace = "spring:session")
public class RedisSessionManager {
}
- 使用spring自带的cacheManager-------RedisCacheManager
可以对此manager添加自定义的cacheDao。如下内容 - 然后进行常规操作,shiro管理中使用redisSessionManager
三 自定义,使用redis自定义session管理和cache管理
- shiro和redis在springboot中的配置
# Shiro
shiro:
user:
# 登录地址
loginUrl: /login
# 权限认证失败地址
unauthorizedUrl: /unauth
# 首页地址
indexUrl: /index
# 验证码开关
captchaEnabled: true
# 验证码类型 math 数组计算 char 字符
captchaType: math
cookie:
# 设置Cookie的域名 默认空,即当前访问的域名
domain:
# 设置cookie的有效访问路径
path: /
# 设置HttpOnly属性
httpOnly: true
# 设置Cookie的过期时间,天为单位
maxAge: 30
# 设置密钥,务必保持唯一性(生成方式,直接拷贝到main运行即可)KeyGenerator keygen = KeyGenerator.getInstance("AES"); SecretKey deskey = keygen.generateKey(); System.out.println(Base64.encodeToString(deskey.getEncoded()));
cipherKey: zSyK5Kp6PZAAjlT+eeNMlg==
session:
# Session超时时间,-1代表永不过期(默认30分钟)
expireTime: 30
# 同步session到数据库的周期(默认1分钟)
dbSyncPeriod: 1
# 相隔多久检查一次session的有效性,默认就是10分钟
validationInterval: 10
# 同一个用户最大会话数,比如2的意思是同一个账号允许最多同时两个人登录(默认-1不限制)
maxSession: -1
# 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户
kickoutAfter: false
spring:
redis:
host: 127.0.0.1
port: 6379
password:
timeout: 5000 #连接超时时间(毫秒)
jedis:
pool:
max-active: 68 #连接池最大连接数,使用负数表示无限制
max-wait: 500 #连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 48 #连接池中的最大空闲连接
min-idle: 30
- 自定义sessionDao
package com.ruoyi.framework.shiro.session.redis;
import com.ruoyi.common.utils.RedisUtil;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.crazycake.shiro.RedisSessionDAO;
import org.crazycake.shiro.SerializeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class RedisSessionDao extends AbstractSessionDAO {
private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
private RedisTemplate redisTemplate = SpringUtils.getBean("shiroRedisTemplate");
private String keyPrefix = "shiro_redis_session:";
private int expire = 18000;
@Override
public void update(Session session) throws UnknownSessionException {
this.saveSession(session);
}
private void saveSession(Session session) throws UnknownSessionException{
if(session == null || session.getId() == null){
logger.error("session or session id is null");
return;
}
HttpServletRequest request = ServletUtils.getRequest();
if (request != null) {
String uri = request.getServletPath();
if(ServletUtils.isStaticFile(uri)){
return;
}
}
String key = getStringKey(session.getId());
logger.info("saveKey----"+key+"redisTemplate----"+redisTemplate.getKeySerializer()+"--hashkey--"+redisTemplate.getHashKeySerializer());
redisTemplate.opsForValue().set(key, session, expire, TimeUnit.SECONDS);
}
@Override
public void delete(Session session) {
if(session == null || session.getId() == null){
logger.error("session or session id is null");
return;
}
logger.info("delete----"+session.getId());
redisTemplate.delete(getStringKey(session.getId()));
logger.info("deleteFinish----");
}
@Override
public Collection<Session> getActiveSessions() {
Set<Session> sessions = new HashSet<Session>();
Set<String> keys =redisTemplate.keys(keyPrefix+"*");
logger.info("keys-----"+keys);
if(keys != null && keys.size()>0){
for(String key:keys){
Session s = (Session) redisTemplate.opsForValue().get(key);
sessions.add(s);
}
}
return sessions;
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
this.saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
Session s = null;
HttpServletRequest request = ServletUtils.getRequest();
if (request != null) {
String url =request.getServletPath();
System.out.println("url---"+url+"---"+request.getRequestURI());
s = (Session)request.getAttribute("session_"+sessionId);
}
if(null == s){
s = (Session) redisTemplate.opsForValue().get(getStringKey(sessionId));
logger.info("getRedisSession------"+s);
}
if (request != null && s != null){
request.setAttribute("session_"+sessionId, s);
}
return s;
}
private byte[] getByteKey(Serializable sessionId){
String preKey = this.keyPrefix + sessionId;
return preKey.getBytes();
}
private String getStringKey(Serializable sessionId){
String preKey = this.keyPrefix + sessionId;
return preKey;
}
public String getKeyPrefix() {
return keyPrefix;
}
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
public int getExpire() {
return expire;
}
public void setExpire(int expire) {
this.expire = expire;
}
}
- 自定义CacheDao
package com.ruoyi.framework.shiro.session.redis;
import com.ruoyi.common.utils.spring.SpringUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.crazycake.shiro.SerializeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Collection;
import java.util.Set;
public class RedisCache<K, V> implements Cache<K, V> {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private RedisTemplate redisTemplate = SpringUtils.getBean("shiroRedisTemplate");
private String keyPrefix = "shiro_redis_cache:";
public String getKeyPrefix() {
return keyPrefix;
}
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
public RedisCache(String prefix){
this.keyPrefix = prefix;
}
private byte[] getByteKey(Object key){
if(key instanceof String){
String v=""+key;
return v.getBytes();
}else{
return SerializeUtils.serialize(key);
}
}
@Override
public V get(K key) throws CacheException {
try {
if (key == null) {
return null;
}else{
Object o = redisTemplate.opsForHash().get(keyPrefix,key.toString());
return o == null ? null : (V) o;
}
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public V put(K key, V value) throws CacheException {
logger.debug("根据key从存储 key [" + key + "]");
if (key == null){
return null;
}
try {
redisTemplate.opsForHash().put(this.keyPrefix,key.toString(),value);
return value;
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public V remove(K key) throws CacheException {
logger.debug("从redis中删除 key [" + key + "]");
try {
V previous = get(key);
redisTemplate.opsForHash().delete(keyPrefix,key.toString());
return previous;
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public void clear() throws CacheException {
logger.debug("从redis中删除所有元素");
try {
redisTemplate.delete(keyPrefix);
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public int size() {
try {
return redisTemplate.opsForHash().size(keyPrefix).intValue();
} catch (Throwable t) {
throw new CacheException(t);
}
}
@SuppressWarnings("unchecked")
@Override
public Set<K> keys() {
try {
return redisTemplate.opsForHash().keys(keyPrefix);
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public Collection<V> values() {
try {
return redisTemplate.opsForHash().values(keyPrefix);
} catch (Throwable t) {
throw new CacheException(t);
}
}
}
- 自定义sessionManager以及其他shiro相关的配置
package com.ruoyi.framework.config;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import com.ruoyi.common.utils.RedisUtil;
import com.ruoyi.framework.shiro.session.OnlineSessionDAO;
import com.ruoyi.framework.shiro.session.redis.RedisCacheManager;
import com.ruoyi.framework.shiro.session.redis.RedisSessionDao;
import com.ruoyi.framework.shiro.session.redis.RedisSessionManager;
import com.ruoyi.framework.shiro.web.filter.sync.SyncOnlineSessionFilter;
import org.apache.commons.io.IOUtils;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
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.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 com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.shiro.realm.UserRealm;
import com.ruoyi.framework.shiro.web.filter.LogoutFilter;
import com.ruoyi.framework.shiro.web.filter.captcha.CaptchaValidateFilter;
import com.ruoyi.framework.shiro.web.filter.kickout.KickoutSessionFilter;
import com.ruoyi.framework.shiro.web.filter.online.OnlineSessionFilter;
import com.ruoyi.framework.shiro.web.session.SpringSessionValidationScheduler;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class ShiroRedisConfig
{
public static final String PREMISSION_STRING = "perms[\"{0}\"]";
@Value("${shiro.session.expireTime}")
private int expireTime;
@Value("${shiro.session.validationInterval}")
private int validationInterval;
@Value("${shiro.session.maxSession}")
private int maxSession;
@Value("${shiro.session.kickoutAfter}")
private boolean kickoutAfter;
@Value("${shiro.user.captchaEnabled}")
private boolean captchaEnabled;
@Value("${shiro.user.captchaType}")
private String captchaType;
@Value("${shiro.cookie.domain}")
private String domain;
@Value("${shiro.cookie.path}")
private String path;
@Value("${shiro.cookie.httpOnly}")
private boolean httpOnly;
@Value("${shiro.cookie.maxAge}")
private int maxAge;
@Value("${shiro.cookie.cipherKey}")
private String cipherKey;
@Value("${shiro.user.loginUrl}")
private String loginUrl;
@Value("${shiro.user.unauthorizedUrl}")
private String unauthorizedUrl;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWaitMillis;
@Value("${spring.redis.password}")
private String password;
@Bean(name = "jedisPool")
public JedisPool redisPoolFactory() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
JedisPool jedisPool = null;
if(password == null || password.equals("")){
jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout);
}else{
jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout,password);
}
return jedisPool;
}
@Bean(name = "shiroRedisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
@Bean
public RedisCacheManager getRedisCacheManager()
{
RedisCacheManager redisCacheManager = new RedisCacheManager();
return redisCacheManager;
}
@Bean
public UserRealm userRealm(RedisCacheManager cacheManager)
{
UserRealm userRealm = new UserRealm();
userRealm.setCacheManager(cacheManager);
return userRealm;
}
@Bean
public RedisSessionDao sessionDAO()
{
RedisSessionDao redisSessionDAO = new RedisSessionDao();
redisSessionDAO.setExpire(expireTime * 60);
return redisSessionDAO;
}
@Bean
public SimpleCookie simpleCookie(){
SimpleCookie simpleCookie = new SimpleCookie("shiro_redis_session");
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
return simpleCookie;
}
@Bean
public SessionManager sessionManager()
{
DefaultWebSessionManager manager = new DefaultWebSessionManager();
manager.setCacheManager(getRedisCacheManager());
manager.setDeleteInvalidSessions(true);
manager.setGlobalSessionTimeout(expireTime * 60 * 1000);
manager.setSessionValidationScheduler(SpringUtils.getBean(SpringSessionValidationScheduler.class));
manager.setSessionValidationSchedulerEnabled(true);
manager.setSessionDAO(sessionDAO());
manager.setSessionIdCookie(simpleCookie());
manager.setSessionIdCookieEnabled(true);
return manager;
}
@Bean
public SecurityManager securityManager(UserRealm userRealm)
{
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
securityManager.setCacheManager(getRedisCacheManager());
securityManager.setSessionManager(sessionManager());
return securityManager;
}
public LogoutFilter logoutFilter()
{
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setCacheManager(getRedisCacheManager());
logoutFilter.setLoginUrl(loginUrl);
return logoutFilter;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl(loginUrl);
shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/favicon.ico**", "anon");
filterChainDefinitionMap.put("/ruoyi.png**", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/docs/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/ajax/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/ruoyi/**", "anon");
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
filterChainDefinitionMap.put("/logout", "logout");
// 不需要拦截的访问
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/register", "anon");
Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
filters.put("kickout", kickoutSessionFilter());
filters.put("logout", logoutFilter());
shiroFilterFactoryBean.setFilters(filters);
filterChainDefinitionMap.put("/**", "user,kickout");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public CaptchaValidateFilter captchaValidateFilter()
{
CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();
captchaValidateFilter.setCaptchaEnabled(captchaEnabled);
captchaValidateFilter.setCaptchaType(captchaType);
return captchaValidateFilter;
}
public SimpleCookie rememberMeCookie()
{
SimpleCookie cookie = new SimpleCookie("rememberMe");
cookie.setDomain(domain);
cookie.setPath(path);
cookie.setHttpOnly(httpOnly);
cookie.setMaxAge(maxAge * 24 * 60 * 60);
return cookie;
}
public CookieRememberMeManager rememberMeManager()
{
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey(Base64.decode(cipherKey));
return cookieRememberMeManager;
}
public KickoutSessionFilter kickoutSessionFilter()
{
KickoutSessionFilter kickoutSessionFilter = new KickoutSessionFilter();
kickoutSessionFilter.setCacheManager(getRedisCacheManager());
kickoutSessionFilter.setSessionManager(sessionManager());
kickoutSessionFilter.setMaxSession(maxSession);
kickoutSessionFilter.setKickoutAfter(kickoutAfter);
kickoutSessionFilter.setKickoutUrl("/login?kickout=1");
return kickoutSessionFilter;
}
@Bean
public ShiroDialect shiroDialect()
{
return new ShiroDialect();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
@Qualifier("securityManager") SecurityManager securityManager)
{
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public RedisUtil redisUtil(){
RedisUtil redisUtil = new RedisUtil();
redisUtil.setExpire(expireTime * 60);
return redisUtil;
}
}
- 验证码相关类
package com.ruoyi.framework.shiro.web.filter.captcha;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.web.filter.AccessControlFilter;
import com.google.code.kaptcha.Constants;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.util.ShiroUtils;
public class CaptchaValidateFilter extends AccessControlFilter
{
private boolean captchaEnabled = true;
private String captchaType = "math";
public void setCaptchaEnabled(boolean captchaEnabled)
{
this.captchaEnabled = captchaEnabled;
}
public void setCaptchaType(String captchaType)
{
this.captchaType = captchaType;
}
@Override
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception
{
request.setAttribute(ShiroConstants.CURRENT_ENABLED, captchaEnabled);
request.setAttribute(ShiroConstants.CURRENT_TYPE, captchaType);
return super.onPreHandle(request, response, mappedValue);
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception
{
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (captchaEnabled == false || !"post".equals(httpServletRequest.getMethod().toLowerCase()))
{
return true;
}
return validateResponse(httpServletRequest, httpServletRequest.getParameter(ShiroConstants.CURRENT_VALIDATECODE));
}
public boolean validateResponse(HttpServletRequest request, String validateCode)
{
Object obj = ShiroUtils.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
String code = String.valueOf(obj != null ? obj : "");
request.getSession().removeAttribute(Constants.KAPTCHA_SESSION_KEY);
if (StringUtils.isEmpty(validateCode) || !validateCode.equalsIgnoreCase(code))
{
return false;
}
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception
{
request.setAttribute(ShiroConstants.CURRENT_CAPTCHA, ShiroConstants.CAPTCHA_ERROR);
return true;
}
}
- shiro记住我功能实现类
package org.apache.shiro.web.mgt;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.AbstractRememberMeManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.subject.WebSubjectContext;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CookieRememberMeManager extends AbstractRememberMeManager {
private static final transient Logger log = LoggerFactory.getLogger(CookieRememberMeManager.class);
public static final String DEFAULT_REMEMBER_ME_COOKIE_NAME = "rememberMe";
private Cookie cookie;
public CookieRememberMeManager() {
Cookie cookie = new SimpleCookie("rememberMe");
cookie.setHttpOnly(true);
cookie.setMaxAge(31536000);
this.cookie = cookie;
}
public Cookie getCookie() {
return this.cookie;
}
public void setCookie(Cookie cookie) {
this.cookie = cookie;
}
protected void rememberSerializedIdentity(Subject subject, byte[] serialized) {
if (!WebUtils.isHttp(subject)) {
if (log.isDebugEnabled()) {
String msg = "Subject argument is not an HTTP-aware instance. This is required to obtain a servlet request and response in order to set the rememberMe cookie. Returning immediately and ignoring rememberMe operation.";
log.debug(msg);
}
} else {
HttpServletRequest request = WebUtils.getHttpRequest(subject);
HttpServletResponse response = WebUtils.getHttpResponse(subject);
String base64 = Base64.encodeToString(serialized);
Cookie template = this.getCookie();
Cookie cookie = new SimpleCookie(template);
cookie.setValue(base64);
cookie.saveTo(request, response);
}
}
private boolean isIdentityRemoved(WebSubjectContext subjectContext) {
ServletRequest request = subjectContext.resolveServletRequest();
if (request == null) {
return false;
} else {
Boolean removed = (Boolean)request.getAttribute(ShiroHttpServletRequest.IDENTITY_REMOVED_KEY);
return removed != null && removed;
}
}
protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {
if (!WebUtils.isHttp(subjectContext)) {
if (log.isDebugEnabled()) {
String msg = "SubjectContext argument is not an HTTP-aware instance. This is required to obtain a servlet request and response in order to retrieve the rememberMe cookie. Returning immediately and ignoring rememberMe operation.";
log.debug(msg);
}
return null;
} else {
WebSubjectContext wsc = (WebSubjectContext)subjectContext;
if (this.isIdentityRemoved(wsc)) {
return null;
} else {
HttpServletRequest request = WebUtils.getHttpRequest(wsc);
HttpServletResponse response = WebUtils.getHttpResponse(wsc);
String base64 = this.getCookie().readValue(request, response);
if ("deleteMe".equals(base64)) {
return null;
} else if (base64 != null) {
base64 = this.ensurePadding(base64);
if (log.isTraceEnabled()) {
log.trace("Acquired Base64 encoded identity [" + base64 + "]");
}
byte[] decoded = Base64.decode(base64);
if (log.isTraceEnabled()) {
log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes.");
}
return decoded;
} else {
return null;
}
}
}
}
private String ensurePadding(String base64) {
int length = base64.length();
if (length % 4 != 0) {
StringBuilder sb = new StringBuilder(base64);
for(int i = 0; i < length % 4; ++i) {
sb.append('=');
}
base64 = sb.toString();
}
return base64;
}
protected void forgetIdentity(Subject subject) {
if (WebUtils.isHttp(subject)) {
HttpServletRequest request = WebUtils.getHttpRequest(subject);
HttpServletResponse response = WebUtils.getHttpResponse(subject);
this.forgetIdentity(request, response);
}
}
public void forgetIdentity(SubjectContext subjectContext) {
if (WebUtils.isHttp(subjectContext)) {
HttpServletRequest request = WebUtils.getHttpRequest(subjectContext);
HttpServletResponse response = WebUtils.getHttpResponse(subjectContext);
this.forgetIdentity(request, response);
}
}
private void forgetIdentity(HttpServletRequest request, HttpServletResponse response) {
this.getCookie().removeFrom(request, response);
}
}
- 同一账号同时登录人数限制
package com.ruoyi.framework.shiro.web.filter.kickout;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.Deque;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.framework.util.ShiroUtils;
import com.ruoyi.system.domain.SysUser;
public class KickoutSessionFilter extends AccessControlFilter
{
private final static ObjectMapper objectMapper = new ObjectMapper();
private int maxSession = -1;
private Boolean kickoutAfter = false;
private String kickoutUrl;
private SessionManager sessionManager;
private Cache<String, Deque<Serializable>> cache;
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o)
throws Exception
{
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception
{
Subject subject = getSubject(request, response);
if (!subject.isAuthenticated() && !subject.isRemembered() || maxSession == -1)
{
return true;
}
try
{
Session session = subject.getSession();
SysUser user = ShiroUtils.getSysUser();
String loginName = user.getLoginName();
Serializable sessionId = session.getId();
Deque<Serializable> deque = cache.get(loginName);
if (deque == null)
{
deque = new ArrayDeque<Serializable>();
}
if (!deque.contains(sessionId) && session.getAttribute("kickout") == null)
{
deque.push(sessionId);
cache.put(loginName, deque);
}
while (deque.size() > maxSession)
{
Serializable kickoutSessionId = null;
if (kickoutAfter)
{
kickoutSessionId = deque.removeFirst();
}
else
{
kickoutSessionId = deque.removeLast();
}
cache.put(loginName, deque);
try
{
Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
if (null != kickoutSession)
{
kickoutSession.setAttribute("kickout", true);
}
}
catch (Exception e)
{
}
}
if ((Boolean) session.getAttribute("kickout") != null && (Boolean) session.getAttribute("kickout") == true)
{
subject.logout();
saveRequest(request);
return isAjaxResponse(request, response);
}
return true;
}
catch (Exception e)
{
return isAjaxResponse(request, response);
}
}
private boolean isAjaxResponse(ServletRequest request, ServletResponse response) throws IOException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (ServletUtils.isAjaxRequest(req))
{
AjaxResult ajaxResult = AjaxResult.error("您已在别处登录,请您修改密码或重新登录");
ServletUtils.renderString(res, objectMapper.writeValueAsString(ajaxResult));
}
else
{
WebUtils.issueRedirect(request, response, kickoutUrl);
}
return false;
}
public void setMaxSession(int maxSession)
{
this.maxSession = maxSession;
}
public void setKickoutAfter(boolean kickoutAfter)
{
this.kickoutAfter = kickoutAfter;
}
public void setKickoutUrl(String kickoutUrl)
{
this.kickoutUrl = kickoutUrl;
}
public void setSessionManager(SessionManager sessionManager)
{
this.sessionManager = sessionManager;
}
public void setCacheManager(CacheManager cacheManager)
{
this.cache = cacheManager.getCache(ShiroConstants.SYS_USERCACHE);
}
}