此内容没有您所选择的语言版本。
Chapter 4. Spring Security with Red Hat Process Automation Manager
Spring Security is provided by a collection of servlet filters that make up the Spring Security library. These filters provide authentication through user names and passwords and authorization through roles. The default Spring Security implementation generated in a Red Hat Process Automation Manager Spring Boot application provides authorization without authentication. This means that anyone with a user name and password valid for the application can access the application without a role.
The servlet filters protect your Spring Boot application against common exploits such as cross-site request forgery (CSRF) and cross-origin resource sharing (CORS). Spring Web relies on the DispatcherServlet
to redirect incoming HTTP requests to your underlying java REST resources annotated with the @Controller
annotation. The DispatchServlet
is agnostic of elements such as security. It is good practice and more efficient to handle implementation details such a security outside of the business application logic. Therefore, Spring uses filters to intercept HTTP requests before routing them to the DispatchServlet
.
A typical Spring Security implementation consists of the following steps that use multiple servlet filters:
- Extract and decode or decrypt user credentials from the HTTP request.
- Complete authentication by validating the credentials against the corporate identity provider, for example a database, a web service, or Red Hat Single Sign-On.
- Complete authorization by determining whether the authorized user has access rights to perform the request.
-
If the user is authenticated and authorized, propagate the request to the
DispatchServlet
.
Spring breaks these steps down into individual filters and chains them together in a FilterChain. This chaining method provides the flexibility required to work with almost any identity provider and security framework. With Spring Security, you can define a FilterChain for your application programmatically. The following section is from the business-application-service/src/main/java/com/company/service/DefaultWebSecurityConfig.java
file generated as part of a business application created on the https://start.jbpm.org
web site.
@Configuration("kieServerSecurity") @EnableWebSecurity public class DefaultWebSecurityConfig extends WebSecurityConfigurerAdapter { @Override (1) protected void configure(HttpSecurity http) throws Exception { http .cors().and() .csrf().disable() (2) .authorizeRequests() (3) .antMatchers("/rest/*").authenticated().and() .httpBasic().and() (4) .headers().frameOptions().disable(); (5) }
-
(1) Overrides the default
configure(HttpSecurity http)
method and defines a custom FilterChain using the Spring HttpClient fluent API/DSL - (2) Disables common exploit filters for CORS and CSRF tokens for local testing
- (3) Requires authentication for any requests made to the pattern 'rest/*' but no roles are defined
- (4) Allows basic authentication through the authorization header, for example header 'Authorization: Basic dGVzdF91c2VyOnBhc3N3b3Jk'
- (5) Removes the 'X-Frame-Options' header from request/response
This configuration allows any authenticated user to execute the KIE API.
Because the default implementation is not integrated into any external identity provider, users are defined in memory, in the same DefaultWebSecurityConfg
class. The following section shows the users that are provided when you create a Red Hat Process Automation Manager Spring Boot business application:
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("user").password("user").roles("kie-server"); auth.inMemoryAuthentication().withUser("wbadmin").password("wbadmin").roles("admin"); auth.inMemoryAuthentication().withUser("kieserver").password("kieserver1!").roles("kie-server"); }
4.1. Using Spring Security to authenticate with authorization
By default, anyone with a user name and password valid for the Red Hat Process Automation Manager Spring Boot application can access the application without requiring a role. Spring Security authentication and authorization are derived from the HTTPSecurity
filter chain configuration. To protect the REST API from users that do not have a specific role mapping, use the Spring Security .authorizeRequests()
method to match the URLs that you want to authorize.
Prerequisites
- You have a Red Hat Process Automation Manager Spring Boot application.
Procedure
-
In the directory that contains your Red Hat Process Automation Manager Spring Boot application, open the
business-application-service/src/main/java/com/company/service/DefaultWebSecurityConfig.java
file in a text editor or IDE. To authorize requests for access by an authenticated user only if they have a specific role, edit the
.antMatchers("/rest/*").authenticated().and()
line in one of the following ways:To authorize for a single role, edit the
antMatchers
method as shown in the following example, where<role>
is the role that that the user must have for access:@Configuration("kieServerSecurity") @EnableWebSecurity public class DefaultWebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .cors().and().csrf().disable() .authorizeRequests() .antMatchers("/**").hasRole("<role>") .anyRequest().authenticated() .and().httpBasic() .and().headers().frameOptions().disable(); } ...
To authorize a user that has one of a range of roles, edit the
antMatchers
method as shown in the following example, where<role>
and<role1>
are each roles the user can have for access:@Configuration("kieServerSecurity") @EnableWebSecurity public class DefaultWebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .cors().and().csrf().disable() .authorizeRequests() .antMatchers("/**").hasAnyRole("<role>", "<role1") .anyRequest().authenticated() .and().httpBasic() .and().headers().frameOptions().disable(); } ...
The authorizeRequests
method requires authorization of requests for a specific expression. All requests must be successfully authenticated. Authentication is performed using HTTP basic authentication. If an authenticated user tries to access a resource that is protected for a role that they do not have, the user receives an HTTP 403 (Forbidden)
error.
4.2. Disabling Spring Security in a Red Hat Process Automation Manager business application
You can configure Spring Security in a Red Hat Process Automation Manager business application to provide the security context without authentication.
Prerequisites
- You have a Red Hat Process Automation Manager Spring Boot application.
Procedure
-
In the directory that contains your Red Hat Process Automation Manager Spring Boot application, open the
business-application-service/src/main/java/com/company/service/DefaultWebSecurityConfig.java
file in a text editor or integrated development environment (IDE). Edit the
.antMatchers
method as shown in the following example:@Override protected void configure(HttpSecurity http) throws Exception { http .cors().and().csrf().disable() .authorizeRequests() .antMatchers("/*") .permitAll() .and().headers().frameOptions().disable(); }
The
PermitAll
method allows any and all requests for the specified URL pattern.
Because no security context is passed in the HttpServletRequest
, Spring creates an AnonymousAuthenticationToken
and populates the SecurityContext
with the anonymousUser
user with no designated roles other than the ROLE_ANONYMOUS
role. The user will not have access to many of the features of the application, for example they will be unable to assign actions to group assigned tasks.
4.3. Using Spring Security with preauthenication
If you disable Spring Security authentication by using the PermitAll
method, any user can log in to the application, but users will have limited access and functionality. However, you can preauthenticate a user, for example a designated service account, so a group of users can use the same login but have all of the permissions that they require. That way, you do not need to create credentials for each user.
The easiest way to implement preauthentication is to create a custom filter servlet and add it before the security FilterChain in the DefaultWebSecurityConfig
class. This way, you can inject a customized, profile-based security context, control its contents, and keep it simple.
Prerequisites
- You have a Red Hat Process Automation Manager Spring Boot application and you have disabled Spring Security as Section 4.2, “Disabling Spring Security in a Red Hat Process Automation Manager business application”.
Procedure
Create the following class that extends the
AnonymousAuthenticationFilter
class:import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; public class <CLASS_NAME> extends AnonymousAuthenticationFilter { private static final Logger log = LoggerFactory.getLogger(<CLASS_NAME>.class); public AnonymousAuthFilter() { super("PROXY_AUTH_FILTER"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req)); log.info("SecurityContextHolder pre-auth user: {}", SecurityContextHolder.getContext()); if (log.isDebugEnabled()) { log.debug("Populated SecurityContextHolder with authenticated user: {}", SecurityContextHolder.getContext().getAuthentication()); } chain.doFilter(req, res); } @Override protected Authentication createAuthentication(final HttpServletRequest request) throws AuthenticationException { log.info("<ANONYMOUS_USER>"); List<? extends GrantedAuthority> authorities = Collections .unmodifiableList(Arrays.asList(new SimpleGrantedAuthority("<ROLE>") )); return new AnonymousAuthenticationToken("ANONYMOUS", "<ANONYMOUS_USER>", authorities); } }
Replace the following variables:
-
Replace
<CLASS_NAME>
with a name for this class, for exampleAnonymousAuthFilter
. -
Replace
<ANONYMOUS_USER>
with a user ID, for exampleService_Group
. -
Replace
<ROLE>
with the role that has the privileges that you want to give to<ANONYMOUS_USER>
.
-
Replace
If you want to give
<ANONYMOUS_USER>
more than one role, add additional roles as shown in the following example:.unmodifiableList(Arrays.asList(new SimpleGrantedAuthority("<ROLE>") , new SimpleGrantedAuthority("<ROLE2>")
Add
.anonymous().authenticationFilter(new <CLASS_NAME>()).and()
to thebusiness-application-service/src/main/java/com/company/service/DefaultWebSecurityConfig.java
file, where<CLASS_NAME>
is the name of the class that you created:@Override protected void configure(HttpSecurity http) throws Exception { http .anonymous().authenticationFilter(new <CLASS_NAME>()).and() // Override anonymousUser .cors().and().csrf().disable() .authorizeRequests() .antMatchers("/*").permitAll() .and().headers().frameOptions().disable(); }
4.4. Configuring the business application with Red Hat Single Sign-On
Most organizations provide user and group details through single sign-on (SSO) tokens. You can use Red Hat Single Sign-On (RHSSO) to enable single sign-on between your services and to have a central place to configure and manage your users and roles.
Prerequisites
- You have a Spring Boot application ZIP file that you created using the business applications website.
Procedure
- Download and install RHSSO. For instructions, see the Red Hat Single Sign-On Getting Started Guide.
Configure RHSSO:
Either use the default master realm or create a new realm.
A realm manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control.
-
Create the
springboot-app
client and set theAccessType
to public. Set a valid redirect URI and web origin according to your local setup, as shown in the following example:
-
Valid redirect URIs:
http://localhost:8090/*
-
Web origin:
http://localhost:8090
-
Valid redirect URIs:
- Create realm roles that are used in the application.
- Create users that are used in the application and assign roles to them.
Add the following element and property to the Spring Boot project
pom.xml
file, where<KEYCLOAK_VERSION>
is the version of Keycloak that you are using:<properties> <version.org.keycloak><KEYCLOAK_VERSION></version.org.keycloak> </properties>
Add the following dependencies to the Spring Boot project
pom.xml
file:<dependencyManagement> <dependencies> <dependency> <groupId>org.keycloak.bom</groupId> <artifactId>keycloak-adapter-bom</artifactId> <version>${version.org.keycloak}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> .... <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-spring-boot-starter</artifactId> </dependency>
In your Spring Boot project directory, open the
business-application-service/src/main/resources/application.properties
file and add the following lines:# keycloak security setup keycloak.auth-server-url=http://localhost:8100/auth keycloak.realm=master keycloak.resource=springboot-app keycloak.public-client=true keycloak.principal-attribute=preferred_username keycloak.enable-basic-auth=true
Modify the
business-application-service/src/main/java/com/company/service/DefaultWebSecurityConfig.java
file to ensure that Spring Security works correctly with RHSSO:import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; @Configuration("kieServerSecurity") @EnableWebSecurity public class DefaultWebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http .csrf().disable() .authorizeRequests() .anyRequest().authenticated() .and() .httpBasic(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); SimpleAuthorityMapper mapper = new SimpleAuthorityMapper(); mapper.setPrefix(""); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(mapper); auth.authenticationProvider(keycloakAuthenticationProvider); } @Bean public KeycloakConfigResolver KeycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } }