问题
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