实现动态REST资源特定的授权

huangapple go评论75阅读模式
英文:

Achieve Dynamic REST Resource specific Authorization

问题

我们有一个基于SpringBoot的模块,我们有REST API,允许使用参数创建资源

请求

POST /resources

{
  "resourceName": "Res1",
  "admins": ["john.doe@company.com", "jane.doe@company.com"]
}

响应

{
  "id": "R1",
  "resourceName": "Res1",
  "admins": ["john.doe@company.com", "jane.doe@company.com"]
}

请求

POST /resources

{
  "resourceName": "Res2",
  "admins": ["alice@company.com", "bob@company.com"]
}

响应

{
  "id": "R2",
  "resourceName": "Res2",
  "admins": ["alice@company.com", "bob@company.com"]
}

对于R1的更新API只能由John/Jane访问

请求

PUT /resources/R1

{
  "resourceName": "Resource1",
  "admins": ["john.doe@company.com", "jane.doe@company.com", "jacob@company.com"]
}

响应

对于John/Jane,响应应为:

{
  "id": "R1",
  "resourceName": "Resource1",
  "admins": ["john.doe@company.com", "jane.doe@company.com", "jacob@company.com"]
}

当Alice/Bob用户更新R1时,响应应为403 Forbidden

类似地,对于R2的更新API只能由Alice/Bob访问

当John/Jane更新R2时,响应应为403 Forbidden

请建议使用哪个框架来实现这一点,最好是减少样板代码

目前,我们有一个资源访问的系统,采用基于角色的访问控制(Role-Based Access Control,RBAC)的形式。我们通过存储权限来实现限制。RBAC配置保存在数据库中。

但现在我们需要更细粒度的每个资源的控制,这可以由现有管理员直接管理。

英文:

We have a SpringBoot based module, we have REST APIs which allow creating resources with Params like

Request

POST /resources

{
  "resourceName": "Res1",
  "admins": ["john.doe@company.com", "jane.doe@company.com"]
}

Response

{
  "id": "R1"
  "resourceName": "Res1",
  "admins": ["john.doe@company.com", "jane.doe@company.com"]
}

Request

POST /resources

{
  "resourceName": "Res2",
  "admins": ["alice@company.com", "bob@company.com"]
}

Response

{
  "id": "R2"
  "resourceName": "Res2",
  "admins": ["alice@company.com", "bob@company.com"]
}

For R1 update API should only be accesible by John/Jane

Request

PUT /resources/R1

{
  "resourceName": "Resource1",
  "admins": ["john.doe@company.com", "jane.doe@company.com", "jacob@company.com"]
}

Response

For John / Jane the response should be:

{
  "id": "R1"
  "resourceName": "Resource1",
  "admins": ["john.doe@company.com", "jane.doe@company.com", "jacob@company.com"]
}

When Alice / Bob user are updating R1 this response should be 403 Forbidden

Similarly For R2 update API should only be accesible by Alice / Bob.

When John / Jane are updating R2 this response should be 403 Forbidden

Please suggest which framework can be used to achieve this, preferably with less boiler plate

Currently we have a system where resource access is in for of RoleBasedAccessControl.
We achieve restriction by storing permissions. The RBAC config is saved in DB.

But now we need more fine grained control per resource which can be managed directly by existing admins

答案1

得分: 0

你可以在这种情况下使用Spring Security。
通常建议为用户分配角色以提高可维护性。
例如,分配管理员角色给 - john.doe@company.com、jane.doe@company.com,然后使用角色管理访问。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    // 为用户分配角色
    private static final String ROLE_1 = "ADMIN";
    private static final String ROLE_2 = "USER";

    // 在内存中配置用户和角色
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("john.doe@company.com")
                .password(passwordEncoder().encode("admin@123"))
                .roles(ROLE_1)
                .and()
                .withUser("jane.doe@company.com")
                .password(passwordEncoder().encode("admin@12345"))
                .roles(ROLE_1)
                .and()
                .withUser("user")
                .password(passwordEncoder().encode("user@123"))
                .roles(ROLE_2);
    }

    // 密码编码
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 基于角色授权请求
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/resources/**").hasRole(ROLE_1)
                .antMatchers("/all").permitAll()
                .and().formLogin();
    }
}
英文:

You could use Spring Security for this use case.
As a general practice it is recommended to assign roles for users for maintability.
For example assign admin role to - john.doe@company.com, jane.doe@company.com then manage the access using the role.

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
	
	// Roles for users
	private static final String ROLE_1 = "ADMIN";
	private static final String ROLE_2 = "USER";
	
	// In-memory users with roles
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {		
		auth.inMemoryAuthentication()
				.withUser("john.doe@company.com")
				.password(passwordEncoder().encode("admin@123"))
				.roles(ROLE_1)
                .and()
				.withUser("jane.doe@company.com")
				.password(passwordEncoder().encode("admin@12345"))
				.roles(ROLE_1)
				.and()
				.withUser("user")
				.password(passwordEncoder().encode("user@123"))
				.roles(ROLE_2);
	}
	
	// Password encoding
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	// Authorized the request based on role
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				.antMatchers("/resources/**").hasRole(ROLE_1)
				.antMatchers("/all").permitAll()
				.and().formLogin();
	}
}

答案2

得分: 0

这可以通过使用SpringBoot的方法级安全性来实现。

首先,通过对配置类进行注释来启用方法级安全性,如下所示 -

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class GlobalMethodConfig extends GlobalMethodSecurityConfiguration

然后提供PermissionEvaluator类的实现,如下所示 -

@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, Object permission) {
  if (permission.equals("RESOURCE_WRITE_AUTHORITY")) {
    Resource res = resourceRepository.getResourceById((String) targetId);
    if (env != null) {
      return env.getAdmins().contains(authentication.getName());
    }
  }
  return false;
}

然后,您可以在控制器中对PUT方法或服务中的相应方法进行注释,以使用PermissionEvaluator,如下所示 -

@PreAuthorize("hasPermission(#resourceName, 'RESOURCE_WRITE_AUTHORITY')")
public Environment updateResource(String resourceName)

参考链接 -
https://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html#:~:text=15.3.1%C2%A0%40Pre%20and%20%40Post%20Annotations

英文:

This can be achieved using a method level security from SpringBoot.

First enable Method Level Security by annotating a Config class like -

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class GlobalMethodConfig extends GlobalMethodSecurityConfiguration

Then provide an implementation of PermissionEvaluator class like -

@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, Object permission) {
  if (permission.equals("RESOURCE_WRITE_AUTHORITY")) {
    Resource res = resourceRepository.getResourceById((String) targetId);
    if(env != null) {
      return env.getAdmins().contains(authentication.getName());
    }
  }
  return false;
}

After which you can annotate your PUT method in controller or the corresponding method in the service, to use PermissionEvaluator, like

@PreAuthorize("hasPermission(#resourceName, 'RESOURCE_WRITE_AUTHORITY')")
public Environment updateResource(String resourceName)

References -
https://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html#:~:text=15.3.1%C2%A0%40Pre%20and%20%40Post%20Annotations

huangapple
  • 本文由 发表于 2023年2月6日 18:06:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/75359869.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定