如何使用KeyCloak通过基于令牌的访问实现简单的Web服务?

时间:2019-03-07 10:48:41

标签: java spring spring-boot keycloak

我想创建一个可通过以下方式工作的Web服务:

  1. 客户端污染KeyCloak并提供用户名和密码。

  2. KeyCloak将访问令牌返回给客户端。

  3. 使用该访问令牌,客户端可以访问受KeyCloak保护的页面。

我该怎么办?

我尝试过的事情

步骤1:客户端获取访问令牌

private String getAccessToken() throws IOException {
    final CloseableHttpClient client = HttpClients.createDefault();

    final List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(4);
    nameValuePairs.add(new BasicNameValuePair("client_id", "test-app"));
    nameValuePairs.add(new BasicNameValuePair("username", "user1"));
    nameValuePairs.add(new BasicNameValuePair("password", "user1"));
    nameValuePairs.add(new BasicNameValuePair("grant_type", "password"));


    try {
        final HttpPost post = new HttpPost("http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token");
        post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        post.setHeader("Accept", "application/json");
        post.setHeader("Content-type", "application/x-www-form-urlencoded");

        final CloseableHttpResponse response = client.execute(post);

        final int statusCode = response.getStatusLine().getStatusCode();

        System.out.println(String.format("Result: %d", statusCode));

        if (statusCode != 200) {
            System.out.println("Something went wrong");
            return null;
        }

        final String responseTxt = EntityUtils.toString(response.getEntity());
        final JSONObject json = new JSONObject(responseTxt);

        final String accessToken = json.getString("access_token");
        return accessToken;
    } finally {
        try {
            client.close();
        } catch (final IOException exception) {
            exception.printStackTrace();
        }
    }
}

当我在正文中获得响应代码200和访问令牌时,这似乎起作用。

第2步:访问受保护的Web服务

该Web服务在http://127.0.0.1:8090/test上运行。

我正在使用以下代码对其进行访问(accessToken是上一步的访问令牌)

    final String targetUrl = "http://127.0.0.1/test";
    final CloseableHttpClient client = HttpClients.createDefault();
    final HttpGet get = new HttpGet(targetUrl);
    get.setHeader("Authorization", String.format("Bearer%s", accessToken));
    final CloseableHttpResponse response = client.execute(get);
    final int statusCode = response.getStatusLine().getStatusCode();
    System.out.println(String.format("Result: %d", statusCode));
    client.close();

这样做时,我得到的是KeyCloak的登录页面,而不是Web服务的响应。

如何配置Web服务?

这是实际的Web服务:

@RestController
public class SampleRestController {
    @RequestMapping("/test")
    public String test() {
        return "Hello, world!";
    }
}

安全配置:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    @Autowired
    public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {

        KeycloakAuthenticationProvider keycloakAuthenticationProvider
                = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(
                new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }
    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }


    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(
                new SessionRegistryImpl());
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .antMatchers("/test*")
                .hasRole("user")
                .anyRequest()
                .permitAll();
    }
}

application.properties

server.port = 8090

spring.main.allow-bean-definition-overriding=true


keycloak.realm = myrealm 
keycloak.auth-server-url = http://127.0.0.1:8080/auth
keycloak.ssl-required = external
keycloak.resource = test-app
keycloak.public-client=true
keycloak.security-constraints[0].authRoles[0]=user
keycloak.security-constraints[0].securityCollections[0].patterns[0]=/test/*

要获得“你好,世界!”,我需要更改什么?发送标有访问令牌的请求时响应如何?

更新1:我发现了问题的一部分-客户端配置错误。应该是bearer only

Screenshot

但是现在我得到了响应Bearer-only applications are not allowed to initiate browser login

更新2:通过以下服务配置,它可以正常工作。

server.port = 8090
spring.main.allow-bean-definition-overriding=true
keycloak.realm = myrealm
keycloak.resource = test-app
keycloak.auth-server-url = http://127.0.0.1:8080/auth
keycloak.bearer-only = true
keycloak.ssl-required = external
keycloak.principal-attribute = preferred_username

1 个答案:

答案 0 :(得分:0)

您使用了不正确的流或目的地。如果您想使用登录名/密码,则可以选择其中两个:

  1. 电流。您尝试访问,服务器检查您的登录名,如果不是,请-重定向到keycloak。登录后,keycloak使用访问码(不是令牌)将您重定向到您的页面(或登录页面,我不记得了)。之后,您的服务会将此代码发送到keycloak并执行其他操作

  2. 将流程更改为密码流程。在这种情况下,您会将用户名/密码发送到您的服务(而不是Keycloak),您的服务将此凭据发送到Keycloak,然后再做其他事情

更新:

我更喜欢让用户使用第一本书,但是如果您需要通过其他程序登录,则最好使用其他机制。 ClientCredentials或角色范围小的自定义用户

UPDATE2:

如果您确实需要使用身份验证令牌,则可以实现自定义AccessTokenProvider并使用UserInfoRestTemplateCustomizer自定义OAuth2RestTemplate。但这并不容易