c7a8742a36
Source code headers
312 lines
13 KiB
XML
312 lines
13 KiB
XML
<!--
|
|
~ 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.
|
|
-->
|
|
|
|
<section id="spring-security-adapter">
|
|
<title>Spring Security Adapter</title>
|
|
<para>
|
|
To secure an application with Spring Security and Keycloak, add this adapter as a dependency to your project.
|
|
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>
|
|
|
|
<bean id="adapterDeploymentContext" class="org.keycloak.adapters.springsecurity.AdapterDeploymentContextFactoryBean">
|
|
<constructor-arg value="/WEB-INF/keycloak.json" />
|
|
</bean>
|
|
|
|
<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">
|
|
<constructor-arg ref="adapterDeploymentContext" />
|
|
</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>
|
|
<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>
|
|
<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>
|
|
<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>
|
|
</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>
|