GitHub地址

码云地址

一般我们通过@PreAuthorize("hasRole('ROLE_USER')") 注解,以及在HttpSecurity配置权限需求等来控制权限。在这里,我们基于请求的URI来控制访问权限,并且可以使用注解来控制权限访问。

新建一个资源项目,配置资源服务。 首先 自定义一个权限认证MySecurityAccessDecisionManager 继承AccessDecisionManager接口,重写 decide方法, 并且复制默认权限验证AbstractAccessDecisionManager的剩余两个方法(实现注解控制的重点)。用户具有的权限在认证服务器中已经自定义了。

/**
 * @Description 自定义权限认证,获取url判断是否有权限
 * @Author wwz
 * @Date 2019/08/01
 * @Param
 * @Return
 */
@Component
public class MySecurityAccessDecisionManager implements AccessDecisionManager {

    private List<AccessDecisionVoter<? extends Object>> decisionVoters;

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        String requestUrl = ((FilterInvocation) object).getRequest().getMethod() + ((FilterInvocation) object).getRequest().getRequestURI();
//        System.out.println("requestUrl>>" + requestUrl);

        // 当前用户所具有的权限
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
//        System.out.println("authorities=" + authorities);
        for (GrantedAuthority grantedAuthority : authorities) {
            if (grantedAuthority.getAuthority().equals(requestUrl)) {
                return;
            }
            if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) {
                return;
            }

        }
        throw new AccessDeniedException("无访问权限");
    }

    /**
     * 复制默认方法,使得@PreAuthorize("hasRole('ROLE_ADMIN')") 可用
     */
    @Override
    public boolean supports(ConfigAttribute attribute) {
        for (AccessDecisionVoter voter : this.decisionVoters) {
            if (voter.supports(attribute)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        for (AccessDecisionVoter voter : this.decisionVoters) {
            if (!voter.supports(clazz)) {
                return false;
            }
        }
        return true;
    }
}

在资源服务配置的httpSecurity中重写并注入:

/**
 * @Description 资源认证
 * @Author wwz
 * @Date 2019/08/01
 * @Param
 * @Return
 */
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)   // 启用注解权限配置
public class MySecurityResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private RedisConnectionFactory connectionFactory;

    @Bean
    public TokenStore tokenStore() {
        RedisTokenStore redis = new RedisTokenStore(connectionFactory);
        return redis;
    }

    @Resource
    private MyAccessDeniedHandler accessDeniedHandler; // 无权访问处理器

    @Resource
    private MyTokenExceptionEntryPoint tokenExceptionEntryPoint; // token失效处理器

    @Resource
    private MySecurityAccessDecisionManager accessDecisionManager; //权限判断

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .exceptionHandling().authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 另外,如果不设置,那么在通过浏览器访问被保护的任何资源时,每次是不同的SessionID,并且将每次请求的历史都记录在OAuth2Authentication的details的中
                .and()
                .authorizeRequests().antMatchers("/actuator/health").permitAll().anyRequest().authenticated()  // httpSecurity 放过健康检查,其他都需要验证  设置了.anyRequest().authenticated()才回进入自定义的权限判断
                .and()
                .requestMatchers().antMatchers("/auth/**") // .requestMatchers().antMatchers(...) OAuth2设置对资源的保护如果是用 /**的话 会把上面的也拦截掉
                .and()
                .authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {       // 重写做权限判断
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setAccessDecisionManager(accessDecisionManager);      // 权限判断
                        return o;
                    }
                })
                .and()
                .httpBasic();

        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.authenticationEntryPoint(tokenExceptionEntryPoint); // token失效处理器
        resources.resourceId("manager"); // 设置资源id  通过client的 resource_ids 来判断是否具有资源权限  资源不存在会报Invalid token does not contain resource id (manager)
    }
}

在MySecurityAccessDecisionManager打断点可以发现,全部请求都走这里进行权限判断了,根据认证服务器中的权限组合,匹配uri的请求进行结合方法上的注解权限进行是否有权访问判断,原则是全过则过,否则无权。

SpringSecurityOAuth2(4)根据请求URI动态权限判断(补)下篇文章

转载于:https://my.oschina.net/u/3500033/blog/3082364

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐