具有授权的Keycloak自定义SPI REST端点

问题描述 投票:0回答:4

我正在尝试使用自定义 REST 端点创建自定义 SPI,该端点应通过评估所请求资源的权限来验证和授权传入请求。 在调试器的帮助下,我发现我应该使用类 TokenEndpoint.java 并在 REST 处理程序方法中调用方法 PermissionGrant() ,但是当我尝试创建 TokenEndpoint 的实例时,出现 REASTEASY 错误和 Keycloak 崩溃。 你有什么例子吗,我该怎么做?

keycloak keycloak-services
4个回答
6
投票

我建议看看以下项目:keycloak-avatar-minio-extension

首先您必须实现

RealmResourceProdiverFactory
RealmResourceProcider

其次,您需要一个在触发

getResource()
中的
RealmResourceProvider
时返回的资源。

您的资源是您在其中定义端点的类。 要检查授权,您可以创建如下方法:

private AuthenticationManager.AuthResult resolveAuthentication(KeycloakSession session) {
    AppAuthManager appAuthManager = new AppAuthManager();
    RealmModel realm = session.getContext().getRealm();

    AuthenticationManager.AuthResult authResult = appAuthManager.authenticateIdentityCookie(session, realm);
    if (authResult != null) {
        return authResult;
    }

    return null;
}

此方法在构造函数中调用,并在资源中设置

private final AuthenticationManager.AuthResult auth;
变量。

现在,在端点实现中,您可以简单地检查

auth
是否不为空,或者,如果需要,执行更复杂的操作,例如检查
auth
变量中可用的用户或令牌。


4
投票

和其他人一样,我需要在我们放入 keycloak 实例的自定义休息端点中使用它。

我分步骤解决了这个问题:

  • 获取给定类型的资源
  • 获取这些资源的权限评估器
  • 评估、过滤并返回响应

这是代码:

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.ws.rs.GET;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.common.DefaultEvaluationContext;
import org.keycloak.authorization.common.UserModelIdentity;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.permission.evaluator.Evaluators;
import org.keycloak.authorization.permission.evaluator.PermissionEvaluator;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager.AuthResult;

public class DemoResource {
    
    private final KeycloakSession session;
    private final AuthResult auth;
    
    public DemoResource(KeycloakSession session) {
        this.session = session;
        this.auth = new AppAuthManager.BearerTokenAuthenticator(session).authenticate();
    }
    
    @GET
    @Path("/demoresources")
    @Produces(MediaType.APPLICATION_JSON)
    public Set<Resource> listDemoResources() {
        if (this.auth == null || this.auth.getToken() == null) {
            throw new NotAuthorizedException("Bearer");
        }
        
        String clientId = ""; // Client id which resources are defined.
        String resourceType = ""; // Get resources by type.
        
        final RealmModel realm = this.session.getContext().getRealm();
        final AuthorizationProvider authorizationProvider = this.session.getProvider(AuthorizationProvider.class);
        final ClientModel client = this.session.clientStorageManager().getClientByClientId(realm, clientId);
        final ResourceServer resourceServer = authorizationProvider
            .getStoreFactory()
            .getResourceServerStore()
            .findById(client.getId());
        final Evaluators evaluators = authorizationProvider.evaluators();
        
        final AuthorizationRequest request = new AuthorizationRequest();
        request.setSubjectToken(this.auth.getToken().toString());
        
        // Get resources by type and put them in a map
        final Map<String, Resource> resourceMap = authorizationProvider
            .getStoreFactory()
            .getResourceStore()
            .findByType(resourceType, resourceServer.getId())
            .stream()
            .collect(Collectors.toMap(Resource::getId, r -> r));
        
        // Generate a permission evaluator for all resources of given type
        final PermissionEvaluator permissionEvaluator = evaluators
            .from(
                resourceMap
                    .entrySet()
                    .stream()
                    .map(r -> new ResourcePermission(r.getValue(), Collections.emptyList(), resourceServer))
                    .collect(Collectors.toList()),
                new DefaultEvaluationContext(new UserModelIdentity(realm, this.auth.getUser()), this.session));
        
        // Evaluate permission and put them in a result set.
        final Collection<Permission> permissions = permissionEvaluator.evaluate(resourceServer, request);
        final Set<Resource> resources = new HashSet<>();
        for (final Permission permission : permissions) {
            if (resourceMap.containsKey(permission.getResourceId())) {
                resources.add(resourceMap.get(permission.getResourceId()));
            }
        }
        return resources;
    }
}

提供商

import org.keycloak.models.KeycloakSession;
import org.keycloak.services.resource.RealmResourceProvider;

public class DemoProvider implements RealmResourceProvider {
    
    private KeycloakSession session;
    
    public DemoProvider(KeycloakSession session) {
        this.session = session;
    }
    
    @Override
    public void close() {
    }
    
    @Override
    public Object getResource() {
        return new DemoResource(this.session);
    }
}

提供者工厂

import org.keycloak.Config.Scope;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resource.RealmResourceProviderFactory;

public class DemoProviderFactory implements RealmResourceProviderFactory {
    
    public static final String ID = "demo";
    
    @Override
    public RealmResourceProvider create(KeycloakSession session) {
        return new DemoProvider(session);
    }
    
    @Override
    public void init(Scope config) {
    }
    
    @Override
    public void postInit(KeycloakSessionFactory factory) {
    }
    
    @Override
    public void close() {
    }
    
    @Override
    public String getId() {
        return ID;
    }
}


0
投票

我最近也遇到了同样的问题。一般来说,瓦尔的答案是正确的。 为了让他的代码正常工作,我们必须添加对 keycloak-services 模块的依赖。您需要编译时依赖项(不要将 jar 添加到 Ear 库中)和模块依赖项。 如何做到这一点的一个很好的例子是https://github.com/dteleguin/beercloak

看点:

  1. https://github.com/dteleguin/beercloak/blob/master/beercloak-ear/src/main/application/META-INF/jboss-deployment-struct.xml
  2. https://github.com/dteleguin/beercloak/blob/master/beercloak-module/src/main/java/beercloak/resources/AbstractAdminResource.java - 设置方法
  3. https://github.com/dteleguin/beercloak/blob/master/beercloak-module/src/main/java/beercloak/providers/BeerResourceProvider.java - getResource方法

在新版本中,身份验证在构造函数中不起作用,必须在

getResource()
方法中完成。

© www.soinside.com 2019 - 2024. All rights reserved.