使用组合模式实现权限验证的例子

步骤

为了演示组合模式实现权限验证的功能,我需要做以下几个步骤:

  • 创建一个Spring Boot项目,并添加spring-boot-starter-security依赖
  • 创建一个简单的Controller类,提供两个受保护的资源:/admin和/user
  • 创建一个SecurityConfig类,继承WebSecurityConfigurerAdapter,并配置自定义的AccessDecisionManager和AccessDecisionVoter
  • 创建一个CustomAccessDecisionManager类,实现AccessDecisionManager接口,并注入一个AccessDecisionVoter列表
  • 创建两个CustomAccessDecisionVoter类,分别实现AccessDecisionVoter接口,并根据不同的逻辑进行投票
  • 在application.properties中配置一些用户和角色信息

首先,创建一个Spring Boot项目,并添加spring-boot-starter-security依赖:


    
        org.springframework.boot
        spring-boot-starter-security
    

然后,创建一个简单的Controller类,提供两个受保护的资源:/admin和/user:

@RestController
public class HelloController {

    @GetMapping("/admin")
    public String admin() {
        return "Hello, admin!";
    }

    @GetMapping("/user")
    public String user() {
        return "Hello, user!";
    }
}

接着,创建一个SecurityConfig类,继承WebSecurityConfigurerAdapter,并配置自定义的AccessDecisionManager和AccessDecisionVoter:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 配置自定义的AccessDecisionManager
        http.authorizeRequests()
                .anyRequest().authenticated()
                .accessDecisionManager(customAccessDecisionManager());
    }

    // 定义一个Bean,返回一个CustomAccessDecisionManager对象,并注入两个CustomAccessDecisionVoter对象
    @Bean
    public AccessDecisionManager customAccessDecisionManager() {
        List> voters = new ArrayList<>();
        voters.add(new CustomAccessDecisionVoter1());
        voters.add(new CustomAccessDecisionVoter2());
        return new CustomAccessDecisionManager(voters);
    }

    // 在内存中配置一些用户和角色信息
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin").password("{noop}123").roles("ADMIN")
                .and()
                .withUser("user").password("{noop}123").roles("USER");
    }
}

接下来,创建一个CustomAccessDecisionManager类,实现AccessDecisionManager接口,并注入一个AccessDecisionVoter列表:

public class CustomAccessDecisionManager implements AccessDecisionManager {

    // 注入一个AccessDecisionVoter列表
    private List> voters;

    public CustomAccessDecisionManager(List> voters) {
        this.voters = voters;
    }

    // 实现decide方法,根据投票结果决定是否授权访问
    @Override
    public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        // 定义一个变量,记录赞成票数
        int grant = 0;
        // 遍历所有的投票者,调用它们的vote方法,并根据返回值进行统计
        for (AccessDecisionVoter voter : voters) {
            int result = voter.vote(authentication, object, configAttributes);
            switch (result) {
                case AccessDecisionVoter.ACCESS_GRANTED:
                    grant++;
                    break;
                case AccessDecisionVoter.ACCESS_DENIED:
                    throw new AccessDeniedException("访问被拒绝");
                default:
                    break;
            }
        }
        // 如果没有赞成票,则抛出异常
        if (grant == 0) {
            throw new AccessDeniedException("访问被拒绝");
        }
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class clazz) {
        return true;
    }
}

最后,创建两个CustomAccessDecisionVoter类,分别实现AccessDecisionVoter接口,并根据不同的逻辑进行投票:

public class CustomAccessDecisionVoter1 implements AccessDecisionVoter {

    // 实现vote方法,根据请求的URL和用户的角色进行投票
    @Override
    public int vote(Authentication authentication, Object object, Collection attributes) {
        // 获取请求对象
        HttpServletRequest request = ((FilterInvocation) object).getRequest();
        // 获取请求的URL
        String url = request.getRequestURI();
        // 获取用户的角色
        Collection authorities = authentication.getAuthorities();
        // 如果请求的是/admin,且用户的角色是ADMIN,则投赞成票
        if (url.equals("/admin") && authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
            return ACCESS_GRANTED;
        }
        // 否则,投弃权票
        return ACCESS_ABSTAIN;
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class clazz) {
        return true;
    }
}
 
  
public class CustomAccessDecisionVoter2 implements AccessDecisionVoter {

    // 实现vote方法,根据请求的URL和用户的角色进行投票
    @Override
    public int vote(Authentication authentication, Object object, Collection attributes) {
        // 获取请求对象
        HttpServletRequest request = ((FilterInvocation) object).getRequest();
        // 获取请求的URL
        String url = request.getRequestURI();
        // 获取用户的角色
        Collection authorities = authentication.getAuthorities();
        // 如果请求的是/user,且用户的角色是USER,则投赞成票
        if (url.equals("/user") && authorities.contains(new SimpleGrantedAuthority("ROLE_USER"))) {
            return ACCESS_GRANTED;
        }
        // 否则,投弃权票
        return ACCESS_ABSTAIN;
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class clazz) {
        return true;
    }
}
 
  

这样,我们就用最简练的代码写出了一个使用组合模式实现权限验证的例子。您可以运行这个项目,并使用不同的用户和密码访问不同的资源。例如:

  • 使用admin/123访问/admin,可以成功访问,返回Hello, admin!
  • 使用admin/123访问/user,会失败访问,抛出AccessDeniedException异常
  • 使用user/123访问/user,可以成功访问,返回Hello, user!
  • 使用user/123访问/admin,会失败访问,抛出AccessDeniedException异常

使用了两个CustomAccessDecisionVoter对象来组合一个CustomAccessDecisionManager对象,并通过它来决定一个请求是否能够访问受保护的资源。这就是使用组合模式实现权限验证的功能。

你可能感兴趣的:(java,spring,boot,组合模式)