Shiro教程、验证码kaptcha、successUrl不跳转问题解决
shiro教程 | http://jinnianshilongnian.iteye.com/blog/2018398 |
验证码kaptcha | http://aperise.iteye.com/blog/2276496 |
FormAuthenticationFilter 的successUrl不跳转问题 |
http://aperise.iteye.com/blog/2276496 |
1.shiro教程
shiro教程参见这篇博客,讲解的太细了,也讲的非常到位,望尘莫及,膜拜膜拜!
《跟我学Shiro》教程 http://jinnianshilongnian.iteye.com/blog/2018398
2.shiro中增加验证码kaptcha
2.1 增加对于kaptcha的maven依赖
<!-- kaptcha验证码 --> <dependency> <version>0.0.9</version> <groupId>com.github.axet</groupId> <artifactId>kaptcha</artifactId> </dependency>
2.2 web.xml中增加kaptcha servlet拦截
<!-- kaptcha验证码 --> <servlet> <servlet-name>kaptcha</servlet-name> <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class> <init-param> <param-name>kaptcha.noise.impl</param-name> <param-value>com.google.code.kaptcha.impl.NoNoise</param-value> </init-param> <!-- 验证码图片是否有边框 --> <init-param> <param-name>kaptcha.border</param-name> <param-value>yes</param-value> </init-param> <!-- 验证码图片宽度 --> <init-param> <param-name>kaptcha.image.width</param-name> <param-value>100</param-value> </init-param> <!-- 验证码图片高度 --> <init-param> <param-name>kaptcha.image.height</param-name> <param-value>50</param-value> </init-param> <!-- 图片上验证码位数 --> <init-param> <param-name>kaptcha.textproducer.char.length</param-name> <param-value>4</param-value> </init-param> </servlet> <!-- 拦截request路径为/kaptcha.jpg的请求,然后调用kaptcha产生验证码 --> <servlet-mapping> <servlet-name>kaptcha</servlet-name> <url-pattern>/kaptcha.jpg</url-pattern> </servlet-mapping>
2.3 login.jsp页面增加验证码功能
<script type="text/javascript"> var captcha; function refreshCaptcha(){ document.getElementById("img_kaptcha").src="${contextPath}/kaptcha.jpg?t=" + Math.random(); } </script> <li> <label for="username">用户名:</label> <input type="text" name="username" class="login_input" id="username" /> </li> <li> <label for="password">密码:</label> <input type="password" name="password" class="login_input" id="password" autocomplete="off" /> </li> <li> <label for="kaptcha">验证码:</label> <input type="text" name="kaptcha" class="login_input_kaptcha" id="kaptcha" autocomplete="off" /> <img class="login_input_kaptcha_img" alt="验证码" src="${contextPath}/kaptcha.jpg" title="点击更换" id="img_kaptcha" onclick="javascript:refreshCaptcha();" /> </li>
2.4 shiro配置里实现一个自己的AccessControlFilter类KaptchaFilter
public class KaptchaFilter extends AccessControlFilter { private String kaptchaParam = "kaptcha";// 前台提交的验证码参数名 public String getKaptchaParam() { return kaptchaParam; } public void setKaptchaParam(String kaptchaParam) { this.kaptchaParam = kaptchaParam; } @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { HttpServletRequest httpServletRequest = WebUtils.toHttp(request); // 验证验证码是否正确 if (null == request.getParameter(kaptchaParam)) { return true; } else { String kaptchaFromWeb = (String) request.getParameter(kaptchaParam); String kaptchaFromSession = (String) httpServletRequest.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY); return kaptchaFromSession.toUpperCase().equals(kaptchaFromWeb.toUpperCase()); } } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { // 如果验证码失败了,存储失败key属性 request.setAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME, "Kaptcha.error"); return true; } }
2.5 重写自己的FormAuthenticationFilter为KaptchaFormAuthenticationFilter
public class KaptchaFormAuthenticationFilter extends FormAuthenticationFilter { protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { if (request.getAttribute(getFailureKeyAttribute()) != null) { return true; } return super.onAccessDenied(request, response, mappedValue); } @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,ServletResponse response) throws Exception { WebUtils.issueRedirect(request, response, getSuccessUrl()); return false; } }
2.6 shiro配置里修改如下
<!-- 基于Form表单的身份验证过滤器 --> <bean id="formAuthenticationFilter" class="com.xxx.shrio.filter.KaptchaFormAuthenticationFilter"> <property name="usernameParam" value="username" /> <property name="passwordParam" value="password" /> <property name="rememberMeParam" value="rememberMe" /> <property name="loginUrl" value="/login" /> <property name="successUrl" value="/index"/> </bean> <bean id="kaptchaFilter" class="com.xxx.shrio.filter.KaptchaFilter"> <property name="kaptchaParam" value="kaptcha" /> </bean> <!-- Shiro的Web过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login"/> <property name="successUrl" value="/index"/> <property name="unauthorizedUrl" value="/redirect"/> <property name="filters"> <util:map> <entry key="authc" value-ref="formAuthenticationFilter" /> <entry key="sysUser" value-ref="sysUserFilter" /> <entry key="kaptcha" value-ref="kaptchaFilter" /> </util:map> </property> <property name="filterChainDefinitions"> <value> /login = kaptcha,authc /unauthorized.jsp = authc /redirect = anon /css/** = anon /js/** = anon /img/** = anon /kaptcha.jpg = anon /** = user,sysUser </value> </property> </bean>
2.7 最后验证码添加后效果
3.shiro的FormAuthenticationFilter的successUrl不跳转问题
3.1 首先查看shiro源代码WebUtils类里方法
/** * Redirects the to the request url from a previously * {@link #saveRequest(javax.servlet.ServletRequest) saved} request, or if there is no saved request, redirects the * end user to the specified {@code fallbackUrl}. If there is no saved request or fallback url, this method * throws an {@link IllegalStateException}. * <p/> * This method is primarily used to support a common login scenario - if an unauthenticated user accesses a * page that requires authentication, it is expected that request is * {@link #saveRequest(javax.servlet.ServletRequest) saved} first and then redirected to the login page. Then, * after a successful login, this method can be called to redirect them back to their originally requested URL, a * nice usability feature. * * @param request the incoming request * @param response the outgoing response * @param fallbackUrl the fallback url to redirect to if there is no saved request available. * @throws IllegalStateException if there is no saved request and the {@code fallbackUrl} is {@code null}. * @throws IOException if there is an error redirecting * @since 1.0 */ public static void redirectToSavedRequest(ServletRequest request, ServletResponse response, String fallbackUrl) throws IOException { String successUrl = null; boolean contextRelative = true; SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request); if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase(AccessControlFilter.GET_METHOD)) { successUrl = savedRequest.getRequestUrl(); contextRelative = false; } if (successUrl == null) { successUrl = fallbackUrl; } if (successUrl == null) { throw new IllegalStateException("Success URL not available via saved request or via the " + "successUrlFallback method parameter. One of these must be non-null for " + "issueSuccessRedirect() to work."); } WebUtils.issueRedirect(request, response, successUrl, null, contextRelative); }首先通过SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request);
从session中获取到第一次请求时的地址,然后通过successUrl = savedRequest.getRequestUrl();
将FormAuthenticationFilter配置的successUrl值覆盖掉,所以shiro默认跳转到了首次请求的url了。
3.2 shiro的FormAuthenticationFilter的successUrl不跳转问题解决
重写自己的FormAuthenticationFilter类KaptchaFormAuthenticationFilter,主要是覆盖里面的方法onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,ServletResponse response),代码如下:
public class KaptchaFormAuthenticationFilter extends FormAuthenticationFilter { protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { if (request.getAttribute(getFailureKeyAttribute()) != null) { return true; } return super.onAccessDenied(request, response, mappedValue); } @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,ServletResponse response) throws Exception { WebUtils.issueRedirect(request, response, getSuccessUrl()); return false; } }然后覆盖shiro配置:
<!-- 基于Form表单的身份验证过滤器 --> <bean id="formAuthenticationFilter" class="com.xxx.shrio.filter.KaptchaFormAuthenticationFilter"> <property name="usernameParam" value="username" /> <property name="passwordParam" value="password" /> <property name="rememberMeParam" value="rememberMe" /> <property name="loginUrl" value="/login" /> <property name="successUrl" value="/index"/> </bean>这样,shiro登录成功后只会跳转到FormAuthenticationFilter配置的successUrl这个地址。
4.shiro集成spring 4.x和quartz 2.x报错java.lang.InstantiationError: org.quartz.SimpleTrigger
shiro集成spring 4.x和quartz 2.x报错java.lang.InstantiationError: org.quartz.SimpleTrigger,主要原因是shiro的org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler是对quartz1.x的实现,但是quartz2.x已经变动很大,导致整合quartz2.x时候,shiro原来对于shiro的实现不可用 ,解决办法如下:
4.1 自己实现quartz2.x的org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler
shiro对于quartz1.x的实现如下:
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.shiro.session.mgt.quartz; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SimpleTrigger; import org.quartz.impl.StdSchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.shiro.session.mgt.DefaultSessionManager; import org.apache.shiro.session.mgt.SessionValidationScheduler; import org.apache.shiro.session.mgt.ValidatingSessionManager; /** * An implementation of the {@link org.apache.shiro.session.mgt.SessionValidationScheduler SessionValidationScheduler} that uses Quartz to schedule a * job to call {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions()} on * a regular basis. * * @since 0.1 */ public class QuartzSessionValidationScheduler implements SessionValidationScheduler { //TODO - complete JavaDoc /*-------------------------------------------- | C O N S T A N T S | ============================================*/ /** * The default interval at which sessions will be validated (1 hour); * This can be overridden by calling {@link #setSessionValidationInterval(long)} */ public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL; /** * The name assigned to the quartz job. */ private static final String JOB_NAME = "SessionValidationJob"; /*-------------------------------------------- | I N S T A N C E V A R I A B L E S | ============================================*/ private static final Logger log = LoggerFactory.getLogger(QuartzSessionValidationScheduler.class); /** * The configured Quartz scheduler to use to schedule the Quartz job. If no scheduler is * configured, the scheduler will be retrieved by calling {@link StdSchedulerFactory#getDefaultScheduler()} */ private Scheduler scheduler; private boolean schedulerImplicitlyCreated = false; private boolean enabled = false; /** * The session manager used to validate sessions. */ private ValidatingSessionManager sessionManager; /** * The session validation interval in milliseconds. */ private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL; /*-------------------------------------------- | C O N S T R U C T O R S | ============================================*/ /** * Default constructor. */ public QuartzSessionValidationScheduler() { } /** * Constructor that specifies the session manager that should be used for validating sessions. * * @param sessionManager the <tt>SessionManager</tt> that should be used to validate sessions. */ public QuartzSessionValidationScheduler(ValidatingSessionManager sessionManager) { this.sessionManager = sessionManager; } /*-------------------------------------------- | A C C E S S O R S / M O D I F I E R S | ============================================*/ protected Scheduler getScheduler() throws SchedulerException { if (scheduler == null) { scheduler = StdSchedulerFactory.getDefaultScheduler(); schedulerImplicitlyCreated = true; } return scheduler; } public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; } public void setSessionManager(ValidatingSessionManager sessionManager) { this.sessionManager = sessionManager; } public boolean isEnabled() { return this.enabled; } /** * Specifies how frequently (in milliseconds) this Scheduler will call the * {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions() ValidatingSessionManager#validateSessions()} method. * * <p>Unless this method is called, the default value is {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}. * * @param sessionValidationInterval */ public void setSessionValidationInterval(long sessionValidationInterval) { this.sessionValidationInterval = sessionValidationInterval; } /*-------------------------------------------- | M E T H O D S | ============================================*/ /** * Starts session validation by creating a Quartz simple trigger, linking it to * the {@link QuartzSessionValidationJob}, and scheduling it with the Quartz scheduler. */ public void enableSessionValidation() { if (log.isDebugEnabled()) { log.debug("Scheduling session validation job using Quartz with " + "session validation interval of [" + sessionValidationInterval + "]ms..."); } try { SimpleTrigger trigger = new SimpleTrigger(getClass().getName(), Scheduler.DEFAULT_GROUP, SimpleTrigger.REPEAT_INDEFINITELY, sessionValidationInterval); JobDetail detail = new JobDetail(JOB_NAME, Scheduler.DEFAULT_GROUP, QuartzSessionValidationJob.class); detail.getJobDataMap().put(QuartzSessionValidationJob.SESSION_MANAGER_KEY, sessionManager); Scheduler scheduler = getScheduler(); scheduler.scheduleJob(detail, trigger); if (schedulerImplicitlyCreated) { scheduler.start(); if (log.isDebugEnabled()) { log.debug("Successfully started implicitly created Quartz Scheduler instance."); } } this.enabled = true; if (log.isDebugEnabled()) { log.debug("Session validation job successfully scheduled with Quartz."); } } catch (SchedulerException e) { if (log.isErrorEnabled()) { log.error("Error starting the Quartz session validation job. Session validation may not occur.", e); } } } public void disableSessionValidation() { if (log.isDebugEnabled()) { log.debug("Stopping Quartz session validation job..."); } Scheduler scheduler; try { scheduler = getScheduler(); if (scheduler == null) { if (log.isWarnEnabled()) { log.warn("getScheduler() method returned a null Quartz scheduler, which is unexpected. Please " + "check your configuration and/or implementation. Returning quietly since there is no " + "validation job to remove (scheduler does not exist)."); } return; } } catch (SchedulerException e) { if (log.isWarnEnabled()) { log.warn("Unable to acquire Quartz Scheduler. Ignoring and returning (already stopped?)", e); } return; } try { scheduler.unscheduleJob(JOB_NAME, Scheduler.DEFAULT_GROUP); if (log.isDebugEnabled()) { log.debug("Quartz session validation job stopped successfully."); } } catch (SchedulerException e) { if (log.isDebugEnabled()) { log.debug("Could not cleanly remove SessionValidationJob from Quartz scheduler. " + "Ignoring and stopping.", e); } } this.enabled = false; if (schedulerImplicitlyCreated) { try { scheduler.shutdown(); } catch (SchedulerException e) { if (log.isWarnEnabled()) { log.warn("Unable to cleanly shutdown implicitly created Quartz Scheduler instance.", e); } } finally { setScheduler(null); schedulerImplicitlyCreated = false; } } } }
现在自己更改这个实现如下:
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.xxx.shiro.session.mgt.quartz; import org.apache.shiro.session.mgt.DefaultSessionManager; import org.apache.shiro.session.mgt.SessionValidationScheduler; import org.apache.shiro.session.mgt.ValidatingSessionManager; import org.apache.shiro.session.mgt.quartz.QuartzSessionValidationJob; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SimpleScheduleBuilder; import org.quartz.SimpleTrigger; import org.quartz.TriggerBuilder; import org.quartz.TriggerKey; import org.quartz.impl.StdSchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * An implementation of the {@link org.apache.shiro.session.mgt.SessionValidationScheduler SessionValidationScheduler} that uses Quartz to schedule a * job to call {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions()} on * a regular basis. * * @since 0.1 */ public class QuartzSessionValidationScheduler implements SessionValidationScheduler { //TODO - complete JavaDoc /*-------------------------------------------- | C O N S T A N T S | ============================================*/ /** * The default interval at which sessions will be validated (1 hour); * This can be overridden by calling {@link #setSessionValidationInterval(long)} */ public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL; /** * The name assigned to the quartz job. */ private static final String JOB_NAME = "SessionValidationJob"; /*-------------------------------------------- | I N S T A N C E V A R I A B L E S | ============================================*/ private static final Logger log = LoggerFactory.getLogger(QuartzSessionValidationScheduler.class); /** * The configured Quartz scheduler to use to schedule the Quartz job. If no scheduler is * configured, the scheduler will be retrieved by calling {@link StdSchedulerFactory#getDefaultScheduler()} */ private Scheduler scheduler; private boolean schedulerImplicitlyCreated = false; private boolean enabled = false; /** * The session manager used to validate sessions. */ private ValidatingSessionManager sessionManager; /** * The session validation interval in milliseconds. */ private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL; /*-------------------------------------------- | C O N S T R U C T O R S | ============================================*/ /** * Default constructor. */ public QuartzSessionValidationScheduler() { } /** * Constructor that specifies the session manager that should be used for validating sessions. * * @param sessionManager the <tt>SessionManager</tt> that should be used to validate sessions. */ public QuartzSessionValidationScheduler(ValidatingSessionManager sessionManager) { this.sessionManager = sessionManager; } /*-------------------------------------------- | A C C E S S O R S / M O D I F I E R S | ============================================*/ protected Scheduler getScheduler() throws SchedulerException { if (scheduler == null) { scheduler = StdSchedulerFactory.getDefaultScheduler(); schedulerImplicitlyCreated = true; } return scheduler; } public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; } public void setSessionManager(ValidatingSessionManager sessionManager) { this.sessionManager = sessionManager; } public boolean isEnabled() { return this.enabled; } /** * Specifies how frequently (in milliseconds) this Scheduler will call the * {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions() ValidatingSessionManager#validateSessions()} method. * * <p>Unless this method is called, the default value is {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}. * * @param sessionValidationInterval */ public void setSessionValidationInterval(long sessionValidationInterval) { this.sessionValidationInterval = sessionValidationInterval; } /*-------------------------------------------- | M E T H O D S | ============================================*/ /** * Starts session validation by creating a Quartz simple trigger, linking it to * the {@link QuartzSessionValidationJob}, and scheduling it with the Quartz scheduler. */ public void enableSessionValidation() { if (log.isDebugEnabled()) { log.debug("Scheduling session validation job using Quartz with " + "session validation interval of [" + sessionValidationInterval + "]ms..."); } try { /* SimpleTrigger trigger = new SimpleTrigger(getClass().getName(), Scheduler.DEFAULT_GROUP, SimpleTrigger.REPEAT_INDEFINITELY, sessionValidationInterval); JobDetail detail = new JobDetail(JOB_NAME, Scheduler.DEFAULT_GROUP, QuartzSessionValidationJob.class); detail.getJobDataMap().put(QuartzSessionValidationJob.SESSION_MANAGER_KEY, sessionManager);*/ //modify by xxx at 2017-07-25 10:55,shiro对于quartz2的实现更改 SimpleTrigger trigger = TriggerBuilder .newTrigger() .startNow() .withIdentity(JOB_NAME, Scheduler.DEFAULT_GROUP) .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withIntervalInMilliseconds( sessionValidationInterval)).build(); JobDetail detail = JobBuilder .newJob(QuartzSessionValidationJob.class) .withIdentity(JOB_NAME, Scheduler.DEFAULT_GROUP).build(); detail.getJobDataMap().put("sessionManager", this.sessionManager); Scheduler scheduler = getScheduler(); scheduler.scheduleJob(detail, trigger); if (schedulerImplicitlyCreated) { scheduler.start(); if (log.isDebugEnabled()) { log.debug("Successfully started implicitly created Quartz Scheduler instance."); } } this.enabled = true; if (log.isDebugEnabled()) { log.debug("Session validation job successfully scheduled with Quartz."); } } catch (SchedulerException e) { if (log.isErrorEnabled()) { log.error("Error starting the Quartz session validation job. Session validation may not occur.", e); } } } public void disableSessionValidation() { if (log.isDebugEnabled()) { log.debug("Stopping Quartz session validation job..."); } Scheduler scheduler; try { scheduler = getScheduler(); if (scheduler == null) { if (log.isWarnEnabled()) { log.warn("getScheduler() method returned a null Quartz scheduler, which is unexpected. Please " + "check your configuration and/or implementation. Returning quietly since there is no " + "validation job to remove (scheduler does not exist)."); } return; } } catch (SchedulerException e) { if (log.isWarnEnabled()) { log.warn("Unable to acquire Quartz Scheduler. Ignoring and returning (already stopped?)", e); } return; } try { /*scheduler.unscheduleJob(JOB_NAME, Scheduler.DEFAULT_GROUP);*/ //modify by xxx at 2017-07-25 10:55,shiro对于quartz2的实现更改 scheduler.unscheduleJob(new TriggerKey(JOB_NAME, Scheduler.DEFAULT_GROUP)); if (log.isDebugEnabled()) { log.debug("Quartz session validation job stopped successfully."); } } catch (SchedulerException e) { if (log.isDebugEnabled()) { log.debug("Could not cleanly remove SessionValidationJob from Quartz scheduler. " + "Ignoring and stopping.", e); } } this.enabled = false; if (schedulerImplicitlyCreated) { try { scheduler.shutdown(); } catch (SchedulerException e) { if (log.isWarnEnabled()) { log.warn("Unable to cleanly shutdown implicitly created Quartz Scheduler instance.", e); } } finally { setScheduler(null); schedulerImplicitlyCreated = false; } } } }
4.2 更改配置为自己的实现
<!-- 会话验证调度器 --> <bean id="sessionValidationScheduler" class="com.xxx.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> <property name="sessionValidationInterval" value="1800000" /> <property name="sessionManager" ref="sessionManager" /> </bean>
上一篇: 删除你的社交媒体账号