//這里是攔截器攔截HTTP請求的入口public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {FilterInvocation fi = new FilterInvocation(request, response, chain);invoke(fi);}//這是具體的攔截調用public void invoke(FilterInvocation fi) throws IOException, ServletException {if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)&& observeOncePerRequest) {//在第一次進(jìn)行過(guò)安全檢查之后就不會(huì )再做了fi.getChain().doFilter(fi.getRequest(), fi.getResponse());} else {//這是第一次收到相應的請求,需要做安全檢測,同時(shí)把標志為設置好 - FILTER_APPLIED,下次就再有請求就不會(huì )作相同的安全檢查了if (fi.getRequest() != null) {fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);}//這里是做安全檢查的地方InterceptorStatusToken token = super.beforeInvocation(fi);//接著(zhù)向攔截器鏈執行try {fi.getChain().doFilter(fi.getRequest(), fi.getResponse());} finally {super.afterInvocation(token, null);}}}
我們看看在A(yíng)bstractSecurityInterceptor是怎樣對HTTP請求作安全檢測的:
protected InterceptorStatusToken beforeInvocation(Object object) {Assert.notNull(object, "Object was null");if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {throw new IllegalArgumentException("Security invocation attempted for object "+ object.getClass().getName()+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "+ getSecureObjectClass());}//這里讀取配置FilterSecurityInterceptor的ObjectDefinitionSource屬性,這些屬性配置了資源的安全設置ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);if (attr == null) {if(rejectPublicInvocations) {throw new IllegalArgumentException("No public invocations are allowed via this AbstractSecurityInterceptor. "+ "This indicates a configuration error because the "+ "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");}if (logger.isDebugEnabled()) {logger.debug("Public object - authentication not attempted");}publishEvent(new PublicInvocationEvent(object));return null; // no further work post-invocation}if (logger.isDebugEnabled()) {logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString());}//這里從SecurityContextHolder中去取Authentication對象,一般在登錄時(shí)會(huì )放到SecurityContextHolder中去if (SecurityContextHolder.getContext().getAuthentication() == null) {credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound","An Authentication object was not found in the SecurityContext"), object, attr);}// 如果前面沒(méi)有處理鑒權,這里需要對鑒權進(jìn)行處理Authentication authenticated;if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) {try {//調用配置好的AuthenticationManager處理鑒權,如果鑒權不成功,拋出異常結束處理authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext().getAuthentication());} catch (AuthenticationException authenticationException) {throw authenticationException;}// We don't authenticated.setAuthentication(true), because each provider should do thatif (logger.isDebugEnabled()) {logger.debug("Successfully Authenticated: " + authenticated.toString());}//這里把鑒權成功后得到的Authentication保存到SecurityContextHolder中供下次使用SecurityContextHolder.getContext().setAuthentication(authenticated);} else {//這里處理前面已經(jīng)通過(guò)鑒權的請求,先從SecurityContextHolder中去取得Authenticationauthenticated = SecurityContextHolder.getContext().getAuthentication();if (logger.isDebugEnabled()) {logger.debug("Previously Authenticated: " + authenticated.toString());}}// 這是處理授權的過(guò)程try {//調用配置好的AccessDecisionManager來(lái)進(jìn)行授權this.accessDecisionManager.decide(authenticated, object, attr);} catch (AccessDeniedException accessDeniedException) {//授權不成功向外發(fā)布事件AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,accessDeniedException);publishEvent(event);throw accessDeniedException;}if (logger.isDebugEnabled()) {logger.debug("Authorization successful");}AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);publishEvent(event);// 這里構建一個(gè)RunAsManager來(lái)替代當前的Authentication對象,默認情況下使用的是NullRunAsManager會(huì )把SecurityContextHolder中的Authentication對象清空Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);if (runAs == null) {if (logger.isDebugEnabled()) {logger.debug("RunAsManager did not change Authentication object");}// no further work post-invocationreturn new InterceptorStatusToken(authenticated, false, attr, object);} else {if (logger.isDebugEnabled()) {logger.debug("Switching to RunAs Authentication: " + runAs.toString());}SecurityContextHolder.getContext().setAuthentication(runAs);// revert to token.Authenticated post-invocationreturn new InterceptorStatusToken(authenticated, true, attr, object);}}
到這里我們假設配置AffirmativeBased作為AccessDecisionManager:
//這里定義了決策機制,需要全票才能通過(guò)public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)throws AccessDeniedException {//這里取得配置好的迭代器集合Iterator iter = this.getDecisionVoters().iterator();int deny = 0;//依次使用各個(gè)投票器進(jìn)行投票,并對投票結果進(jìn)行計票while (iter.hasNext()) {AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();int result = voter.vote(authentication, object, config);//這是對投票結果進(jìn)行處理,如果遇到其中一票通過(guò),那就授權通過(guò),如果是棄權或者反對,那就繼續投票switch (result) {case AccessDecisionVoter.ACCESS_GRANTED:return;case AccessDecisionVoter.ACCESS_DENIED://這里對反對票進(jìn)行計數deny++;break;default:break;}}//如果有反對票,拋出異常,整個(gè)授權不通過(guò)if (deny > 0) {throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied","Access is denied"));}// 這里對棄權票進(jìn)行處理,看看是全是棄權票的決定情況,默認是不通過(guò),由allowIfAllAbstainDecisions變量控制checkAllowIfAllAbstainDecisions();}具體的投票由投票器進(jìn)行,我們這里配置了RoleVoter來(lái)進(jìn)行投票:public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {int result = ACCESS_ABSTAIN;//這里取得資源的安全配置Iterator iter = config.getConfigAttributes();while (iter.hasNext()) {ConfigAttribute attribute = (ConfigAttribute) iter.next();if (this.supports(attribute)) {result = ACCESS_DENIED;// 這里對資源配置的安全授權級別進(jìn)行判斷,也就是匹配ROLE為前綴的角色配置// 遍歷每個(gè)配置屬性,如果其中一個(gè)匹配該主體持有的GrantedAuthority,則訪(fǎng)問(wèn)被允許。for (int i = 0; i < authentication.getAuthorities().length; i++) {if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) {return ACCESS_GRANTED;}}}}return result;}
上面就是對整個(gè)授權過(guò)程的一個(gè)分析,從FilterSecurityInterceptor攔截Http請求入手,然后讀取對資源的安全配置以后,把這些信息交由AccessDecisionManager來(lái)進(jìn)行決策,Spring為我們提供了若干決策器來(lái)使用,在決策器中我們可以配置投票器來(lái)完成投票,我們在上面具體分析了角色投票器的使用過(guò)程。
聯(lián)系客服