OAuth2客户端凭据通过Spring Boot Keycloak集成进行流动 [英] OAuth2 client credentials flow via Spring Boot Keycloak integration

查看:140
本文介绍了OAuth2客户端凭据通过Spring Boot Keycloak集成进行流动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序包括:

  • 后端/资源服务器
  • UI webapp
  • 钥匙斗篷

UI正在使用具有授权代码授予流程的keycloak客户端通过RESTful API与后端服务器进行通信.一切正常.

The UI is talking with the backend server via RESTful API using the keycloak client with authorization code grant flow. This is working fine.

现在,我需要使用系统/服务帐户(通常具有比用户更多的权限)访问后端资源的其他可能性.您将如何实施此要求?我认为客户端凭据流在这里会很有用.

Now, I need the additional possibility to access resource of the backend using a system/service account (with usually more permissions than the user). How would you implement this requirement? I thought the client credentials flow would be useful here.

是否可以将OAuth2客户端凭据流与keycloak客户端一起用于Spring Boot?我发现了一些示例,这些示例使用Spring Security OAuth2客户端功能来实现客户端凭证流,但是这感觉很奇怪,因为我已经将密钥斗篷客户端用于OAuth.

Is it possible to use the OAuth2 client credentials flow with the keycloak client for Spring Boot? I found examples that used the Spring Security OAuth2 client features to achieve a client credentials flow but that feels weird because I already use the keycloak client for the OAuth thing.

感谢您的回答,这对我很有帮助.现在,在我的UI Web应用程序中,我可以通过使用经过身份验证的用户OAuth2令牌或使用我的UI服务帐户的客户端凭据流中的令牌与后端进行通信.每种方法都有自己的RestTemplate,第一种方法是通过密钥斗篷集成完成的,第二种是由Spring Security OAuth2完成的,如

Thanks for your answers which helped me a lot. In my UI webapp, I am now able to communicate with the backend either by using the authenticated user OAuth2 token or by using the token from the client credentials flow of my UI service account. Each way has its own RestTemplate, the first is done via the keycloak integration and second is done by Spring Security OAuth2 as explained here.

推荐答案

在@ dmitri-algazin实施工作流之后,您基本上有两种选择:

Following @dmitri-algazin you to implement the workflow you have basically two options:

  1. 如果您想涵盖除Keycloak之外的其他IDM,以某种方式解决单一责任原则,我会使用RestTemplate.您可以在下面找到变量:
  1. If you want to cover other IdMs besides Keycloak which solves somehow the Single Responsibility principle, I would use RestTemplate. Below you can find the variables:

    //Constants
    @Value("${keycloak.url}")
    private String keycloakUrl;

    @Value("${keycloak.realm}")
    private String keycloakRealm;

    @Value("${keycloak.client_id}")
    private String keycloakClientId;

    RestTemplate restTemplate = new RestTemplate();
    private static final String BEARER = "BEARER ";

首先,您需要生成访问令牌:

First you need to generate the access token:

    @Override
    public AccessTokenResponse login(KeycloakUser user) throws NotAuthorizedException {
        try {
            String uri = keycloakUrl + "/realms/" + keycloakRealm + 
                    "/protocol/openid-connect/token";
            String data = "grant_type=password&username="+
                    user.getUsername()+"&password="+user.getPassword()+"&client_id="+
                    keycloakClientId;

            HttpHeaders headers = new HttpHeaders();
            headers.set("Content-Type", "application/x-www-form-urlencoded");

            HttpEntity<String> entity = new HttpEntity<String>(data, headers);
            ResponseEntity<AccessTokenResponse> response = restTemplate.exchange(uri, 
                    HttpMethod.POST, entity, AccessTokenResponse.class);            

            if (response.getStatusCode().value() != HttpStatus.SC_OK) {
                log.error("Unauthorised access to protected resource", response.getStatusCode().value());
                throw new NotAuthorizedException("Unauthorised access to protected resource");
            }
            return response.getBody();
        } catch (Exception ex) {
            log.error("Unauthorised access to protected resource", ex);
            throw new NotAuthorizedException("Unauthorised access to protected resource");
        } 
    }

然后使用令牌,您可以从用户那里检索信息:

And then with the token you can retrieve information from the users:

    @Override
    public String user(String authToken) throws NotAuthorizedException {

        if (! authToken.toUpperCase().startsWith(BEARER)) {
            throw new NotAuthorizedException("Invalid OAuth Header. Missing Bearer prefix");
        }

        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", authToken);

        HttpEntity<String> entity = new HttpEntity<>(headers);

        ResponseEntity<AccessToken> response = restTemplate.exchange(
                keycloakUrl + "/realms/" + keycloakRealm + "/protocol/openid-connect/userinfo", 
                HttpMethod.POST, 
                entity, 
                AccessToken.class);

        if (response.getStatusCode().value() != HttpStatus.SC_OK) {
            log.error("OAuth2 Authentication failure. "
                    + "Invalid OAuth Token supplied in Authorization Header on Request. Code {}", response.getStatusCode().value());
            throw new NotAuthorizedException("OAuth2 Authentication failure. "
                    + "Invalid OAuth Token supplied in Authorization Header on Request.");
        }

        log.debug("User info: {}", response.getBody().getPreferredUsername());
        return response.getBody().getPreferredUsername();
    }

您可以将该URL替换为@ dimitri-algazin提供的URL,以检索所有用户信息.

You can substitute this URL by the one provided by @dimitri-algazin to retrieve all the users information.

  1. 可以使用Keycloak依赖项:

        <!-- keycloak -->
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-admin-client</artifactId>
            <version>3.4.3.Final</version>
        </dependency>

        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <version>3.1.4.Final</version>
        </dependency>

并使用这些类来生成令牌:

And use the classes to generate the token:

            Keycloak keycloak = KeycloakBuilder
                    .builder()
                    .serverUrl(keycloakUrl)
                    .realm(keycloakRealm)
                    .username(user.getUsername())
                    .password(user.getPassword())
                    .clientId(keycloakClientId)
                    .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build())
                    .build();

            return keycloak.tokenManager().getAccessToken();

示例摘自此处.我们还将图像上传到Docker Hub ,以促进与Keycloak的交互.因此,我们从选项2)开始.目前,我们正在覆盖其他IdM,我们选择了选项1),以避免包括额外的依赖项.结论:

The examples are extracted from here. We also uploaded the image to Docker Hub to facilitate the interaction with Keycloak. For this reason we started with option 2). Right now we are in the process to cover other IdMs and we went for option 1) in order to avoid including extra dependencies. Conclusion:

如果您坚持使用Keycloak,我会选择选项2 ,因为类包含Keycloak工具的额外功能. 我将使用选项1 进行进一步介绍,并使用其他OAuth 2.0工具.

I would go for option 2 if you stick to Keycloak because classes include extra functionalities for Keycloak tool. I would go for option 1 for further coverage and other OAuth 2.0 tools.

这篇关于OAuth2客户端凭据通过Spring Boot Keycloak集成进行流动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆