spring资料休息:如何发布一个用户管理api

spring data rest: How to publish a user management api

是否可以公开生成的 spring 剩余数据 API 来管理与 spring 安全性一起用于身份验证和访问控制的相同用户?

考虑实体:

@Entity
public class User implements UserDetails {
   ....
}

与 spring 安全性一起使用:

@Service
public class RepositoryUserDetailsService implements UserDetailsService{

    private final UnsecuredUserRepository users;

    @Autowired
    public RepositoryUserDetailsService(UnsecuredUserRepository users) {
        this.users = users;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User one = users.findOne(username);
        if (one == null) {
            throw new UsernameNotFoundException("No such user");
        }
        return one;
    }

}

它使用以下 spring 数据存储库:

public interface UnsecuredUserRepository extends CrudRepository<User, String> {
}

我现在想添加一个管理员 API 来管理用户。 Spring data rest 可以为我做这件事,我可以使用 spring 安全来保护它。

@PreAuthorize("hasRole('ROLE_USER_MANAGER')")
public interface UserRepository extends CrudRepository<User, String>, UserSignUpExtension {

    @Override
    @PreAuthorize("hasRole('ROLE_USER_MANAGER') or #userName == authentication?.name")
    void delete(@Param("userName") String userName);

}

问题是不能 multiple repositories for the same entities with spring data rest,使用安全存储库会产生一个先有鸡蛋的问题,并阻止我使用创建默认用户的启动代码(因为已经执行了安全检查) .

我最终通过拥抱 spring 安全性而不是试图规避它来解决这个问题。

我实现了这个实用程序:

import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

public class AsInternalUser implements AutoCloseable {

    private final SecurityContext previousContext;

    public AsInternalUser() {
        previousContext = SecurityContextHolder.getContext();
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(
                new AnonymousAuthenticationToken("INTERNAL","INTERNAL_USERNAME", AuthorityUtils.createAuthorityList("ROLE_INTERNAL"))
        );
        SecurityContextHolder.setContext(
                context
        );
    }

    @Override
    public void close() {
        if (previousContext == null) {
            SecurityContextHolder.clearContext();
        } else {
            SecurityContextHolder.setContext(previousContext);
        }
    }
}

我最初的用户创建因此变成:

try (AsInternalUser __ = new AsInternalUser()) {
            if (!users.exists(DEFAULT_ADMIN_NAME)) {
                users.save(new User(DEFAULT_ADMIN_NAME, passwordEncoder.encode(DEFAULT_ADMIN_PASSWORD), Arrays.asList(Roles.values())));
            }
        }

当然,存储库需要授予对新 ROLE_INTERNAL

的访问权限
@PreAuthorize("hasAnyRole('ROLE_USER_MANAGER', 'ROLE_INTERNAL')")
public interface UserRepository extends CrudRepository<User, String>, UserSignUpExtension {
}

用户注册等其他地方也必须升级为内部角色。

我认为这是比规避安全模型更好的方法,因为它允许更精细的粒度控制并减少意外调用不安全存储库和危及安全性的机会。