运行基于form表单的acegitest2demo,你是否发现有什么不妥?
测试不妥处如下:
1.在IE中运行http://localhost:8080/acegitest2/userinfo.jsp
2.肯定会转到login页面,然后登陆进去test/1,进入accessdefined无权限页面。使用lisi/1,进入当前用户信息。
但是:
假如你用test/1登陆进去的,当再次运行http://localhost:8080/acegitest2/userinfo.jsp,你猜猜会出现什么现象呢?
哈哈,进入accessdefined无权限页面。
我再打开IE浏览器,再运行这个url,还是进入无权限页面。
是缓存?那我明明是新打开的浏览器啊,而不是打开新标签啊?
无论你打开几层IE浏览器,你F12看看,会发现,咦,sessionId是相同的。
这说明什么问题?说明你用的IE浏览器是session共享的,这是浏览器为用户方便而专门为用户设置的。IE8默认情况下,session是共享的。
那如何新建一个session不同的页面呢?
在IE中,文件—新建session会话即可。
修改浏览器的快捷方式属性,在 C:\Program Files\Internet Explorer 文件夹下,通过iexplore.exe 新建一快捷方式至桌面,在桌面的快捷方式上点击右键选择属性,修改“目标”为 "C:\Program Files\Internet Explorer\iexplore.exe" -nomerge 通过此快捷方式启动的浏览器不会共享会话
按照以上方式,更改了,再运行url,还是进入无权限页面,发现跟session没关系【后面会讲跟session有关系的博文】,你使用360、谷歌测试,都是相同页面。
进入debug调试……【当初debug时,漏掉一个方法,导致误以为是session的问题】
因为咱们配置acegi的filter是:authenticationProcessingFilter,exceptionTranslationFilter,
,filterInvocationInterceptor这三个,其中第一个是身份认证的,debug首先进入第一个filter,但是实际上是进入AbstractProcessingFilter,而authenticationProcessingFilter是extends AbstractProcessingFilter,其中第一个filter的方法:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException( "Can only process HttpServletRequest");
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException( "Can only process HttpServletResponse");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (requiresAuthentication(httpRequest, httpResponse)) {
if ( logger.isDebugEnabled()) {
logger.debug( "Request is to process authentication");
}
Authentication authResult;
try {
onPreAuthentication(httpRequest, httpResponse);
authResult = attemptAuthentication(httpRequest);
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(httpRequest, httpResponse, failed);
return;
}
// Authentication success
if ( continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(httpRequest, httpResponse, authResult);
return;
}
chain.doFilter(request, response);
}
其中的分支点在于:requiresAuthentication(httpRequest, httpResponse)这个方法;根据名称猜测到是否需要验证的意思。当第一次运行时,这个为true,然后进入其中身份认证。但是第二次执行时,就false,直接跳到下一个filter。
exceptionfilter这个就是捕捉错误的,不多谈了。
然后再进入授权filter-filterInvocationInterceptor,获取SecurityContextHolder.getContext().getAuthentication(),其中securitycontex中存在权限对象,然后直接进入decide投票方法。
那么关键问题在于,为啥securitycontext中保留上次的权限对象?
在第一个fiter认证后,securitycontext把已经通过认证对象保存进去successfulAuthentication,
SecurityContextHolder.getContext().setAuthentication(authResult);
这样在filterInvocationInterceptor中,关键代码如下:
if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) {
try {
authenticated = this. authenticationManager.authenticate(SecurityContextHolder. getContext()
.getAuthentication());
}
catch (AuthenticationException authenticationException) {
throw authenticationException;
}
// We don't authenticated.setAuthentication(true), because each
// provider should do that
if ( logger.isDebugEnabled()) {
logger.debug( "Successfully Authenticated: " + authenticated.toString());
}
SecurityContextHolder. getContext().setAuthentication(authenticated);
}
else {
authenticated = SecurityContextHolder.getContext().getAuthentication();
if ( logger.isDebugEnabled()) {
logger.debug( "Previously Authenticated: " + authenticated.toString());
}
}
直接获取其中的权限对象,进行投票匹配。
这篇博客主要分析了form认证的关键代码,那下篇博客我们看看若添加了sessionfilter,会有什么变化呢?