1.先执行isAccessAllowed(),通过subject.isAuthenticated()判断当前session中的subject是否已经登陆过。如果在当前session即会话中已经登陆过,返回true,authc过滤器放行请求到loginUrl。
问题?
这里会有一个问题,如果我登陆成功后,再次访问loginUrl,会执行isAccessAllowed()并返回true放行,那么我访问到了loginUrl,如果此时我在loginUrl中输入新的用户名密码,再提交则先执行isAccessAllowed(),因为当前session中的subject是已经登陆过的,isAccessAllowed()返回true,autch放行,请求又会到loginUrl。因为在登陆的情况下,再访问loginUrl则isAccessAllowed()始终返回true,不会执行到onAccessDenied()方法,即不会验证请求中的新的用户名和密码。
2.如果isAccessAllowed()是false即拒绝访问后执行onAccessDenied()方法:
2.1判断是否是登陆请请求(即request中的url和我们设置的loginUrl是否一致)
2.1.1如果请求的url和我们在配置文件中设置的loginUrl一样
2.1.1.1如果是get请求,则返回true,即该过滤器连放行,让请求访问到loginUrl
2.1.1.2如果是post请求,则创建subjet和tocken 调用subject的login方法进行认证(即开始执行realm的doGetAuthenticationInfo方法)
2.1.1.2.1如果认证成功则会返回false阻止filterChain继续执行即不让请求达到loginUrl,而是在这里直接重定向我们上次访问的非logurl的请求地址去(这个请求地址在你访问的时候已经被保存到session中了)如果没有上次访问的地址,则到我们设置的SuccessUrl
2.1.1…2.2如果认证失败则会返回true,将认证失败的异常信息放到reqeust属性中,即放行让fliterChain继续执行,让请求达到loginUrl。
2.2如果否即request中的url不是loginUrl,则保存请求(应该是到session里),再将请求重定向到loginUrl(浏览器又会访问通过get方法访问loginUrl,又会被authc拦截,再重複上面流程,此时是登陆请求且是GET请求则authc会放行访问到loginUrl),执行完重定向后返回false阻止filterChain继续执行。
综上所述:
只有以下四种情况会到loginUrl:
//1.还没有登陆成功的情况下:get请求我们在配置文件中设置的loginUrl,authc会放行请求到这里
//2.还没有登陆成功的情况下:post请求我们在配置文件中设置的loginUrl,authc在认证失败后会让浏览器重定向到这里
//3.还没有登陆成功的情况下:请求(不管post还是get)的页面不是loginUrl且需要authc过滤,那么authc也会让浏览器重定向到这里
//4.登陆成功后,浏览器再次访问loginUrl(不管post或get),authc也会放行到这里。
怎么解决上面登陆成功后再访问loginUrl,不会执行新用户认证的问题呢?只有重写authc过滤器的isAccessAllowed()方法
public class MyAuthcFilter extends FormAuthenticationFilter {@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue){System.out.println("执行自定义过滤");//如果请求的是loginUrl 并且是POST请求,那么肯定是要验证密码的,这里直接返回false 就会执行onAcessDenied()方法if (isLoginRequest(request, response) && isLoginSubmission(request, response)){return false;}//如果是其他请求 则执行父类的方法return super.isAccessAllowed(request, response, mappedValue);}
}
最后再配置文件中注册该类,覆盖掉shiro原本的过滤器FormAuthenticationFilter:
<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.jsp" /><property name="unauthorizedUrl" value="/unauthorized.jsp" /><property name="filters"><map><entry key="authc" value-ref="myAuthcFilter" /></map></property><property name="filterChainDefinitions"><value># some example chain definitions:/login = authc/logout = logout/1.jsp = user/** = authc# more URL-to-FilterChain definitions here</value></property></bean>
login的写法:
@Controller
public class UsersController {@RequestMapping("login")public String login(HttpServletRequest req) {//只有//1.还没有登陆成功的情况下:get请求我们在配置文件中设置的loginUrl,authc会放行请求到这里//2.还没有登陆成功的情况下:post请求我们在配置文件中设置的loginUrl,authc在认证失败后会让浏览器重定向到这里//3.还没有登陆成功的情况下:请求(不管post还是get)的页面不是loginUrl且需要authc过滤,那么authc也会让浏览器重定向到这里//4.登陆成功后,浏览器再次访问loginUrl(不管post或get),authc也会放行到这里。如果我们在自定义的过滤器中//设置成如果是POST请求的loginUrl,我们返回false。那么post请求的loginUrl就会执行在过滤器中执行onAcessDenied()方法,如果post的loginUrl验证失败了会到这里,如果验证成功则到secessUrl这里。String errorClassName = (String) req.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);if(UnknownAccountException.class.getName().equals(errorClassName)) {req.setAttribute("error", "用户名/密码错误");} else if(IncorrectCredentialsException.class.getName().equals(errorClassName)) {req.setAttribute("error", "用户名/密码错误");} else if(errorClassName != null) {req.setAttribute("error", "未知错误:" + errorClassName);}return "login.jsp";}
}
session和cookie:
通过浏览器第一次发送http请求时,服务器会创建一个session和cookie(cookie中保存sessionid),这个session服务器的默认时间30分钟,可以设置。服务第一次响应时也把cookie放到响应中给浏览器,浏览器
收到cookie后,会判断cookie有没有设置过期时间,如果没有则只保存在内存中。如果有则存到硬盘里。
如果只是保存到内存中,服务器端的session依然会保留30分钟。那么浏览器一关闭,cookie消失,sessionid也消失了。。但此时打开浏览器再访问服务器时,虽然服务器的session依然存在,但浏览器中的cookie(sessionid)消失了
浏览器发送的请求头里没有cookie(没有sessionid),服务器会当作一个新的请求,会再创建一个新session。服务器中的之前的session 30分钟侯清除。
如果保存到硬盘里了, 服务器端的session依然会保留30分钟。那么关闭浏览器后,再打开浏览器访问服务器时,浏览器会找到存到硬盘里的cookie(sessionId), 浏览器再次请求服务器时会带上cookie,服务器收到请求后发现有cookie(sessionId)并且服务器
端的session也存在。那么服务器就依然会使用之前的session。不会再创建一个新的session。
所以session默认关闭浏览器就失效了。