当前位置: 首页 > news >正文

SpringSecurity原理解析(七):权限校验流程

SpringSecurity是一个权限管理框架,核心功能就是认证和授权;前边已经介绍了认证流程,

这里看下授权的流程和核心类。

一、权限管理的实现

1、开启对权限管理的注解控制

      工程中后端各种请求资源要想被SpringSecurity的权限管理控制,首先我们需要在工程

      中开启 “对权限管理的注解控制”,放开了相关的注解后我们在Controller中才可以使用

      相关的注解来控制权限管理;可以通过 “注解” 和 “标签” 的方式开启

       1)基于“注解”的方式开启 “对权限管理的注解控制” 是在配置类或启动类通过注解 

             @EnableGlobalMethodSecurity  实现的,如下图所示:

              

        2)基于“标签” 的方式开启 “对权限管理的注解控制” 是在配置文件中实现的,

              如下图所示:

             

              

2、jsr250、spring、springsecurity 提供的权限管理注解在使用上的区别

         1)使用jsr250提供的注解来进行权限管理      

/********************************************************* 采用JSR250注解 开启权限管理*******************************************************/
@Controller
@RequestMapping("/jsr")
public class Jsr250Controller {@RolesAllowed(value = {"ROLE_ADMIN"})@RequestMapping("/query")public String query(){System.out.println("用户查询....");return "/home.jsp";}@RolesAllowed(value = {"ROLE_USER"})@RequestMapping("/save")public String save(){System.out.println("用户添加....");return "/home.jsp";}@RequestMapping("/update")public String update(){System.out.println("用户更新....");return "/home.jsp";}
}

         2)使用spring表达式来进行权限管理

/******************************************************** 使用spring 表达式开启权限管理* 注意权限注解的区别* *******************************************************/
@Controller
@RequestMapping("/order")
public class OrderController {@PreAuthorize(value = "hasAnyRole('ROLE_USER')")@RequestMapping("/query")public String query(){System.out.println("用户查询....");return "/home.jsp";}@PreAuthorize(value = "hasAnyRole('ROLE_ADMIN')")@RequestMapping("/save")public String save(){System.out.println("用户添加....");return "/home.jsp";}@RequestMapping("/update")public String update(){System.out.println("用户更新....");return "/home.jsp";}
}

         3)使用 springsecurity 提供的注解来进行权限管理

/******************************************************** 使用 SpringSecurity提供的注解开启权限管理* 注意权限注解的区别* * @author lbf* @date 2024-09-14 16:42*******************************************************/
@Controller
@RequestMapping("/role")
public class RoleController {@Secured(value = "ROLE_USER")@RequestMapping("/query")public String query(){System.out.println("用户查询....");return "/home.jsp";}@Secured("ROLE_ADMIN")@RequestMapping("/save")public String save(){System.out.println("用户添加....");return "/home.jsp";}@RequestMapping("/update")public String update(){System.out.println("用户更新....");return "/home.jsp";}
}

     

3、最后一点,在我们自定义的service服务中重写UserDetailsService的loadUserByUsername

         中进行用户认证的时候,需要绑定用户的权限数据,如下图所示:

                          

二、权限校验原理

        在前边"请求流转流程流程" 中分析过,一个请求到达后端servlet服务时需要经过很多个拦截

        器,但会在最后一个拦截器 FilterSecurityInterceptor中做认证和权限的校验操作;下面就从

        拦截器 FilterSecurityInterceptor 开始分析 SpringSecurity 的权限校验原理。

        如下图所示:

               

1、FilterSecurityInterceptor

      记住一点:任何拦截器的处里具体拦截操作的方法都是 doFilter 方法。

      打开 FilterSecurityInterceptor 类找到 doFilter 方法 发现,在 doFilter 方法中先创建一个

      FilterInvocation 对象,然后以 FilterInvocation 对象为参数调用FilterSecurityInterceptor.invoke

      方法, doFilter方法代码如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {this.invoke(new FilterInvocation(request, response, chain));}

      接着,先看下 FilterInvocation 的构造函数,通过构造函数可以看到,FilterInvocation其实就是

      对Request,Response和FilterChain做了一个非空的校验及封装。如下所示:

public FilterInvocation(ServletRequest request, ServletResponse response, FilterChain chain) {Assert.isTrue(request != null && response != null && chain != null, "Cannot pass null values to constructor");this.request = (HttpServletRequest)request;this.response = (HttpServletResponse)response;this.chain = chain;}

      接着进入 FilterSecurityInterceptor.invoke 方法中,看下具体的处里;

      FilterSecurityInterceptor.invoke 代码如下:    

public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {if (this.isApplied(filterInvocation) && this.observeOncePerRequest) {filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());} else {if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {filterInvocation.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);}/*核心方法,在该方法中完成了用户认证校验和权限校验操作*/InterceptorStatusToken token = super.beforeInvocation(filterInvocation);try {//执行连接器链的后续拦截器filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());} finally {//判断SpringSecurity 对象SecurityContext是否刷新了,若已经刷新,则重新设置SecurityContextsuper.finallyInvocation(token);}super.afterInvocation(token, (Object)null);}}

      接着进入父类中的 beforeInvocation 方法,看下用户认证和授权的处里

      beforeInvocation 代码如下:

//这个方法中完成对资源的授权
//这个object实际上就是FilterInvocation对象,里边封装了request,response,chain
protected InterceptorStatusToken beforeInvocation(Object object) {Assert.notNull(object, "Object was null");if (!this.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: " + this.getSecureObjectClass());} else {/*obtainSecurityMetadataSource(): 该方法的作用是根据当前的请求获取对应的需 要具备的权限信息,即attributes表示当前要访问的资源需要哪些权限才能访问,这个是我 们自己配置的,ConfigAttribute是一个接口,用来对资源的访问权限进行封装,这个后边再具体看。这里先理解成*/Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);// 对attributes进行校验if (CollectionUtils.isEmpty(attributes)) {Assert.isTrue(!this.rejectPublicInvocations, () -> {return "Secure object invocation " + object + " was denied as public invocations are not allowed via this interceptor. This indicates a configuration error because the rejectPublicInvocations property is set to 'true'";});if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Authorized public object %s", object));}this.publishEvent(new PublicInvocationEvent(object));return null;} else {if (SecurityContextHolder.getContext().getAuthentication() == null) {this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);}/*检查当前的authentication对象是否是已经认证过的,如果没有它会调用认证管理器尝试进行一次认证*/Authentication authenticated = this.authenticateIfRequired();if (this.logger.isTraceEnabled()) {this.logger.trace(LogMessage.format("Authorizing %s with attributes %s", object, attributes));}/*执行 attemptAuthorization 方法进行权限校验*/this.attemptAuthorization(object, attributes, authenticated);if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Authorized %s with attributes %s", object, attributes));}if (this.publishAuthorizationSuccess) {this.publishEvent(new AuthorizedEvent(object, attributes, authenticated));}Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);if (runAs != null) {SecurityContext origCtx = SecurityContextHolder.getContext();SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());SecurityContextHolder.getContext().setAuthentication(runAs);if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs));}return new InterceptorStatusToken(origCtx, true, attributes, object);} else {this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null");return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);}}}}private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes, Authentication authenticated) {try {//调用 AccessDecisionManager的 decide 方法进行权限校验//调用决策管理器的决策方法来授权this.accessDecisionManager.decide(authenticated, object, attributes);} catch (AccessDeniedException var5) {if (this.logger.isTraceEnabled()) {this.logger.trace(LogMessage.format("Failed to authorize %s with attributes %s using %s", object, attributes, this.accessDecisionManager));} else if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Failed to authorize %s with attributes %s", object, attributes));}this.publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, var5));throw var5;}}

      

2、ConfigAttribute 的获取

      由上边可以知道,ConfigAttribute 是通过 FilterSecurityInterceptor 父类 

      AbstractSecurityInterceptor中的方法 obtainSecurityMetadataSource() 来获取 ConfigAttribute

      的;obtainSecurityMetadataSource() 是一个抽象方法,其有2个实现,分别是:

             1)FilterSecurityInterceptor.obtainSecurityMetadataSource()

             2)MethodSecurityInterceptor.obtainSecurityMetadataSource()

      当然,需要看在FilterSecurityInterceptor类中的实现。

      FilterSecurityInterceptor.obtainSecurityMetadataSource() 方法代码如下:

public SecurityMetadataSource obtainSecurityMetadataSource() {return this.securityMetadataSource;}

      obtainSecurityMetadataSource() 方法返回的是一个 FilterInvocationSecurityMetadataSource

      类型的对象。

      所以obtainSecurityMetadataSource()先获取到FilterInvocationSecurityMetadataSource,再

      根据传入的object也就是最开始创建的FilterInvocation对象调用getAttributes方法来获取当

      前request请求的资源需要什么权限才能访问。

2.1、FilterInvocationSecurityMetadataSource

         FilterInvocationSecurityMetadataSource 是一个接口,定义如下:

               

         虽然 FilterInvocationSecurityMetadataSource 接口的实现类有很多,但我们需要看的方法

         getAttributes 的实现却只有2个即:

               1)AbstractMethodSecurityMetadataSource

               2)DefaultFilterInvocationSecurityMetadataSource

         这里我们需要看 getAttributes 在DefaultFilterInvocationSecurityMetadataSource 中的实现;

         DefaultFilterInvocationSecurityMetadataSource代码如下:   

public class DefaultFilterInvocationSecurityMetadataSource implementsFilterInvocationSecurityMetadataSource {protected final Log logger = LogFactory.getLog(getClass());//这个map很重要,key:请求的匹配器,value:某个请求需要哪些权限才能访问private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;//构造方法,map是通过构造方法传进来的public DefaultFilterInvocationSecurityMetadataSource(LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap) {this.requestMap = requestMap;}//这就是上边获取ConfigAttribute时调用的方法public Collection<ConfigAttribute> getAttributes(Object object) {//从object中获取到requestfinal HttpServletRequest request = ((FilterInvocation) object).getRequest();//拿到上边map中的所有key进行遍历,如果找到一个key能够匹配request就会返回//所以RequestMatcher在这个map中的顺序很重要,顺序靠前的就会被先匹配。for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {if (entry.getKey().matches(request)) {return entry.getValue();}}//找不到匹配的就返回nullreturn null;}public boolean supports(Class<?> clazz) {return FilterInvocation.class.isAssignableFrom(clazz);}}

         

3、AccessDescisionManager

     AccessDescisionManager 从字面上看称之为决策管理器“”

     前边提到,SpringSecurity 真正去请求资源进行授权的操作是在 AccessDescisionManager

     中完成的;

      1)AccessDescisionManager 定义如下:

public interface AccessDecisionManager {/**决策方法,参数:authentication:用户的authentication,object:实际是FilterInvocation,configAttributes:当前请求资源需要具备的权限列表*/void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException;/*判断当前管理器是否支持对本次请求资源授权*/boolean supports(ConfigAttribute attribute);boolean supports(Class<?> clazz);
}

      AccessDecisionManager并不直接处理授权,它通过调用内部维护的投票器

      AccessDecisionVoter来完成授权,每个投票器的投票结果有 通过,否决,弃权三种情况。

      所以针对多个投票器的结果就会有 一票通过,一票否决,少数服从多数这几种授权模式,

      对应的就是AccessDecisionManager的几个实现类;

      AccessDecisionManager 默认实现有3个,如下图所示:

             

      但这三个默认实现并不是直接实现AccessDecisionManager 接口 ,而是继承抽象

      类 AbstractAccessDecisionManager;AbstractAccessDecisionManager 是直接实

      现 AccessDecisionManager 接口,但并没有重写决策方法 decide,决策方法 decide

      方法是在三个子类中实现的。

      AbstractAccessDecisionManager 定义如下:

          

public abstract class AbstractAccessDecisionManager implements AccessDecisionManager, InitializingBean, MessageSourceAware {protected final Log logger = LogFactory.getLog(this.getClass());//维护的投票器列表private List<AccessDecisionVoter<?>> decisionVoters;protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();//用来标识全部投票器都弃权的情况private boolean allowIfAllAbstainDecisions = false;//初始化投票器列表protected AbstractAccessDecisionManager(List<AccessDecisionVoter<?>> decisionVoters) {Assert.notEmpty(decisionVoters, "A list of AccessDecisionVoters is required");this.decisionVoters = decisionVoters;}/*判断是否所有的投票器都弃权了,*/protected final void checkAllowIfAllAbstainDecisions() {if (!this.isAllowIfAllAbstainDecisions()) {throw new AccessDeniedException(this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));}}/**遍历投票器列表,只要有一个投票器支持这个ConfigAttribute,则此管理器就支持此次授权*/public boolean supports(ConfigAttribute attribute) {Iterator var2 = this.decisionVoters.iterator();AccessDecisionVoter voter;do {if (!var2.hasNext()) {return false;}voter = (AccessDecisionVoter)var2.next();} while(!voter.supports(attribute));return true;}public boolean supports(Class<?> clazz) {Iterator var2 = this.decisionVoters.iterator();AccessDecisionVoter voter;do {if (!var2.hasNext()) {return true;}voter = (AccessDecisionVoter)var2.next();} while(voter.supports(clazz));return false;}//其他方法省略
}

     下边分别看下 AccessDecisionManager 的3个默认实现类,       

3.1、AffirmativeBased

        在SpringSecurity中默认的权限决策对象就是AffirmativeBased。AffirmativeBased的作用

        是在众多的投票者中只要有一个返回肯定的结果,就会授予访问权限。

        具体的决策逻辑如下:

public class AffirmativeBased extends AbstractAccessDecisionManager {//通过构造方法传入的投票器集合public AffirmativeBased(List<AccessDecisionVoter<? extends Object>> decisionVoters) {super(decisionVoters);}//核心方法//此决策管理器的决策方法,重写接口AccessDecisionManager 的方法public void decide(Authentication authentication, Object object,Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {//否决的票数int deny = 0;//获取到所有的投票器后,循环遍历所有的投票器//getDecisionVoters(): 获取所有的投票器for (AccessDecisionVoter voter : getDecisionVoters()) {//调用投票器的投票方法,进行投票处里int result = voter.vote(authentication, object, configAttributes);if (logger.isDebugEnabled()) {logger.debug("Voter: " + voter + ", returned: " + result);}switch (result) {//通过case AccessDecisionVoter.ACCESS_GRANTED:return;// 若投票器做出了 同意的操作,那么直接结束//若投票器做出了 否决 的操作,则记录否决次数case AccessDecisionVoter.ACCESS_DENIED:deny++;break;default: //默认是弃权break;}}//如果deny > 0 说明没有投票器投赞成的,有投了否决的 则抛出异常if (deny > 0) {throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));}// To get this far, every AccessDecisionVoter abstained// 执行到这儿说明 deny = 0 说明都投了弃权 票   然后检查是否支持都弃权,//该方法定义在父类中checkAllowIfAllAbstainDecisions();}
}

      

3.2、ConsensusBased

        ConsensusBased则是基于少数服从多数的方案来实现授权的决策方案。

        ConsensusBased 代码如下:

public class ConsensusBased extends AbstractAccessDecisionManager {/*用于给用户提供自定义的机会,其默认值为 true,即代表允许授予权限和拒绝权限相等,且同时也代表授予访问权限。*/private boolean allowIfEqualGrantedDeniedDecisions = true;//初始化,传入投票器集合public ConsensusBased(List<AccessDecisionVoter<?>> decisionVoters) {super(decisionVoters);}/**注意下授予权限和否决权限相等时的逻辑*/public void decide(Authentication authentication, Object object,Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {int grant = 0; // 同意int deny = 0;  // 否决for (AccessDecisionVoter voter : getDecisionVoters()) {int result = voter.vote(authentication, object, configAttributes);if (logger.isDebugEnabled()) {logger.debug("Voter: " + voter + ", returned: " + result);}switch (result) {case AccessDecisionVoter.ACCESS_GRANTED:grant++; // 同意的 grant + 1break;case AccessDecisionVoter.ACCESS_DENIED:deny++; // 否决的 deny + 1break;default:break;}}if (grant > deny) {return; // 如果 同意的多与 否决的就放过}if (deny > grant) { // 如果否决的占多数 就拒绝访问throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));}if ((grant == deny) && (grant != 0)) { // 如果同意的和拒绝的票数一样 继续判断是否有同意的if (this.allowIfEqualGrantedDeniedDecisions) {return; // 如果支持票数相同就放过}else { // 否则就抛出异常 拒绝throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));}}// 所有都投了弃权票的情况// To get this far, every AccessDecisionVoter abstainedcheckAllowIfAllAbstainDecisions();}public boolean isAllowIfEqualGrantedDeniedDecisions() {return this.allowIfEqualGrantedDeniedDecisions;}public void setAllowIfEqualGrantedDeniedDecisions(boolean allowIfEqualGrantedDeniedDecisions) {this.allowIfEqualGrantedDeniedDecisions = allowIfEqualGrantedDeniedDecisions;}
}

   

3.3、UnanimousBased

         UnanimousBased是最严格的决策器,要求所有的AccessDecisionVoter都授权,

         才代表授予资源权限,否则就拒绝。  

         UnanimousBased 代码如下:

public class UnanimousBased extends AbstractAccessDecisionManager {public UnanimousBased(List<AccessDecisionVoter<?>> decisionVoters) {super(decisionVoters);}public void decide(Authentication authentication, Object object,Collection<ConfigAttribute> attributes) throws AccessDeniedException {int grant = 0; // 赞成的计票器List<ConfigAttribute> singleAttributeList = new ArrayList<>(1);singleAttributeList.add(null);for (ConfigAttribute attribute : attributes) {singleAttributeList.set(0, attribute);for (AccessDecisionVoter voter : getDecisionVoters()) {int result = voter.vote(authentication, object, singleAttributeList);if (logger.isDebugEnabled()) {logger.debug("Voter: " + voter + ", returned: " + result);}switch (result) {case AccessDecisionVoter.ACCESS_GRANTED:grant++;break;case AccessDecisionVoter.ACCESS_DENIED: // 只要有一个拒绝 就 否决授权 抛出异常throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied","Access is denied"));default:break;}}}// 执行到这儿说明没有投 否决的, grant>0 说明有投 同意的// To get this far, there were no deny votesif (grant > 0) {return;}// 说明都投了 弃权票// To get this far, every AccessDecisionVoter abstainedcheckAllowIfAllAbstainDecisions();}
}

         下面再来看看各种投票器AccessDecisionVoter

4、AccessDecisionVoter

     AccessDecisionVoter是一个投票器,负责对授权决策进行表决。表决的结构最终由

     AccessDecisionManager统计,并做出最终的决策。

public interface AccessDecisionVoter<S> {int ACCESS_GRANTED = 1; // 赞成int ACCESS_ABSTAIN = 0; // 弃权int ACCESS_DENIED = -1;  // 否决boolean supports(ConfigAttribute attribute);boolean supports(Class<?> clazz);//投票,表决int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);}

        AccessDecisionVoter 具体实现类图:

               

        下边来看看常见的几种投票器

4.1、WebExpressionVoter

         WebExpressionVoter是最常用的,也是SpringSecurity中默认的 FilterSecurityInterceptor

         实例中 AccessDecisionManager默认的投票器,它其实就是 http.authorizeRequests()基

         于 Spring-EL进行控制权限的授权决策类。

          如:进入自定义spring security配置类的SpringSecurityConfiguration.configure方法,

          进入 authorizeRequests()方法 ,如下图:

                  

                  

          而getExpressionHandler返回的对应的ExpressionHandler其实就是对SPEL表达式

          做相关的解析处理。

4.2、AuthenticatedVoter

        AuthenticatedVoter针对的是ConfigAttribute#getAttribute() 中配置为

        IS_AUTHENTICATED_FULLY 、IS_AUTHENTICATED_REMEMBERED、

        IS_AUTHENTICATED_ANONYMOUSLY 权限标识时的授权决策。因此,其投票

        策略比较简单,如下所示:

@Overridepublic int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {int result = ACCESS_ABSTAIN; // 默认 弃权 0for (ConfigAttribute attribute : attributes) {if (this.supports(attribute)) {result = ACCESS_DENIED; // 拒绝if (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())) {if (isFullyAuthenticated(authentication)) {return ACCESS_GRANTED; // 认证状态直接放过}}if (IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())) {if (this.authenticationTrustResolver.isRememberMe(authentication)|| isFullyAuthenticated(authentication)) {return ACCESS_GRANTED; // 记住我的状态 放过}}if (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) {if (this.authenticationTrustResolver.isAnonymous(authentication)|| isFullyAuthenticated(authentication)|| this.authenticationTrustResolver.isRememberMe(authentication)) {return ACCESS_GRANTED; // 可匿名访问 放过}}}}return result;}

        

4.3、PreInvocationAuthorizationAdviceVoter

         用于处理基于注解 @PreFilter 和 @PreAuthorize 生成的

         PreInvocationAuthorizationAdvice,来处理授权决策的实现。

         如下图所示:

                

         具体投票逻辑代码如下所示:

	@Overridepublic int vote(Authentication authentication, MethodInvocation method, Collection<ConfigAttribute> attributes) {// Find prefilter and preauth (or combined) attributes// if both null, abstain else call advice with themPreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);if (preAttr == null) {// No expression based metadata, so abstainreturn ACCESS_ABSTAIN;}return this.preAdvice.before(authentication, method, preAttr) ? ACCESS_GRANTED : ACCESS_DENIED;}

         

4.4、RoleVoter

         角色投票器。用于 ConfigAttribute#getAttribute() 中配置为角色的授权决策。其默认前

         缀为 ROLE_,可以自定义,也可以设置为空,直接使用角色标识进行判断。这就意味着,

         任何属性都可以使用该投票器投票,也就偏离了该投票器的本意,是不可取的。

public class RoleVoter implements AccessDecisionVoter<Object> {//角色前缀private String rolePrefix = "ROLE_";public String getRolePrefix() {return rolePrefix;}/*** Allows the default role prefix of <code>ROLE_</code> to be overridden. May be set* to an empty value, although this is usually not desirable.** @param rolePrefix the new prefix*/public void setRolePrefix(String rolePrefix) {this.rolePrefix = rolePrefix;}//是否支持某个ConfigAttribute的判断public boolean supports(ConfigAttribute attribute) {if ((attribute.getAttribute() != null)&& attribute.getAttribute().startsWith(getRolePrefix())) {return true;}else {return false;}}public boolean supports(Class<?> clazz) {return true;}//真正的决策方法//决策策略比较简单,用户只需拥有任一当前请求需要的角色即可,不必全部拥有public int vote(Authentication authentication, Object object,Collection<ConfigAttribute> attributes) {if (authentication == null) {return ACCESS_DENIED;}int result = ACCESS_ABSTAIN;//先默认是弃权//获取当前用户的权限集合,默认的用户配置时获取到的实际就是角色集合Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);// attributes表示能访问资源的角色集合//遍历for (ConfigAttribute attribute : attributes) {//此投票器是否支持此ConfigAttributeif (this.supports(attribute)) {result = ACCESS_DENIED;// 遍历用户的权限(角色)集合,尝试找到一个匹配的for (GrantedAuthority authority : authorities) {if (attribute.getAttribute().equals(authority.getAuthority())) {return ACCESS_GRANTED;}}}}return result;}Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {//获取用户的权限数据。return authentication.getAuthorities();}
}

4.5、RoleHierarchyVoter

        基于 RoleVoter,唯一的不同就是该投票器中的角色是附带上下级关系的。也就是说,

        角色A包含角色B,角色B包含 角色C,此时,如果用户拥有角色A,那么理论上可以同时

        拥有角色B、角色C的全部资源访问权限。

@OverrideCollection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {return this.roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());}


http://www.mrgr.cn/news/27491.html

相关文章:

  • 区块链技术在游戏行业的应用
  • uniapp小程序分享使用canvas自定义绘制 vue3
  • FPGA 第6讲 简单组合逻辑多路选择器
  • GA/T1400视图库平台EasyCVR视频融合平台HLS视频协议是什么?
  • 从零创建vue+elementui+sass+three.js项目
  • JMeter中添加请求头
  • Python数据分析-Numpy快速入门
  • 【Scala入门学习】基本数据类型和变量声明
  • 6.1 溪降技术:绳结
  • 分享一些智慧农业数据集
  • VMware中安装win7和kail等虚拟机
  • 适合学生党开学买的蓝牙耳机?分享开放式耳机排行榜前十名
  • 半导体制造技术中的沉积和驱入(Deposition and drive-in)过程
  • P1540 [NOIP2010 提高组] 机器翻译
  • 深入理解 SpringMVC:现代Web开发全面指南
  • Java | Leetcode Java题解之第406题根据身高重建队列
  • Mac清理软件哪个好?一场与“垃圾”的欢乐对决!
  • 数据结构基础详解:哈希表【C语言代码实践篇】开放地址法__拉链法_哈希表的创建_增删查操作详解
  • 【WRF工具介绍】WRF Domain Wizard-确定模拟区域
  • kali——fcrackzip和rarcrack的使用
  • 解决win11 使用wsl工具,不能使用systemctl
  • 深度学习基础案例5--运用动态学习率构建CNN卷积神经网络实现的运动鞋识别(测试集的准确率84%)
  • 【UEFI基础】BIOS模块执行的优先级
  • matlab delsat = setdiff(1:69,unique(Eph(30,:))); 语句含义
  • 二十天刷leetcode【hot100】算法- day2[后端golang]
  • 文件的应用实例