Shiro的authc过滤器的执行流程

Shiro的authc过滤器的执行流程

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 {
	@Override
    protected 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:


		
		
		
		
		
			
				
			
		
		
			
				# some example chain definitions:
				/login = authc
				/logout = logout
				/1.jsp = user
				/** = authc
				# more URL-to-FilterChain definitions here
			
		
	

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默認關閉瀏覽器就失效了。

你可能感兴趣的:(Shiro)