shiro将ServletHttpSession包装成了ShiroHttpSession
最近在做Oauth SSO单点登录集成,目标有三个:
A:各个业务系统在统一门户登录,各业务系统不用再次登录,即SSO;
B:原来各业务系统的登录入口暂时保留;
C:解决同一个浏览器登录多个账户,session覆盖的问题。
因为业务系统使用的是SpringMVC,并且使用了shiro安全框架。我的实现逻辑是,先执行OauthFilter,后执行Shiro认证。
但是在shiro中session设置的属性,在OauthFilter中始终无法获取,于是决定打印一下日志。
在shiro中:
HttpSession session = req.getSession();
System.out.println("shiroFilter-session:"+session);
session.setAttribute("CURRENT_USER"+accessToken, token.getUsername());
在OauthFilter中:
HttpSession session = request.getSession();
System.out.println("OauthFilter-session:"+session);
输出日志:
shiroFilter-session:[email protected]
OauthFilter-session:[email protected]
发现,这两个session不是同一个Session,shiro将ServletHttpSession转换成了ShiroHttpSession。
跟踪一下源码:AbstractShiroFilter
protected 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);
//noinspection unchecked
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);
}
}
@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 ServletRequest wrapServletRequest(HttpServletRequest orig) {
return new ShiroHttpServletRequest(orig, getServletContext(), isHttpSessions());
}
protected boolean isHttpSessions() {
return getSecurityManager().isHttpSessionMode();
}
现在终于知道shiro是怎么把request对象包装成ShiroHttpServletRequest类型的了.
一开始session的困惑也能解开了:
因为session是通过request获取的,所以先看一下ShiroHttpServletRequest获取session的源码:
public HttpSession getSession(boolean create) {
HttpSession httpSession;
if (isHttpSessions()) {
httpSession = super.getSession(false);
if (httpSession == null && create) {
//Shiro 1.2: assert that creation is enabled (SHIRO-266):
if (WebUtils._isSessionCreationEnabled(this)) {
httpSession = super.getSession(create);
} else {
throw newNoSessionCreationException();
}
}
} else {
if (this.session == null) {
boolean existing = getSubject().getSession(false) != null;
Session shiroSession = getSubject().getSession(create);
if (shiroSession != null) {
this.session = new ShiroHttpSession(shiroSession, this, this.servletContext);
if (!existing) {
setAttribute(REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
}
}
}
httpSession = this.session;
}
return httpSession;
}
如果this.isHttpSessions()返回true,则返回父类HttpServletRequestWrapper的
也就是servelet规范的session,否则返回ShiroHttpSession对象.
那么this.isHttpSessions()是什么呢?
这里的this.isHttpSessions()取决于this.getSecurityManager().isHttpSessionMode()的值.
我们项目里SecurityManager设置为DefaultWebSecurityManager.其中isHttpSessionMode()方法为:
我们这个项目使用的sessionManager是DefaultWebSessionManager,DefaultWebSessionManager实现了sessionManager 接口.
但是DefaultWebSessionManager中该方法返回的是false.
public boolean isServletContainerSessions() {
return false;
}
所以最终session得到的是ShiroHttpSession.
如果不想让Session转换为ShiroHttpSession,我们可以自己实现sessionManager
<!-- sessionManager -->
<!-- <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> -->
<bean id="sessionManager" class="com.unis.module.auth.shiro.MyWebSessionManager">
<property name="globalSessionTimeout" value="7200000" />
<property name="sessionDAO" ref="redisSessionDAO" />
<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionIdCookie" ref="wapsession"/>
</bean>
public class MyWebSessionManager extends DefaultWebSessionManager{
@Override
public boolean isServletContainerSessions() {
return true;
}
}
下一篇: typescript类型断言