Spring Acegi框架授权的实现

来源:互联网 发布:华侨出版社知乎 编辑:程序博客网 时间:2024/05/22 12:56

原文网址:http://www.bianceng.cn/Programming/Java/201103/25348.htm

我们从FilterSecurityInterceptor我们从入手看看怎样进行授权的:

Java代码

 

//这里是拦截器拦截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) {      //在第一次进行过安全检查之后就不会再做了       fi.getChain().doFilter(fi.getRequest(), fi.getResponse());     } else {       //这是第一次收到相应的请求,需要做安全检测,同时把标志为设置好 - FILTER_APPLIED,下次就再有请求就不会作相同的安全检查了       if (fi.getRequest() != null) {         fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);       }       //这里是做安全检查的地方       InterceptorStatusToken token = super.beforeInvocation(fi);       //接着向拦截器链执行       try {         fi.getChain().doFilter(fi.getRequest(), fi.getResponse ());       } finally {         super.afterInvocation(token, null);       }     }   }


 

我们看看在AbstractSecurityInterceptor是怎样对HTTP请求作安全检测的:

Java代码

 

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对象,一般在登录时 会放到SecurityContextHolder中去     if (SecurityContextHolder.getContext().getAuthentication() == null) {       credentialsNotFound(messages.getMessage ("AbstractSecurityInterceptor.authenticationNotFound",           "An Authentication object was not found in the SecurityContext"), object, attr);     }     // 如果前面没有处理鉴权,这里需要对鉴权进行处理     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 that       if (logger.isDebugEnabled()) {         logger.debug("Successfully Authenticated: " + authenticated.toString());       }       //这里把鉴权成功后得到的Authentication保存到 SecurityContextHolder中供下次使用       SecurityContextHolder.getContext().setAuthentication (authenticated);     } else {//这里处理前面已经通过鉴权的请求,先从SecurityContextHolder 中去取得Authentication       authenticated = SecurityContextHolder.getContext ().getAuthentication();       if (logger.isDebugEnabled()) {         logger.debug("Previously Authenticated: " + authenticated.toString());       }     }     // 这是处理授权的过程     try {       //调用配置好的AccessDecisionManager来进行授权       this.accessDecisionManager.decide(authenticated, object, attr);     } catch (AccessDeniedException accessDeniedException) {       //授权不成功向外发布事件       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);     // 这里构建一个RunAsManager来替代当前的Authentication对象,默认情况 下使用的是NullRunAsManager会把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-invocation       return 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-invocation       return new InterceptorStatusToken(authenticated, true, attr, object);     }   }

到这里我们假设配置AffirmativeBased作为AccessDecisionManager:

Java代码

//这里定义了决策机制,需要全票才能通过   public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)     throws AccessDeniedException {     //这里取得配置好的迭代器集合     Iterator iter = this.getDecisionVoters().iterator();     int deny = 0;     //依次使用各个投票器进行投票,并对投票结果进行计票     while (iter.hasNext()) {       AccessDecisionVoter voter = (AccessDecisionVoter) iter.next ();       int result = voter.vote(authentication, object, config);       //这是对投票结果进行处理,如果遇到其中一票通过,那就授权通过, 如果是弃权或者反对,那就继续投票       switch (result) {       case AccessDecisionVoter.ACCESS_GRANTED:         return;       case AccessDecisionVoter.ACCESS_DENIED:       //这里对反对票进行计数         deny++;         break;       default:         break;       }     }     //如果有反对票,抛出异常,整个授权不通过     if (deny > 0) {       throw new AccessDeniedException(messages.getMessage ("AbstractAccessDecisionManager.accessDenied",           "Access is denied"));     }     // 这里对弃权票进行处理,看看是全是弃权票的决定情况,默认是不通过, 由allowIfAllAbstainDecisions变量控制     checkAllowIfAllAbstainDecisions();   }具体的投票由投票器进行,我们这里配置了RoleVoter来进行投票:   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;         // 这里对资源配置的安全授权级别进行判断,也就是匹配ROLE为前 缀的角色配置         // 遍历每个配置属性,如果其中一个匹配该主体持有的 GrantedAuthority,则访问被允许。         for (int i = 0; i < authentication.getAuthorities ().length; i++) {           if (attribute.getAttribute().equals (authentication.getAuthorities()[i].getAuthority())) {             return ACCESS_GRANTED;           }         }       }     }     return result;   }


上面就是对整个授权过程的一个分析,从FilterSecurityInterceptor拦截Http请求入 手,然后读取对资源的安全配置以后,把这些信息交由AccessDecisionManager来进行决 策,Spring为我们提供了若干决策器来使用,在决策器中我们可以配置投票器来完成投票 ,我们在上面具体分析了角色投票器的使用过程。

 

 

原创粉丝点击