Spring app basic auth over keycloack

限于喜欢 提交于 2020-02-02 10:21:08

问题


I need to add auth to my spring boot (MVC) app. Auth provider is keycloak via OpenID. Both Implicit and Authorization Code grants are disabled, so I am stuck with Resource owner credentials grant. What I want to achieve is basic auth prompt for unauthorized users. Credentials retrieved that way should be used to get token and user information from keycloak for its further usage by spring security. The token should be checked on each request.

Most examples I've found are using the redirect feature of org.keycloak:keycloak-spring-boot-starter. Though I've found enable-basic-auth here, It's not working for me. I am aware that I must use keycloak.bearer-only=true to turn off redirects, but it works so instead of redirecting it returns 401 for unauthorized users.

My security config:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
            .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .anyRequest().permitAll();
    }
}

My keycloak properties (I don't use placeholders, it's just for the sake of security):

keycloak.auth-server-url=https://${keycloak.host}/auth/
keycloak.realm=master
keycloak.resource=${client.name}
keycloak.enable-basic-auth=true
keycloak.credentials.secret=${client.secret}

Sorry for the general question. I've mostly used jdbcAuthentication and it's my first experience with Identity Management software.


回答1:


Here is one way to add basic auth to your application over keycloak openid with Resource owner password credentials flow.

First of all you'll need to add this to pom.xml:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.keycloak.bom</groupId>
            <artifactId>keycloak-adapter-bom</artifactId>
            <version>4.2.0.Final</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

...

<dependencies>
...
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-boot-starter</artifactId>
        </dependency>
...
</dependencies>

Add keycloak server properties:

keycloak.auth-server-url=https://${keycloak.host}/auth/
keycloak.realm=master
keycloak.resource=${client.name}
keycloak.credentials.secret=${client.secret}
keycloak.enable-basic-auth=true
keycloak.bearer-only=true

enable-basic-auth is actually responsible for enabling resource owner password credentials flow. So, every time you'll send request with Basic Authorization BasicAuthRequestAuthenticator will request token from keycloak. bearer-only is needed to turn off access code flow. Every time you'd make a request to secured resource without basic authorization (and outside authotized session) you'd get redirected to keycloak server - we don't want that. With bearer-only you'll get 401.

And last step is adding security configuration:

@Configuration
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
@EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Bean
    public KeycloakAuthenticationProvider authenticationProvider() {
        KeycloakAuthenticationProvider provider = new KeycloakAuthenticationProvider();
        provider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        return provider;
    }

    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
                .csrf().disable()
                .exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
                    response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
                    response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
                })
                .and()
                .authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .anyRequest().permitAll();
    }
}

Most interesting part here is this:

        .exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
            response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
            response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
        })

Standard keycloak AuthenticationEntryPoint implementation is setting WWW-Authenticate header to String.format("Bearer realm=\"%s\"", realm) in case of authorization failure. I need it to be set to Basic realm="Restricted Content" for the basic auth prompt to pop up. If you want to avoid using this prompt (you want to add your own login form) - remove this part.



来源:https://stackoverflow.com/questions/51725873/spring-app-basic-auth-over-keycloack

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!