關於shiro刷新最後一次請求時間來控制session超時

1、session的創建在用戶登錄的時候查創建成功,然後可以通過 SessionDao 這個類獲取到關於session的一些信息,比如:session的 session.getLastAccessTime()最後一次刷新時間,session的 超時時間session.getTimeout()(這裡的這個超時時間是靜默多長時間session自動失效),還有sessionId session.getId(),

2、session的失效時間是通過 session.getLastAccessTime()這個去判斷的,也就是通過session的最後一次刷新時間去判斷,而session最後一次操作時間是由shiro框架自動去刷新,調用的是touch()方法,而具體的實現是通過這個AbstractShiroFilter類實現的,而這類實現是繼承自OncePerRequestFilter 這個類,實現了其中的 doFilterInternal 這個方法實現的,而AbstractShiroFilter 類是通過ShiroFilterFactoryBean 中的一個內部類SpringShiroFilter 這個類的構造方法來調用。 而ShiroFilterFactoryBean這個類又是通過DelegatingFilterProxy 代理來調用,這個DelegatingFilterProxy 代理最後註冊到FilterRegistrationBean 這個類中,實現了shiro的功能。

3、解決思路,由於之前項目中已經有了代理類代理咱的shiroFilter 過濾器。代碼如下:

<code>@Bean
\tpublic FilterRegistrationBean delegatingFilterProxy(){
\t FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
\t DelegatingFilterProxy proxy = new DelegatingFilterProxy();
\t proxy.setTargetFilterLifecycle(true);
\t proxy.setTargetBeanName("shiroFilter");//這裡代理了shiroFilter
\t filterRegistrationBean.setFilter(proxy);
\t return filterRegistrationBean;
\t}

@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();//這裡還是原來的類ShiroFilterFactoryBean,後面將會改寫


shiroFilterFactoryBean.setSecurityManager(securityManager);
// 攔截器
Map<string> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/**", "anon");
//filterChainDefinitionMap.put("/system/submitLogin", "anon");
//filterChainDefinitionMap.put("/**", "authc");
//配置退出過濾器,其中的具體代碼Shiro已經替我們實現了

//filterChainDefinitionMap.put("/logout", "logout");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

/*
* 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面
* shiroFilterFactoryBean.setLoginUrl("/login");
* shiroFilterFactoryBean.setSuccessUrl("/index");
* shiroFilterFactoryBean.setUnauthorizedUrl("/403");
*/

//Map<string> filters = new LinkedHashMap<>();
//filters.put("authc" , new CustomAuthenticationFilter());
//shiroFilterFactoryBean.setFilters(filters);
return shiroFilterFactoryBean;

}/<string>/<string>/<code>

4、現在新建兩個類來是實現之前shiro框架中 相當於 ShiroFilterFactoryBean 和 AbstractShiroFilter 這個類的功能,

<code>package com.qzt.config.shiro.filter;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.config.Ini;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.util.Nameable;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.filter.authc.AuthenticationFilter;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class ExtendShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {

private static transient final Logger log = LoggerFactory.getLogger(ShiroFilterFactoryBean.class);

private SecurityManager securityManager;

private Map<string> filters;

private Map<string> filterChainDefinitionMap; //urlPathExpression_to_comma-delimited-filter-chain-definition

private String loginUrl;
private String successUrl;
private String unauthorizedUrl;

private ExtendAbstractShiroFilter instance;

public ExtendShiroFilterFactoryBean() {
this.filters = new LinkedHashMap<string>();
this.filterChainDefinitionMap = new LinkedHashMap<string>(); //order matters!
}

public SecurityManager getSecurityManager() {
return securityManager;
}

public void setSecurityManager(SecurityManager securityManager) {
this.securityManager = securityManager;
}

public String getLoginUrl() {
return loginUrl;
}

public void setLoginUrl(String loginUrl) {
this.loginUrl = loginUrl;
}

public String getSuccessUrl() {
return successUrl;
}

public void setSuccessUrl(String successUrl) {
this.successUrl = successUrl;
}

public String getUnauthorizedUrl() {
return unauthorizedUrl;

}

public void setUnauthorizedUrl(String unauthorizedUrl) {
this.unauthorizedUrl = unauthorizedUrl;
}

public Map<string> getFilters() {
return filters;
}

public void setFilters(Map<string> filters) {
this.filters = filters;
}
public Map<string> getFilterChainDefinitionMap() {
return filterChainDefinitionMap;
}

public void setFilterChainDefinitionMap(Map<string> filterChainDefinitionMap) {
this.filterChainDefinitionMap = filterChainDefinitionMap;
}

public void setFilterChainDefinitions(String definitions) {
Ini ini = new Ini();
ini.load(definitions);
Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
if (CollectionUtils.isEmpty(section)) {
section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
setFilterChainDefinitionMap(section);
}

public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}

public Class getObjectType() {
return SpringShiroFilter.class;
}
public boolean isSingleton() {
return true;
}

protected FilterChainManager createFilterChainManager() {

DefaultFilterChainManager manager = new DefaultFilterChainManager();
Map<string> defaultFilters = manager.getFilters();
for (Filter filter : defaultFilters.values()) {

applyGlobalPropertiesIfNecessary(filter);
}

Map<string> filters = getFilters();
if (!CollectionUtils.isEmpty(filters)) {
for (Map.Entry<string> entry : filters.entrySet()) {
String name = entry.getKey();
Filter filter = entry.getValue();
applyGlobalPropertiesIfNecessary(filter);
if (filter instanceof Nameable) {
((Nameable) filter).setName(name);
}
manager.addFilter(name, filter, false);
}
}

//build up the chains:
Map<string> chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry<string> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
manager.createChain(url, chainDefinition);
}
}

return manager;
}
protected ExtendAbstractShiroFilter createInstance() throws Exception {

log.debug("Creating Shiro Filter instance.");

SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}

if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}

FilterChainManager manager = createFilterChainManager();

PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);

return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}


private void applyLoginUrlIfNecessary(Filter filter) {
String loginUrl = getLoginUrl();
if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) {
AccessControlFilter acFilter = (AccessControlFilter) filter;
//only apply the login url if they haven't explicitly configured one already:
String existingLoginUrl = acFilter.getLoginUrl();
if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) {
acFilter.setLoginUrl(loginUrl);
}
}
}

private void applySuccessUrlIfNecessary(Filter filter) {
String successUrl = getSuccessUrl();
if (StringUtils.hasText(successUrl) && (filter instanceof AuthenticationFilter)) {
AuthenticationFilter authcFilter = (AuthenticationFilter) filter;
//only apply the successUrl if they haven't explicitly configured one already:
String existingSuccessUrl = authcFilter.getSuccessUrl();
if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl)) {
authcFilter.setSuccessUrl(successUrl);
}
}
}

private void applyUnauthorizedUrlIfNecessary(Filter filter) {
String unauthorizedUrl = getUnauthorizedUrl();
if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter)) {
AuthorizationFilter authzFilter = (AuthorizationFilter) filter;
//only apply the unauthorizedUrl if they haven't explicitly configured one already:
String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl();
if (existingUnauthorizedUrl == null) {
authzFilter.setUnauthorizedUrl(unauthorizedUrl);
}
}
}

private void applyGlobalPropertiesIfNecessary(Filter filter) {
applyLoginUrlIfNecessary(filter);
applySuccessUrlIfNecessary(filter);
applyUnauthorizedUrlIfNecessary(filter);
}

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Filter) {
log.debug("Found filter chain candidate filter '{}'", beanName);
Filter filter = (Filter) bean;
applyGlobalPropertiesIfNecessary(filter);
getFilters().put(beanName, filter);
} else {

log.trace("Ignoring non-Filter bean '{}'", beanName);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

private static final class SpringShiroFilter extends ExtendAbstractShiroFilter {

protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
super();
if (webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
}
setSecurityManager(webSecurityManager);
if (resolver != null) {
setFilterChainResolver(resolver);
}
}
}
}/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<code>
<code>package com.qzt.config.shiro.filter;

import java.io.IOException;
import java.util.concurrent.Callable;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.ExecutionException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.web.servlet.OncePerRequestFilter;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.servlet.ShiroHttpServletResponse;
import org.apache.shiro.web.subject.WebSubject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExtendAbstractShiroFilter extends OncePerRequestFilter {

private static final Logger log = LoggerFactory.getLogger(AbstractShiroFilter.class);

private static final String STATIC_INIT_PARAM_NAME = "staticSecurityManagerEnabled";

private WebSecurityManager securityManager;

private FilterChainResolver filterChainResolver;
private boolean staticSecurityManagerEnabled;

protected ExtendAbstractShiroFilter() {
this.staticSecurityManagerEnabled = false;
}

public WebSecurityManager getSecurityManager() {
return securityManager;
}

public void setSecurityManager(WebSecurityManager sm) {
this.securityManager = sm;
}

public FilterChainResolver getFilterChainResolver() {
return filterChainResolver;
}

public void setFilterChainResolver(FilterChainResolver filterChainResolver) {
this.filterChainResolver = filterChainResolver;
}


public boolean isStaticSecurityManagerEnabled() {
return staticSecurityManagerEnabled;
}


public void setStaticSecurityManagerEnabled(boolean staticSecurityManagerEnabled) {
this.staticSecurityManagerEnabled = staticSecurityManagerEnabled;
}

protected final void onFilterConfigSet() throws Exception {
applyStaticSecurityManagerEnabledConfig();
init();
ensureSecurityManager();
if (isStaticSecurityManagerEnabled()) {
SecurityUtils.setSecurityManager(getSecurityManager());
}
}


private void applyStaticSecurityManagerEnabledConfig() {
String value = getInitParam(STATIC_INIT_PARAM_NAME);
if (value != null) {
Boolean b = Boolean.valueOf(value);
if (b != null) {
setStaticSecurityManagerEnabled(b);
}
}
}

public void init() throws Exception {
}


private void ensureSecurityManager() {
WebSecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
log.info("No SecurityManager configured. Creating default.");
securityManager = createDefaultSecurityManager();
setSecurityManager(securityManager);
}
}

protected WebSecurityManager createDefaultSecurityManager() {
return new DefaultWebSecurityManager();
}

protected boolean isHttpSessions() {
return getSecurityManager().isHttpSessionMode();
}


protected ServletRequest wrapServletRequest(HttpServletRequest orig) {
return new ShiroHttpServletRequest(orig, getServletContext(), isHttpSessions());
}


@SuppressWarnings({"UnusedDeclaration"})
protected ServletRequest prepareServletRequest(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletRequest toUse = request;
if (request instanceof HttpServletRequest) {
HttpServletRequest http = (HttpServletRequest) request;
toUse = wrapServletRequest(http);
}
return toUse;
}


protected ServletResponse wrapServletResponse(HttpServletResponse orig, ShiroHttpServletRequest request) {

return new ShiroHttpServletResponse(orig, getServletContext(), request);
}


@SuppressWarnings({"UnusedDeclaration"})
protected ServletResponse prepareServletResponse(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletResponse toUse = response;
if (!isHttpSessions() && (request instanceof ShiroHttpServletRequest) &&
(response instanceof HttpServletResponse)) {
//the ShiroHttpServletResponse exists to support URL rewriting for session ids. This is only needed if
//using Shiro sessions (i.e. not simple HttpSession based sessions):
toUse = wrapServletResponse((HttpServletResponse) response, (ShiroHttpServletRequest) request);
}
return toUse;
}


protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
}


@SuppressWarnings({"UnusedDeclaration"})
protected void updateSessionLastAccessTime(ServletRequest request, ServletResponse response) {
\t HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// \tlog.info("-------------------------------------uri------"+req.getRequestURI());
\tString requestURI = req.getRequestURI();
\ttry {
\t\t\t
\tif (requestURI.startsWith("/uri/uri")) {// /uri/uri 這個就是前端定時請求的接口
\t\t\t//當是這個接口的時候就不進行對session的最後操作時間進行刷新了
\t\t}else{
//if (!isHttpSessions()) { //'native' sessions,這裡我去掉了,因為我的session是httpssesion
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
Session session = subject.getSession(false);
if (session != null) {
try {
//刷新session最後訪問時間為當前時間
session.touch();
//同步把cookie的超時時間也刷新一下
Cookie cookie2 = new Cookie("token", session.getId().toString());

cookie2.setMaxAge(1800);
cookie2.setHttpOnly(false);
cookie2.setPath("/");
res.addCookie(cookie2);
System.err.println("超時時間========"+session.getTimeout());
System.err.println("最後訪問時間====="+session.getLastAccessTime());
} catch (Throwable t) {
log.error("session.touch() method invocation has failed. Unable to update" +
"the corresponding session's last access time based on the incoming request.", t);
}
}
}
//}
\t\t}
\t} catch (Exception e) {
\t\t\tlog.error("error", e);
\t\t}
}


@SuppressWarnings({ "unchecked", "rawtypes" })
\tprotected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
throws ServletException, IOException {

Throwable t = null;

try {
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

final Subject subject = createSubject(request, response);

subject.execute(new Callable() {
public Object call() throws Exception {
updateSessionLastAccessTime(request, response);
executeChain(request, response, chain);
return null;
}
});
} catch (ExecutionException ex) {
t = ex.getCause();
} catch (Throwable throwable) {
t = throwable;
}

if (t != null) {
if (t instanceof ServletException) {
throw (ServletException) t;
}

if (t instanceof IOException) {
throw (IOException) t;
}
//otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one:
String msg = "Filtered request failed.";
throw new ServletException(msg, t);
}
}


protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
FilterChain chain = origChain;

FilterChainResolver resolver = getFilterChainResolver();
if (resolver == null) {
log.debug("No FilterChainResolver configured. Returning original FilterChain.");
return origChain;
}

FilterChain resolved = resolver.getChain(request, response, origChain);
if (resolved != null) {
log.trace("Resolved a configured FilterChain for the current request.");
chain = resolved;
} else {
log.trace("No FilterChain configured for the current request. Using the default.");
}

return chain;
}


protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
throws IOException, ServletException {
FilterChain chain = getExecutionChain(request, response, origChain);
chain.doFilter(request, response);
}
}/<code>

5、上面的步驟完事以後需要將原來的shiroFilter進行改寫,讓使用我自己重新寫的類,如下:

<code>@Bean
\tpublic FilterRegistrationBean delegatingFilterProxy(){
\t FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
\t DelegatingFilterProxy proxy = new DelegatingFilterProxy();
\t proxy.setTargetFilterLifecycle(true);
\t proxy.setTargetBeanName("shiroFilter");
\t filterRegistrationBean.setFilter(proxy);

\t return filterRegistrationBean;
\t}

@Bean("shiroFilter")
public ExtendShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
// ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
\t//使用我們上邊自定義的類
ExtendShiroFilterFactoryBean shiroFilterFactoryBean= new ExtendShiroFilterFactoryBean();

shiroFilterFactoryBean.setSecurityManager(securityManager);
//設置一下 攔截器,把定時任務和登錄接口一類的過濾掉,防止影響超時時間的刷新
Map<string> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/net/tips/getMsg", "anon");//是定時任務接口
filterChainDefinitionMap.put("/net/tips/**", "authc");//其他接口依舊攔截
filterChainDefinitionMap.put("/net/collect/getACData", "anon");//過濾定時任務接口
filterChainDefinitionMap.put("/net/collect/**", "authc");//其他接口依舊攔截
filterChainDefinitionMap.put("/net/task/**", "authc");

filterChainDefinitionMap.put("/net/user/login", "anon");//過濾登錄接口
filterChainDefinitionMap.put("/net/user/**", "authc");//攔截除登錄接口之外的其他接口
//filterChainDefinitionMap.put("/**", "anon");
//filterChainDefinitionMap.put("/system/submitLogin", "anon");
//filterChainDefinitionMap.put("/**", "authc");
//配置退出過濾器,其中的具體代碼Shiro已經替我們實現了
//filterChainDefinitionMap.put("/logout", "logout");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

/*
* 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面
* shiroFilterFactoryBean.setLoginUrl("/login");
* shiroFilterFactoryBean.setSuccessUrl("/index");
* shiroFilterFactoryBean.setUnauthorizedUrl("/403");
*/

//Map<string> filters = new LinkedHashMap<>();
//filters.put("authc" , new CustomAuthenticationFilter());

//shiroFilterFactoryBean.setFilters(filters);
return shiroFilterFactoryBean;

}/<string>/<string>/<code>

6、我的項目中要修改的時候下面這個類中的超時時間要註釋掉

<code>public class TokenManager {

\t@Autowired
\tprivate static SampleRealm realm;

\t/**
\t * 獲取當前登錄的用戶User對象
\t * @return
\t */
\tpublic static User getToken(){
\t\tUser token = (User) SecurityUtils.getSubject().getPrincipal();
SecurityUtils.getSubject().getSession().setTimeout(300000);//我測試的不需要註釋,這句設置超時時間,但有情況是要註釋掉,不然影響超時其他地方超時時間的設置;看個人情況吧
\t\treturn token ;
\t}/<code>

7、看個人情況:可以忽略;超時時間要修改sessionManager.setGlobalSessionTimeout(1200000);這個位置的,配置文件中的不管用

8、大功告成


注:自己做完的每個都要進行測試,完整測試,不然其實內部還存在好多的問題的

注意點:

1.session超時時間的設置;

2.cookie超時時間的同步刷新;

3.定時任務的過濾:可以在shiro級別過濾,也可以在過濾器中過濾(不刷新超時時間)


關於shiro刷新最後一次請求時間來控制session超時

shiro級別過濾


關於shiro刷新最後一次請求時間來控制session超時

過濾器中過濾


要實現某一個類的功能,不是要繼承這個類來實現,而是通過繼承這個類所繼承的類或者實現這個類所實現的接口來實現。


分享到:


相關文章: