springboot + shiro之登录人数限制、登录判断重定向、session时间设置
springboot + shiro之登录人数控制
项目
前篇:spring boot + mybatis + layui + shiro后台权限管理系统:https://blog.51cto.com/wyait/2082803
本文是基于spring boot + mybatis + layui + shiro后台权限管理系统开发的,新增功能:
- shiro并发登陆人数控制(超出登录用户最大配置数量,清理用户)功能;
- 解决在父子页面中,判断用户未登录之后,重定向到登录页面嵌套显示问题;
- 解决ajax请求,判断用户未登录之后,如何重定向到登录页面问题;
- 解决使用并完成了功能1,导致的session有效时间冲突问题。
后篇:
- springboot + shiro 动态更新用户信息:https://blog.51cto.com/wyait/2112200
- springboot + shiro 权限注解、统一异常处理、请求乱码解决 :https://blog.51cto.com/wyait/2125708
项目源码
项目源码:(包含数据库源码)
github源码: https://github.com/wyait/manage.git
码云:https://gitee.com/wyait/manage.git
github对应项目源码目录:wyait-manage-1.2.0
码云对应项目源码目录:wyait-manage-1.2.0
场景
同一个用户,先在A×××登录;之后在B×××登录时,退出A×××的登录状态;反之相同。或者限制同一个用户在不同的设备上,同时在线的数量;
技术实现
解决思路
/*** Returns <code>true</code> if the request is allowed to proceed through the filter normally, or <code>false</code>* if the request should be handled by the* {@link #onAccessDenied(ServletRequest,ServletResponse,Object) onAccessDenied(request,response,mappedValue)}* method instead.** @param request the incoming <code>ServletRequest</code>* @param response the outgoing <code>ServletResponse</code>* @param mappedValue the filter-specific config value mapped to this filter in the URL rules mappings.* @return <code>true</code> if the request should proceed through the filter normally, <code>false</code> if the* request should be processed by this filter's* {@link #onAccessDenied(ServletRequest,ServletResponse,Object)} method instead.* @throws Exception if an error occurs during processing.*/protected abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;... .../*** Processes requests where the subject was denied access as determined by the* {@link #isAccessAllowed(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Object) isAccessAllowed}* method.** @param request the incoming <code>ServletRequest</code>* @param response the outgoing <code>ServletResponse</code>* @return <code>true</code> if the request should continue to be processed; false if the subclass will* handle/render the response directly.* @throws Exception if there is an error processing the request.*/protected abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;
查看抽象类AccessControlFilter:
isAccessAllowed:表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false;
onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理了,将直接返回即可。
- onPreHandle:会自动调用这两个方法决定是否继续处理;
另外AccessControlFilter还提供了如下方法用于处理如登录成功后/重定向到上一个请求:
void setLoginUrl(String loginUrl) //身份验证时使用,默认/login.jsp
String getLoginUrl()
Subject getSubject(ServletRequest request, ServletResponse response) //获取Subject实例
boolean isLoginRequest(ServletRequest request, ServletResponse response)//当前请求是否是登录请求
void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException //将当前请求保存起来并重定向到登录页面
void saveRequest(ServletRequest request) //将请求保存起来,如登录成功后再重定向回该请求
void redirectToLogin(ServletRequest request, ServletResponse response) //重定向到登录页面
要进行用户访问控制,可以继承AccessControlFilter。
- 思路:
a. 登陆成功时将用户保存到了shiro提供的session中,并同时添加到ehcache缓存中;
b. KickoutSessionFilter拿到了session之后先判断能不能通过缓存取到值,如果取得到再和服务器端session进行匹配(用户的名字(每个用户的名字必须不同));
c. 如果匹配,系统会为新登录的用户新建一个session;之前的session确认失效并踢出,老用户就无法继续操作而被迫下线;
shiro技术实现流程
下面就是自定义的访问控制拦截器:KickoutSessionFilter:
自定义过滤器类KickoutSessionFilter
package com.wyait.manage.filter;import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.Deque;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;import com.wyait.manage.pojo.User;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.lyd.admin.pojo.AdminUser;/*** * @项目名称:wyait-manager* @类名称:KickoutSessionFilter* @类描述:自定义过滤器,进行用户访问控制* @创建人:wyait* @创建时间:2018年4月24日 下午5:18:29* @version:*/
public class KickoutSessionFilter extends AccessControlFilter {private static final Logger logger = LoggerFactory.getLogger(KickoutSessionFilter.class);private String kickoutUrl; // 踢出后到的地址private boolean kickoutAfter = false; // 踢出之前登录的/之后登录的用户 默认false踢出之前登录的用户private int maxSession = 1; // 同一个帐号最大会话数 默认1private SessionManager sessionManager;private Cache<String, Deque<Serializable>> cache;public void setKickoutUrl(String kickoutUrl) {this.kickoutUrl = kickoutUrl;}public void setKickoutAfter(boolean kickoutAfter) {this.kickoutAfter = kickoutAfter;}public void setMaxSession(int maxSession) {this.maxSession = maxSession;}public void setSessionManager(SessionManager sessionManager) {this.sessionManager = sessionManager;}// 设置Cache的key的前缀public void setCacheManager(CacheManager cacheManager) {//必须和ehcache缓存配置中的缓存name一致this.cache = cacheManager.getCache("shiro-activeSessionCache");}@Overrideprotected boolean isAccessAllowed(ServletRequest request,ServletResponse response, Object mappedValue) throws Exception {return false;}@Overrideprotected boolean onAccessDenied(ServletRequest request,ServletResponse response) throws Exception {Subject subject = getSubject(request, response);// 没有登录授权 且没有记住我if (!subject.isAuthenticated() && !subject.isRemembered()) {// 如果没有登录,直接进行之后的流程return true;}Session session = subject.getSession();logger.debug("==session时间设置:" + String.valueOf(session.getTimeout())+ "===========");try {// 当前用户User user = (User) subject.getPrincipal();String username = user.getUsername();logger.debug("===当前用户username:==" + username);Serializable sessionId = session.getId();logger.debug("===当前用户sessionId:==" + sessionId);// 读取缓存用户 没有就存入Deque<Serializable> deque = cache.get(username);logger.debug("===当前deque:==" + deque);if (deque == null) {// 初始化队列deque = new ArrayDeque<Serializable>();}// 如果队列里没有此sessionId,且用户没有被踢出;放入队列if (!deque.contains(sessionId)&& session.getAttribute("kickout") == null) {// 将sessionId存入队列deque.push(sessionId);// 将用户的sessionId队列缓存cache.put(username, deque);}// 如果队列里的sessionId数超出最大会话数,开始踢人while (deque.size() > maxSession) {logger.debug("===deque队列长度:==" + deque.size());Serializable kickoutSessionId = null;// 是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;if (kickoutAfter) { // 如果踢出后者kickoutSessionId = deque.removeFirst();} else { // 否则踢出前者kickoutSessionId = deque.removeLast();}// 踢出后再更新下缓存队列cache.put(username, deque);try {// 获取被踢出的sessionId的session对象Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));if (kickoutSession != null) {// 设置会话的kickout属性表示踢出了kickoutSession.setAttribute("kickout", true);}} catch (Exception e) {// ignore exception}}// ajax请求// 如果被踢出了,(前者或后者)直接退出,重定向到踢出后的地址if ((Boolean) session.getAttribute("kickout") != null&& (Boolean) session.getAttribute("kickout") == true) {// 会话被踢出了try {// 退出登录subject.logout();} catch (Exception e) { // ignore}saveRequest(request);logger.debug("==踢出后用户重定向的路径kickoutUrl:" + kickoutUrl);// 重定向WebUtils.issueRedirect(request, response, kickoutUrl);return false;}return true;} catch (Exception e) { // ignore//重定向到登录界面WebUtils.issueRedirect(request, response, "/login");return false;}}}
设置ShiroConfig配置类
- SessionDAO 用于会话的CRUD;查看该接口源码:
public interface SessionDAO { /*如DefaultSessionManager在创建完session后会调用该方法; 如保存到关系数据库/文件系统/NoSQL数据库;即可以实现会话的持久化; 返回会话ID;主要此处返回的ID.equals(session.getId()); */ Serializable create(Session session); //根据会话ID获取会话 Session readSession(Serializable sessionId) throws UnknownSessionException; //更新会话;如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用 void update(Session session) throws UnknownSessionException; //删除会话;当会话过期/会话停止(如用户退出时)会调用 void delete(Session session); //获取当前所有活跃用户,如果用户量多此方法影响性能 Collection<Session> getActiveSessions();
}
SessionDAO实现类:
a. AbstractSessionDAO提供了SessionDAO的基础实现,如生成会话ID等;
b. CachingSessionDAO提供了对开发者透明的会话缓存的功能,只需要设置相应的CacheManager即可;
c. MemorySessionDAO直接在内存中进行会话维护;
d. EnterpriseCacheSessionDAO提供了缓存功能的会话维护,默认情况下使用MapCache实现,内部使用ConcurrentHashMap保存缓存的会话。
- ShiroConfig配置类中EnterpriseCacheSessionDAO配置:
/*** EnterpriseCacheSessionDAO shiro sessionDao层的实现;* 提供了缓存功能的会话维护,默认情况下使用MapCache实现,内部使用ConcurrentHashMap保存缓存的会话。*/ @Bean public EnterpriseCacheSessionDAO enterCacheSessionDAO() {EnterpriseCacheSessionDAO enterCacheSessionDAO = new EnterpriseCacheSessionDAO();//添加缓存管理器//enterCacheSessionDAO.setCacheManager(ehCacheManager());//添加ehcache活跃缓存名称(必须和ehcache缓存名称一致)enterCacheSessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");return enterCacheSessionDAO; }
- SessionManager配置:
/**** @描述:sessionManager添加session缓存操作DAO* @创建人:wyait* @创建时间:2018年4月24日 下午8:13:52* @return*/@Beanpublic DefaultWebSessionManager sessionManager() {DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();//sessionManager.setCacheManager(ehCacheManager());sessionManager.setSessionDAO(enterCacheSessionDAO());return sessionManager;}
- kickoutSessionFilter配置
/**** @描述:kickoutSessionFilter同一个用户多设备登录限制* @创建人:wyait* @创建时间:2018年4月24日 下午8:14:28* @return*/public KickoutSessionFilter kickoutSessionFilter(){KickoutSessionFilter kickoutSessionFilter = new KickoutSessionFilter();//使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;//这里我们还是用之前shiro使用的ehcache实现的cacheManager()缓存管理//也可以重新另写一个,重新配置缓存时间之类的自定义缓存属性kickoutSessionFilter.setCacheManager(ehCacheManager());//用于根据会话ID,获取会话进行踢出操作的;kickoutSessionFilter.setSessionManager(sessionManager());//是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;踢出顺序。kickoutSessionFilter.setKickoutAfter(false);//同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;kickoutSessionFilter.setMaxSession(1);//被踢出后重定向到的地址;kickoutSessionFilter.setKickoutUrl("/toLogin?kickout=1");return kickoutSessionFilter;}
- 将SessionManager交给SecurityManager管理
/*** shiro安全管理器设置realm认证、ehcache缓存管理、session管理器、Cookie记住我管理器* @return*/@Bean public org.apache.shiro.mgt.SecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 设置realm.securityManager.setRealm(shiroRealm());// //注入ehcache缓存管理器;securityManager.setCacheManager(ehCacheManager());// //注入session管理器;securityManager.setSessionManager(sessionManager());//注入Cookie记住我管理器securityManager.setRememberMeManager(rememberMeManager());return securityManager;}
- 配置filterChainDefinitionMap
...
//添加kickout认证
HashMap<String,Filter> hashMap=new HashMap<String,Filter>();
hashMap.put("kickout",kickoutSessionFilter());
shiroFilterFactoryBean.setFilters(hashMap);
...
filterChainDefinitionMap.put("/**", "kickout,authc");
...
解决子页面,重定向之后,出现页面嵌套的问题
新增登录中转页面toLogin.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<!--head部分-->
<head th:include="layout :: htmlhead" th:with="title='利易达贷款后台'">
</head>
<script type="text/javascript">
var href=window.location.href;
if(href.indexOf("kickout")>0){setTimeout("top.location.href='/login?kickout';", 0);
}else{setTimeout("top.location.href='/login';", 0);
}
</script>
</html>
更改shiro中filterChainDefinitionMap配置
// 指定要求登录时的链接
shiroFilterFactoryBean.setLoginUrl("/toLogin");
...
// 配置不会被拦截的链接 从上向下顺序判断
filterChainDefinitionMap.put("/login", "anon");
上面两个配置,即可解决页面重定向后,嵌套问题。
ajax请求问题
如果对用户在线数量进行限制,踢出了之前登录的用户A;这时候用户A在系统中,发送了一个ajax请求,会出现弹框空白等问题;
解决方案
- 自定义ShiroFilterUtils工具类判断请求是否为ajax
package com.wyait.manage.utils;import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*** * @项目名称:wyait-manager* @类名称:ShiroFilterUtils* @类描述:shiro工具类* @创建人:wyait* @创建时间:2018年4月24日 下午5:12:04 * @version:*/
public class ShiroFilterUtils {private static final Logger logger = LoggerFactory.getLogger(ShiroFilterUtils.class);/*** * @描述:判断请求是否是ajax* @创建人:wyait* @创建时间:2018年4月24日 下午5:00:22* @param request* @return*/public static boolean isAjax(ServletRequest request){String header = ((HttpServletRequest) request).getHeader("X-Requested-With");if("XMLHttpRequest".equalsIgnoreCase(header)){logger.debug("shiro工具类【wyait-manager-->ShiroFilterUtils.isAjax】当前请求,为Ajax请求");return Boolean.TRUE;}logger.debug("shiro工具类【wyait-manager-->ShiroFilterUtils.isAjax】当前请求,非Ajax请求");return Boolean.FALSE;}
}
- 调整KickoutSessionFilter过滤器,新增ajax请求判断和响应
private final static ObjectMapper objectMapper = new ObjectMapper();
...
// ajax请求
/*** 判断是否已经踢出* 1.如果是Ajax 访问,那么给予json返回值提示。* 2.如果是普通请求,直接跳转到登录页*/
//判断是不是Ajax请求
ResponseResult responseResult = new ResponseResult();
if (ShiroFilterUtils.isAjax(request) ) {logger.debug(getClass().getName()+ "当前用户已经在其他地方登录,并且是Ajax请求!");responseResult.setCode(IStatusMessage.SystemStatus.MANY_LOGINS.getCode());responseResult.setMessage("您已在别处登录,请您修改密码或重新登录");out(response, responseResult);
}else{// 重定向WebUtils.issueRedirect(request, response, kickoutUrl);
}
...
/*** * @描述:response输出json* @创建人:wyait* @创建时间:2018年4月24日 下午5:14:22* @param response* @param result*/
public static void out(ServletResponse response, ResponseResult result){PrintWriter out = null;try {response.setCharacterEncoding("UTF-8");//设置编码response.setContentType("application/json");//设置返回类型out = response.getWriter();out.println(objectMapper.writeValueAsString(result));//输出logger.error("用户在线数量限制【wyait-manager-->KickoutSessionFilter.out】响应json信息成功");} catch (Exception e) {logger.error("用户在线数量限制【wyait-manager-->KickoutSessionFilter.out】响应json信息出错", e);}finally{if(null != out){out.flush();out.close();}}
}
- 前端编写公共判断用户是否登录方法isLogin
/*** 判断是否登录,没登录刷新当前页,促使Shiro拦截后跳转登录页* @param result ajax请求返回的值* @returns {如果没登录,刷新当前页}*/
function isLogin(result){if(result && result.code && result.code == '1101'){window.location.reload(true);//刷新当前页}return true;//返回true
}
- js中ajax调用isLogin方法
$.post("/user/delUser",{"id":id},function(data){//判断用户是否登录if(isLogin(data)){if(data=="ok"){//回调弹框layer.alert("删除成功!",function(){layer.closeAll();//加载load方法load(obj);//自定义});}else{layer.alert(data);//弹出错误提示}}
});
只改动了userList.js用户列表界面,其他界面//TODO
- 测试
同一个用户在线冲突测试,然后点击先登录用户界面中其中一个ajax方法,如果后台用户已退出,前台isLogin刷新页面,重新请求重定向到/toLogin?kickout页面,最终跳转到登录界面。
session有效时间设置
session默认有效时间:30分钟(1800s)
- spring boot session时间配置:
# 会话超时(秒)1天 server.session.timeout=86400
- session有效时间问题
使用shiro进行用户在线数量限制功能中,securityManager配置sessionManager之后,springboot中配置的session有效时间无效(sessionManager管理器覆盖了springboot中session有效时间的配置)。
session过期问题
使用shiro进行用户在线数量限制功能;用户登录后,2分钟不操作,之后session失效。
原因
- spring boot整合shiro,在使用shiro进行用户在线数量限制时,重新配置了SessionManger,
// //注入session管理器;
securityManager.setSessionManager(sessionManager());
SessionManager,配置EnterpriseCacheSessionDAO:
sessionManager.setSessionDAO(enterCacheSessionDAO());
EnterpriseCacheSessionDAO类,存取session的时候,是通过ehcache缓存中操作的。
这里如果配置有缓存的话需要给其配置一个cache的键类似于:
shiro默认了一个默认值为:shiro-activeSessionCache,如果不相同(cache文件中的键值) 需要进行替换,最终进行session存取的类为CachingSessionDAO
缓存管理器使用的是org.apache.shiro.cache.ehcache.EhCacheManager,那么最终shiro在找session的时候也会调用getCache。
Ehcache.xml配置
<!-- shiro-activeSessionCache活跃用户session缓存策略 --><cache name="shiro-activeSessionCache"maxElementsInMemory="10000"timeToIdleSeconds="120"timeToLiveSeconds="120"maxElementsOnDisk="10000000"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"></cache>
这里配置了session缓存时间为2分钟,故会出现登录2分钟无操作后,session失效问题。
- shiro拿到ehcache缓存中的session后,和服务器中的session校验匹配,这时,如果服务器的session失效,也会出现问题;
假设设置服务器端当前用户的session为30s【SecurityUtils.getSubject().getSession().setTimeout(30000);//毫秒
】,ehcache中session有效时间120s不变;在无操作30s后,请求后台,报错如下:
org.apache.shiro.session.ExpiredSessionException: Session with id [8aac0daf-c432-44b6-86cc-a618095ad2bd] has expired. Last access time: 18-4-24 上午11:32. Current time: 18-4-24 上午11:33. Session timeout is set to 30 seconds (0 minutes)at org.apache.shiro.session.mgt.SimpleSession.validate(SimpleSession.java:292) ~[shiro-core-1.3.1.jar:1.3.1]at org.apache.shiro.session.mgt.AbstractValidatingSessionManager.doValidate(AbstractValidatingSessionManager.java:186) ~[shiro-core-1.3.1.jar:1.3.1]
... ...
故ehcache缓存中session的有效时间和服务器端session有效时间必须配置一致。
解决方案
- 服务端session时间设置:
//session有效时间1天(毫秒)
SecurityUtils.getSubject().getSession().setTimeout(86400000);
- 设置的最大时间,正负都可以,为负数时表示永不超时。
SecurityUtils.getSubject().getSession().setTimeout(-1000l);
注意:这里设置的时间单位是:ms,但是Shiro会把这个时间转成:s,而且是会舍掉小数部分,这样设置的是-1ms,转成s后就是0s,马上就过期了。所有要是除以1000以后还是负数,必须设置小于-1000
- 将Ehcache.xml时间配置和服务器设置的session有效时间保持一致。
<!-- shiro-activeSessionCache活跃用户session缓存策略(秒) --> <cache name="shiro-activeSessionCache"maxElementsInMemory="10000"timeToIdleSeconds="86400"timeToLiveSeconds="86400"maxElementsOnDisk="10000000"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"> </cache>
通过代码中查看session有效时间:
logger.debug("session设置的有效时间:"+request.getSession().getMaxInactiveInterval());
logger.debug("shiro中session设置的有效时间:"+SecurityUtils.getSubject().getSession().getTimeout());
//86400(秒)
//86400000(毫秒)
总结
具体实现可以根据具体需求做调整;近期提供redis实现版本。
20180426版本更新内容
- 编辑用户自己成功后,执行退出,重新登录信息生效;
- 禁止用户删除自己;
- 优化用户列表操作信息提示;
- 角色管理列表,通过添加参数callback,实现菜单回显选中;
20180503版本更新内容
- 新增用户表version版本字段;
- 更新用户操作,通过version字段来保证数据一致;
- 新增通过拦截器实现动态更新用户信息(同步更新在线用户信息);
- 新增登录成功后默认页面home.html;
- 页面操作细节优化。
spring boot + shiro 动态更新用户信息
链接入口--> spring boot + shiro 动态更新用户信息:https://blog.51cto.com/wyait/2112200
20180606版本更新内容
- 新增shiro权限注解;
- 请求乱码问题解决;
- 统一异常处理;
- 页面操作细节优化。
springboot + shiro 权限注解、统一异常处理、请求乱码解决
链接入口--> springboot + shiro 权限注解、统一异常处理、请求乱码解决 :https://blog.51cto.com/wyait/2125708
TODO
- 后台方法级别权限控制,通过shiro配置可实现;具体用户管理的操作根据业务实际的需求可做调整;
以上更新,项目wyait-manage、wyait-manage-1.2.0源码同步更新。
前篇:
spring boot + mybatis + layui + shiro后台权限管理系统:https://blog.51cto.com/wyait/2082803
后篇:
- springboot + shiro 动态更新用户信息:https://blog.51cto.com/wyait/2112200
- springboot + shiro 权限注解、统一异常处理、请求乱码解决 :https://blog.51cto.com/wyait/2125708
项目源码:(包含数据库源码)
github源码: https://github.com/wyait/manage.git
码云:https://gitee.com/wyait/manage.git
github对应项目源码目录:wyait-manage-1.2.0
码云对应项目源码目录:wyait-manage-1.2.0
转载于:https://blog.51cto.com/wyait/2107423
springboot + shiro之登录人数限制、登录判断重定向、session时间设置相关推荐
- Cisco IOS 的登录密码以及权限分配以及log时间设置
Cisco IOS 的登录密码以及权限分配以及log时间设置 enable password xxxx 初级密码,用于验证从用户模式到特权模式的验证 enable secret xxxxx ...
- 超详细!附源码!SpringBoot+shiro+mybatis+Thymeleaf实现权限登录系统
最近在做一个期末作品,就是使用ssm+thymeleaf+vue+shiro完成一个具有权限登录,且能实现用户信息增删查改的这么一个项目,下面仅仅是实现权限认证和登录.为什么我选shiro,而不选sp ...
- springboot+shiro+jwt实现token认证登录
准备: springboot 2.5.5 jdk 1.8 没有操作刷新token功能,也没有放redis做缓存 1.先贴代码 2.后讲一下验证逻辑 1.导入依赖 <!--shiro-->& ...
- springboot + shiro 尝试登录次数限制与并发登录人数控制
源码项目地址 尝试登录次数控制实现 实现原理 Realm在验证用户身份的时候,要进行密码匹配.最简单的情况就是明文直接匹配,然后就是加密匹配,这里的匹配工作则就是交给CredentialsMatche ...
- SpringBoot 并发登录人数控制
点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 作者:殷天文 www.jianshu.com/p/b6f5ec98d790 技术经验交流:点击入 ...
- 如何用 SpringBoot 实现并发登录人数控制(附代码)
点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达今日推荐:2020年7月程序员工资统计,平均14357元,又跌了,扎心个人原创100W+访问量博客:点击前往,查看更多 作者 ...
- springboot shiro和freemarker集成之权限控制完全参考手册(跳过认证,登录由三方验证,全网首发)...
本文主要考虑单点登录场景,登录由其他系统负责,业务子系统只使用shiro进行菜单和功能权限校验,登录信息通过token从redis取得,这样登录验证和授权就相互解耦了. 用户.角色.权限进行集中式管理 ...
- Shiro并发登录人数控制遇到的问题和解决
shiro并发登录人数控制遇到的问题和解决 问题1:KickoutSessionControlFilter不起作用 问题2:KickoutSessionControlFilter中cache为null ...
- SpringBoot + Shiro 实现微博登录
介绍在服务端使用 SpringBoot + Shiro ,用户端使用 jQuery 的环境下如何实现网站对接微博登录 更多精彩 更多技术博客,请移步 IT人才终生实训与职业进阶平台 - 实训在线 写在 ...
最新文章
- 从互联网跳到国企半年了,说说我的感受!
- paloalto防火墙执行初始配置
- markdown的11个语法
- HDU-3998 Sequence LIS统计
- echo -e “\033[字背景颜色 字体颜色m字符串\033[0m“解释
- LBP(Local Binary Patterns)局部二进制模式
- OpenGL基础22:贴图
- 详解如何设计一套健康体检信息管理系统
- C语言程序设计谭浩强(第四版)部分课后习题作答——第四章
- 基于MiniC的语义分析(使用javacc)
- 色貌模型-introduction
- 《Mastering OpenCV》--3.Markless AR.无标识式AR (1)
- Transformer单目标跟踪
- 设计需求分析方法与过程
- Vue项目在标签中如何书写多个内敛style样式
- Maven Archetype脚手架制作
- linux下网卡测速,Linux下 网卡测速
- 「前端」webp图片适配流量优化 1
- opencv支持向量机(python)
- Google大数据三篇著名论文中文版
热门文章
- 蓝桥杯 ALGO-54 算法训练 简单加法(基本型)
- 蓝桥杯 ALGO-110 算法训练 字符串的展开
- LeetCode 744. Find Smallest Letter Greater Than Target
- 如何去除chrome最常访问的网页
- L3-003. 社交集群-PAT团体程序设计天梯赛GPLT(并查集)
- Python存储和读取数据
- mysql组合索引与字段顺序
- 比较LANG和NLS_LANG在Linux中的作用
- 网络编程6_multiprocess模块.锁.队列
- BZOJ3123[Sdoi2013]森林——主席树+LCA+启发式合并