当前位置: 技术文章>> Spring Security专题之-Spring Security的权限表达式与自定义表达式

文章标题:Spring Security专题之-Spring Security的权限表达式与自定义表达式
  • 文章分类: 后端
  • 7039 阅读

Spring Security的权限表达式与自定义表达式

在Spring Security中,权限控制是保障应用安全性的重要一环。通过权限表达式,我们可以灵活地对访问资源的用户进行授权验证。Spring Security不仅提供了内置的权限表达式如hasRolehasAuthority等,还支持自定义权限表达式以满足复杂的业务需求。本文将深入探讨Spring Security的权限表达式及其自定义方法,并结合实际案例进行说明。

内置权限表达式

Spring Security内置了几种常用的权限表达式,这些表达式可以直接在@PreAuthorize@PostAuthorize等注解中使用,用于控制方法的访问权限。

hasRole

hasRole表达式用于检查用户是否拥有特定的角色。需要注意的是,在hasRole表达式中,角色名称前需要加上ROLE_前缀。但在控制器中使用时,前缀通常不需要添加。

例如,在UserDetailsService中设置用户角色:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER,ROLE_ADMIN");
    return new User(username, "password", authorities);
}

在控制器中使用@PreAuthorize注解控制访问:

@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin")
public String adminPage() {
    return "adminPage";
}

hasAuthority

hasAuthority表达式用于检查用户是否拥有特定的权限。与hasRole不同,hasAuthority不需要在权限名称前添加任何前缀。

例如,在UserDetailsService中设置用户权限:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("READ,WRITE");
    return new User(username, "password", authorities);
}

在控制器中使用@PreAuthorize注解控制访问:

@PreAuthorize("hasAuthority('WRITE')")
@GetMapping("/write")
public String writePage() {
    return "writePage";
}

其他内置表达式

除了hasRolehasAuthority,Spring Security还提供了其他一些内置表达式,如permitAll(允许所有用户访问)、denyAll(拒绝所有用户访问)、isAuthenticated()(用户已认证)、isAnonymous()(用户是匿名用户)等。这些表达式在特定场景下非常有用,例如:

@PreAuthorize("isAuthenticated()")
@GetMapping("/protected")
public String protectedPage() {
    return "protectedPage";
}

自定义权限表达式

虽然Spring Security提供了丰富的内置权限表达式,但在某些复杂业务场景中,我们可能需要定义自己的权限表达式。自定义权限表达式通常通过继承SecurityExpressionRoot类(或MethodSecurityExpressionRoot类,如果针对方法安全)并实现MethodSecurityExpressionOperations接口来完成。

步骤一:定义自定义权限表达式类

首先,我们定义一个继承自SecurityExpressionRoot的类,并实现MethodSecurityExpressionOperations接口(如果必要)。在这个类中,我们可以添加自己的权限校验方法。

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    private Object filterObject;
    private Object returnObject;
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    public CustomSecurityExpressionRoot(Authentication authentication) {
        super(authentication);
    }

    // 自定义权限校验方法
    public boolean hasPermission(String permission) {
        // 获取用户的所有权限
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (GrantedAuthority authority : authorities) {
            if (antPathMatcher.match(authority.getAuthority(), permission)) {
                return true;
            }
        }
        return false;
    }

    // 其他MethodSecurityExpressionOperations接口方法...
}

步骤二:配置自定义权限表达式处理器

接下来,我们需要配置一个自定义的权限表达式处理器,该处理器将使用我们定义的CustomSecurityExpressionRoot类。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        // 认证配置...
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // HTTP安全配置...
    }

    @Bean
    public CustomMethodSecurityExpressionHandler customMethodSecurityExpressionHandler() {
        return new CustomMethodSecurityExpressionHandler();
    }

    public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
        @Override
        protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
                Authentication authentication, MethodInvocation invocation) {
            CustomSecurityExpressionRoot root = new CustomSecurityExpressionRoot(authentication);
            // 设置其他必要的属性或处理器...
            return root;
        }
    }
}

步骤三:在控制器中使用自定义权限表达式

配置完成后,我们就可以在控制器中使用自定义的权限表达式了。

@PreAuthorize("hasPermission('system:user:add')")
@PostMapping("/users")
public ResponseEntity<?> addUser(@RequestBody User user) {
    // 添加用户逻辑...
    return ResponseEntity.ok().build();
}

实际应用案例

假设我们有一个在线文档系统,需要控制用户对文档的访问权限。我们可以定义以下几种权限:

  • READ_DOCUMENT:读取文档
  • EDIT_DOCUMENT:编辑文档
  • DELETE_DOCUMENT:删除文档

UserDetailsService中,我们根据用户的角色或权限设置相应的GrantedAuthority

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("READ_DOCUMENT,EDIT_DOCUMENT");
    return new User(username, "password", authorities);
}

然后,在控制器中,我们使用自定义的权限表达式来控制对文档的访问:

@PreAuthorize("hasPermission('READ_DOCUMENT')")
@GetMapping("/documents/{id}")
public ResponseEntity<?> getDocument(@PathVariable Long id) {
    // 获取文档逻辑...
    return ResponseEntity.ok().build();
}

@PreAuthorize("hasPermission('EDIT_DOCUMENT')")
@PutMapping("/documents/{id}")
public ResponseEntity<?> updateDocument(@PathVariable Long id, @RequestBody Document document) {
    // 更新文档逻辑...
    return ResponseEntity.ok().build();
}

@PreAuthorize("hasPermission('DELETE_DOCUMENT')")
@DeleteMapping("/documents/{id}")
public ResponseEntity<?> deleteDocument(@PathVariable Long id) {
    // 删除文档逻辑...
    return ResponseEntity.ok().build();
}

通过自定义权限表达式,我们能够更灵活地控制用户对资源的访问权限,满足复杂业务场景的需求。

总结

Spring Security的权限表达式是保障应用安全性的重要工具。通过内置权限表达式和自定义权限表达式,我们可以灵活地对用户进行权限控制。在自定义权限表达式时,我们需要定义自己的SecurityExpressionRoot类,并配置相应的权限表达式处理器。通过这种方式,我们可以将复杂的权限校验逻辑封装在自定义权限表达式中,使控制器层更加简洁明了。希望本文能够帮助你更好地理解Spring Security的权限表达式及其自定义方法。

推荐文章