2016-02-03 10:20:22 +00:00
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
2015-05-08 04:14:06 +00:00
<section id= "spring-security-adapter" >
<title > Spring Security Adapter</title>
<para >
2016-01-04 19:16:37 +00:00
To secure an application with Spring Security and Keycloak, add this adapter as a dependency to your project.
2015-05-08 04:14:06 +00:00
You then have to provide some extra beans in your Spring Security configuration file and add the Keycloak security
filter to your pipeline.
</para>
<para >
Unlike the other Keycloak Adapters, you should not configure your security in web.xml. However, keycloak.json is still required.
</para>
<section >
<title > Adapter Installation</title>
<para >
Add Keycloak Spring Security adapter as a dependency to your Maven POM or Gradle build.
</para>
<para >
<programlisting >
< ![CDATA[
<dependency >
<groupId > org.keycloak</groupId>
<artifactId > keycloak-spring-security-adapter</artifactId>
<version > &project.version; </version>
</dependency>
]]>
</programlisting>
</para>
</section>
<section >
<title > Spring Security Configuration</title>
<para >
The Keycloak Spring Security adapter takes advantage of Spring Security's flexible security configuration syntax.
</para>
<section >
<title > Java Configuration</title>
<para >
Keycloak provides a KeycloakWebSecurityConfigurerAdapter as a convenient base class for creating a
<ulink url= "http://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/config/annotation/web/WebSecurityConfigurer.html" > WebSecurityConfigurer</ulink>
instance. The implementation allows customization by overriding methods. While its use is not required, it greatly simplifies your security context configuration.
</para>
<para >
<programlisting >
< ![CDATA[
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
/**
* Defines the session authentication strategy.
*/
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http
.authorizeRequests()
.antMatchers("/customers*").hasRole("USER")
.antMatchers("/admin*").hasRole("ADMIN")
.anyRequest().permitAll();
}
}
]]>
</programlisting>
</para>
<para >
You must provide a session authentication strategy bean which should be of type
<code > RegisterSessionAuthenticationStrategy</code> for public or confidential applications and
<code > NullAuthenticatedSessionStrategy</code> for bearer-only applications.
</para>
<para >
Spring Security's <code > SessionFixationProtectionStrategy</code> is currently not supported because it changes
the session identifier after login via Keycloak. If the session identifier changes, universal log out will not
work because Keycloak is unaware of the new session identifier.
</para>
</section>
<section >
<title > XML Configuration</title>
<para >
While Spring Security's XML namespace simplifies configuration, customizing the configuration can be a bit
verbose.
</para>
<para >
<programlisting >
< ![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<context:component-scan base-package= "org.keycloak.adapters.springsecurity" />
<security:authentication-manager alias= "authenticationManager" >
<security:authentication-provider ref= "keycloakAuthenticationProvider" />
</security:authentication-manager>
2016-01-04 19:09:36 +00:00
<bean id= "adapterDeploymentContext" class= "org.keycloak.adapters.springsecurity.AdapterDeploymentContextFactoryBean" >
<constructor-arg value= "/WEB-INF/keycloak.json" />
</bean>
2015-05-08 04:14:06 +00:00
<bean id= "keycloakAuthenticationEntryPoint" class= "org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint" />
<bean id= "keycloakAuthenticationProvider" class= "org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider" />
<bean id= "keycloakPreAuthActionsFilter" class= "org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter" />
<bean id= "keycloakAuthenticationProcessingFilter" class= "org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter" >
<constructor-arg name= "authenticationManager" ref= "authenticationManager" />
</bean>
<bean id= "keycloakLogoutHandler" class= "org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler" >
2016-01-04 19:09:36 +00:00
<constructor-arg ref= "adapterDeploymentContext" />
2015-05-08 04:14:06 +00:00
</bean>
<bean id= "logoutFilter" class= "org.springframework.security.web.authentication.logout.LogoutFilter" >
<constructor-arg name= "logoutSuccessUrl" value= "/" />
<constructor-arg name= "handlers" >
<list >
<ref bean= "keycloakLogoutHandler" />
<bean class= "org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</list>
</constructor-arg>
<property name= "logoutRequestMatcher" >
<bean class= "org.springframework.security.web.util.matcher.AntPathRequestMatcher" >
<constructor-arg name= "pattern" value= "/sso/logout**" />
<constructor-arg name= "httpMethod" value= "GET" />
</bean>
</property>
</bean>
<security:http auto-config= "false" entry-point-ref= "keycloakAuthenticationEntryPoint" >
<security:custom-filter ref= "keycloakPreAuthActionsFilter" before= "LOGOUT_FILTER" />
<security:custom-filter ref= "keycloakAuthenticationProcessingFilter" before= "FORM_LOGIN_FILTER" />
<security:intercept-url pattern= "/customers**" access= "ROLE_USER" />
<security:intercept-url pattern= "/admin**" access= "ROLE_ADMIN" />
<security:custom-filter ref= "logoutFilter" position= "LOGOUT_FILTER" />
</security:http>
</beans>
]]>
</programlisting>
</para>
</section>
</section>
2016-01-04 19:09:36 +00:00
<section >
<title > Multi Tenancy</title>
<para >
The Keycloak Spring Security adapter also supports multi tenancy. Instead of injecting
<literal > AdapterDeploymentContextFactoryBean</literal> with the path to <literal > keycloak.json</literal> you
can inject an implementation of the <literal > KeycloakConfigResolver</literal> interface. More details on how
to implement the <literal > KeycloakConfigResolver</literal> can be found in <xref linkend= "multi_tenancy" /> .
</para>
</section>
2015-05-08 04:14:06 +00:00
<section >
<title > Naming Security Roles</title>
<para >
Spring Security, when using role-based authentication, requires that role names start with <code > ROLE_</code> .
For example, an administrator role must be declared in Keycloak as <code > ROLE_ADMIN</code> or similar, not simply
<code > ADMIN</code> .
</para>
2016-01-04 19:16:37 +00:00
<para >
The class <code > org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider</code>
supports an optional <code > org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper</code>
which can be used to map roles coming from Keycloak to roles recognized by Spring Security. Use, for example,
<code > org.springframework.security.core.authority.mapping.SimpleAuthorityMapper</code> to insert the
<code > ROLE_</code> prefix and convert the role name to upper case. The class is part of Spring Security
Core module.
</para>
2015-05-08 04:14:06 +00:00
</section>
<section >
<title > Client to Client Support</title>
<para >
To simplify communication between clients, Keycloak provides an extension of Spring's <code > RestTemplate</code> that
handles bearer token authentication for you. To enable this feature your security configuration must add the
<code > KeycloakRestTemplate</code> bean. Note that it must be scoped as a prototype to function correctly.
</para>
<para >
For Java configuration:
<programlisting >
< ![CDATA[
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
...
@Autowired
public KeycloakClientRequestFactory keycloakClientRequestFactory;
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KeycloakRestTemplate keycloakRestTemplate() {
return new KeycloakRestTemplate(keycloakClientRequestFactory);
}
...
}
]]>
</programlisting>
</para>
<para >
For XML configuration:
<programlisting >
< ![CDATA[
<bean id= "keycloakRestTemplate" class= "org.keycloak.adapters.springsecurity.client.KeycloakRestTemplate" scope= "prototype" >
<constructor-arg name= "factory" ref= "keycloakClientRequestFactory" />
</bean>
]]>
</programlisting>
</para>
<para >
Your application code can then use <code > KeycloakRestTemplate</code> any time it needs to make a call to another
client. For example:
<programlisting >
< ![CDATA[
@Service
public class RemoteProductService implements ProductService {
@Autowired
private KeycloakRestTemplate template;
private String endpoint;
@Override
public List<String > getProducts() {
ResponseEntity<String [ ] > response = template.getForEntity(endpoint, String[].class);
return Arrays.asList(response.getBody());
}
}
]]>
</programlisting>
</para>
</section>
<section >
<title > Spring Boot Configuration</title>
<para >
Spring Boot attempts to eagerly register filter beans with the web application context. Therefore,
when running the Keycloak Spring Security adapter in a Spring Boot environment, it may be necessary to add two
<code > FilterRegistrationBean</code> s to your security configuration to prevent the Keycloak filters from being
registered
twice.
</para>
<para >
<programlisting >
< ![CDATA[
@Configuration
@EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
...
@Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
...
}
]]>
</programlisting>
</para>
</section>
</section>