migrate to keycloak from spring boot security - spring-boot

i want to migrate to keycloak from my old spring boot security app.Below is my security config.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/*", "/static/**", "/css/**", "/js/**", "/images/**").permitAll()
.antMatchers("/school-admin/*").hasAuthority("SCHOOL_ADMIN").anyRequest().fullyAuthenticated()
.antMatchers("/teacher/*").hasAuthority("TEACHER").anyRequest().fullyAuthenticated()
.anyRequest().authenticated().and()
.formLogin()
.loginPage("/login.html").defaultSuccessUrl("/loginSuccess.html")
.failureUrl("/login.html?error").permitAll().and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout.html")).logoutSuccessUrl("/login.html?logout");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
I have already installed the keycloak and it is running on port 8080.The problem I found out that, we should create role and user on keycloak admin page, But what my current system is, users and roles are on my MySQL DB. I don't want to insert the users and roles on keycloak for authentication and authorization.

Ok, obviously the first thing is a running keycloak instance, I assume this should be doable with the online documentation. We use i.e. Keycloak on a Wildfly instance.
Next step is to define a realm and at least one client in keycloak that you will use to connect to with your spring-boot application. In you application's POM you will need to add dependencies for a keylcoak adapter like i.e.
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-tomcat8-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-adapter</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
The rest can be done in your application.properties, that's the place where you configure how the adapter connects to keycloak and which parts of your application should be secured. This can look like
keycloak.realm=myrealm #realm that you have created in keycloak, contains your client
keycloak.auth-server-url=KeycloakHOST:KeycloakPort/auth # Substitute with your settings
keycloak.ssl-required=none
keycloak.resource=myclient
#keycloak.use-resource-role-mappings=true
keycloak.enable-basic-auth=true # we use basic authentication in this example
keycloak.credentials.secret=2dcf74ca-4e4f-44bf-9774-6c32c12783d3 # Secret generated for you client in keycloak
keycloak.cors=true
keycloak.cors-allowed-headers=x-requested-with,origin,content-type,accept,authorization
keycloak.cors-allowed-methods=GET,POST,DELETE,PUT,OPTIONS
keycloak.cors-max-age=3600
keycloak.expose-token=true
keycloak.bearer-only=true
keycloak.securityConstraints[0].securityCollections[0].name=adminRule
keycloak.securityConstraints[0].securityCollections[0].authRoles[0]=SCHOOL_ADMIN
keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/school-admin/*
keycloak.securityConstraints[1].securityCollections[0].name=teacherRule
keycloak.securityConstraints[1].securityCollections[0].authRoles[0]=TEACHER
keycloak.securityConstraints[1].securityCollections[0].patterns[0]=/teacher/*
That's basically all you need to do in your spring-boot application. All other endpoints not covered by the rules above remain available to all. You can find a pretty good tutorial on that here that is the longer version what I have described.

Related

How do I use multiple 'JWK Set Uri' values in the same Spring Boot app?

I have a requirement to use two different authorization servers (two Okta instances) to validate authentication tokens coming from two different web applications inside a single Spring Boot application which is a back-end REST API layer.
Currently I have one resource server working with the following configuration:
#Configuration
#EnableWebSecurity
public class ResourceServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests().antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
}
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-X.okta.com/oauth2/default
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://dev-X.okta.com/oauth2/default/v1/keys
and with dependencies spring-security-oauth2-resource-server and spring-security-oauth2-jose in my Spring Boot app (version 2.2.4.RELEASE)
The end state I want to get into is, depending on a custom HTTP header set in the request, I want to pick which Okta instance my Spring Boot app uses to decode and validate the JWT token.
Ideally I would have two properties in my configuration file as follows:
jwkSetUri.X=https://dev-X.okta.com/oauth2/default/v1/keys
jwtIssuerUri.X=https://dev-X.okta.com/oauth2/default
jwkSetUri.Y=https://dev-Y.okta.com/oauth2/default/v1/keys
jwtIssuerUri.Y=https://dev-Y.okta.com/oauth2/default
I should be able to use a RequestHeaderRequestMatcher to match the header value in the security configuration. What I cannot workout is how to use two different oauth2ResourceServer instances that goes with the security configuration.
With spring boot this is not possible to do out of the box right now.
Spring Security 5.3 provides functionality to do this (spring boot 2.2.6 still doesn't support spring security 5.3).
Please see following issues:
https://github.com/spring-projects/spring-security/issues/7857
https://github.com/spring-projects/spring-security/pull/7887
It is possible to do manual configuration of resource server to use multiple identity providers, by following links that i have provided. Provided links are mainly for spring boot webflux development. For basic spring boot web development please see this video:
https://www.youtube.com/watch?v=ke13w8nab-k
This is possible as of Spring security 5.3+ using the JwtIssuerAuthenticationManagerResolver object
Override the configure(HttpSecurity http) inside your configuration class which extends WebSecurityConfigurerAdapter
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
"http://localhost:8080/auth/realms/SpringBootKeyClock",
"https://accounts.google.com/o/oauth2/auth",
"https://<subdomain>.okta.com/oauth2/default"
);
http.cors()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/user/info", "/api/foos/**")
.hasAnyAuthority("SCOPE_email")
.antMatchers(HttpMethod.POST, "/api/foos")
.hasAuthority("SCOPE_profile")
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver));

Spring boot 2.1.x how to secure Actuator end points with basic auth

I am trying to build a spring boot application and wanted to leverage the Actuator features, but I want to secure the end points of Actuator /health,/shutdown etc. I have the below configurations which does not seem to work. I.e., application never prompts for credentials or when hit from post man does not give 403. I tried various ways, even the one from spring documentation. Can any one please help with this. Again this is spring boot 2.1.x. I know there is a configuration that can be made in application.yml in the previous version of spring
#Configuration
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(EndpointRequest.to(ShutdownEndpoint.class, InfoEndpoint.class, HealthEndpoint.class,
MetricsEndpoint.class))
.hasRole("ENDPOINT_ADMIN").requestMatchers(PathRequest.toStaticResources().atCommonLocations())
.authenticated().and().httpBasic();
}
application.yml
spring:
security:
user:
name: admin
password: admin
roles:
- ENDPOINT_ADMIN
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
health:
show-details: when-authorized
roles:
- ENDPOINT_ADMIN
mappings:
enabled: true
This code can serve you as a reference to achieve BasicAuth for Actuator Endpoints Spring Boot 2.X. This is not the exact code. While Extending WebSecurityConfigurerAdapter you have to configure AuthenticationManagerBuilder to assign roles and passwords for the roles. Here I am using "noop" password encoder you can use a different one to provide more security.
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("ROLE_USER").password("{noop}" + "USER_PWD").roles("USER").and()
.withUser("ROLE_ADMIN").password("{noop}" + "ADMIN").roles("ADMIN", "USER");
}
Once AuthenticationManagerBuilder is configured now configure HttpSecurity by disabling csrf. Below code requires authentication for metrics alone using any role. You can customize according to the end points you need to authenticate. Make sure you exclude base url of Rest Controller from this Authentication. You can insert authorizeRequests().antMatchers("/baseurl").permitAll().and() in the below configuration code to achieve that. Below is an example to configure HttpSecurity.
protected void configure(Httpsecurity http) {
http.csrf().authorizeRequests().requestMatchers(EndpointRequest.to(MetricsEndpoint.class))
.hasANyRole("ADMIN","USER").and().authorizeRequests().and().httpBasic();
}

Spring Boot 2.0.0.M4 breaks http basic auth in application.yml

Spring Boot 1.5.6.RELEASE respected the basic-auth username and password as specified in my application.yml below.
I have upgraded to 2.0.0.M4 and now the application always starts with the default 'user' and randomly generated password. Basically the settings below are always completely ignored.
I saw some changes in the release note/doc specific to simplifying actuator security enabled/disabled. I didn't see anything specific to this.
Any ideas?
From my application.yml
security:
basic:
enabled: true
realm: some-service
user:
name: example_user
password: example_password
Update:
I've confirmed this functionality was just plainly taken out starting with Spring Boot 2.0.0.M4
In the appendices:
All the security.basic.* family of stuff is missing here from the M4 reference:
https://docs.spring.io/spring-boot/docs/2.0.0.M4/reference/html/common-application-properties.html
But appears here in the M3 reference:
https://docs.spring.io/spring-boot/docs/2.0.0.M3/reference/html/common-application-properties.html
I was able to temporarily downgrade to M3 to restore the previous functionality but would still appreciate some guidance on what replaced it. I just need a single hardcoded basic-auth user for this scenario. I'm aware I could use object configurations to do a much more complicated setup.
Edit 2018-01-31:
The ability to auto-configure a single user has been restored (via the spring.security.user configuration keys) starting with Spring Boot 2.0.0-RC1 (source).
Original answer:
The Spring Boot security.* properties have been deprecated starting with Spring Boot 2.0.0-M4. You can read about this in the Release Notes:
Security auto-configuration has been completely revisited: developers should read the companion blog post and refer to the Spring Boot 2.0 Security wiki page for more details about the change.
In order to restore the basic auth functionality you can register a custom WebSecurityConfigurerAdapter, like this:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
return new InMemoryUserDetailsManager(
User.withDefaultPasswordEncoder().username("user").password("password")
.authorities("ROLE_USER").build(),
User.withDefaultPasswordEncoder().username("admin").password("admin")
.authorities("ROLE_ACTUATOR", "ROLE_USER").build());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(EndpointRequest.to("health", "info")).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
.requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll()
.antMatchers("/**").hasRole("USER")
.and()
.cors()
.and()
.httpBasic();
}
}
(This will also configure basic auth for the Actuator endpoints)
If you additionally need to read the username and password from a .properties file, you can simply inject the values using the #Value annotation.

Spring Boot CSRF

Tried to implement CSRF protection on the latest Spring Boot.
All the examples on internet are based on user login and authentication, which I do not need.
My site does not have any sections requiring authentication.
I would like
1) Rest requests come from within site. No direct request from outside with wget to be allowed.
2) All pages (routes) must be requested from the index page (/)
Included the security dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
-- Defined users in application.properties (even though, I do not need)
-- App creates _csrf.token .
-- Created class extending WebSecurityConfigurerAdapter with "configure" method overriding.
Tried all suggested filters in "configure". It did not work and finally left it blank.
The problem is that Wget can get api pages directly.
How to prevent it?
I've quickly put together a POC of this configuration:
#Configuration
#EnableWebSecurity
#SpringBootApplication
public class StackoverflowQ40929943Application extends WebSecurityConfigurerAdapter{
public static void main(String[] args) {
SpringApplication.run(StackoverflowQ40929943Application.class, args);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").permitAll();
}
}
The gist of it is Spring Boot + Security will secure all endpoints automatically. Here we explicitly allow requests to all endpoints. But, Spring Boot + Security automatically configures CSRF out of the box which we've left enabled. Thus you get the best of both worlds.
NOTE: You'll probably need to refine this configuration further to meet your needs.
Full Example on GitHub

Spring boot actuator secure services does not work fine

I an Trying to secure spring actuator services /manage context path when calling for example:
http://localhost:9091/manage/metrics
with this config in my yalm.properties
management:
port: 9091
address: 127.0.0.1
context-path: /manage
security:
enabled: true
role: ADMIN.
Git branch with security actuator service layer
but access to every service is still free.
Spring security config:
'#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/pizzas","/info","/addPizza").hasAnyRole("USER","ADMIN").and().authorizeRequests().antMatchers("/users","/addUser").hasRole("ADMIN").and().authorizeRequests().antMatchers("/static/**","/logout","/login").permitAll();
http.formLogin().loginPage("/login").failureUrl("/login?error").permitAll();
http.logout().logoutSuccessUrl("/?logout").deleteCookies("remember-me").permitAll();
http.sessionManagement().maximumSessions(1).
expiredUrl("/?expired").maxSessionsPreventsLogin(true).and()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
}
/**
* Configure global security with Bccyptenoncder and custom userDetailService with Spring Security
* #param auth
* #throws Exception
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceImpl).passwordEncoder(passwordEncoder());
}
/**
* Bcrypt password encoding configuration, more info at http://www.baeldung.com/spring-security-registration-password-encoding-bcrypt
* #return
*/
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
'
Spring boot team has resolved me this issue. I share the solution here:
Same Origin Policy
You cannot use the login page from your main Spring Application within actuator security. The reason is that the cookie is going to be associated with the domain + port + context path of the application. This is part of the Same Origin Policy
This means if you sent the user to localhost:9090/pizza/login and authenticated, when you visited localhost:9091/manage/ the JSESSIONID cookie would not be submitted to the management application which means you would not be seen as authenticated.
In order to authenticate across domains (i.e. different ports in this case) you would need some single sign on (OpenID, CAS, SAML, etc) mechanism.
Mapping a Login Page in the Management Application
In order to use this configuration you would need to setup a login page within the management application. To do this you would just need to return an HTML form when /login is requested. However, I'm not really certain how you would do that within the Boot management application. Perhaps #philwebb or #dsyer can elaborate on how one would do that.
Distinct Security Configuration for the Management Application
Alternatively you could create separate security configuration for the management application that allows authenticating with Basic Authentication. To do this you would create another Security Configuration that looks something like this:
#Order(0)
#Configuration
public class ManagementSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.requestMatchers(request -> "/manage".equals(request.getContextPath()))
.and()
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
This would make sure that if the context root is "/manage" that this security configuration is used. A few points of interest:
#Order(0) makes sure the configuration occurs before your other security configuration since by default any subclass of WebSecurityConfigurerAdapter will be ordered at 100. This is important because only the first WebSecurityConfigurerAdapter is used (similar to the authorizeRequests() matchers).
The request matcher is using a lambda for matching on the contextPath. I had thought there was a better way to distinguish Spring Boot application from the main application, but it does not appear that is the case. Perhaps #dsyer knows how this should be done.
NOTE
You can rewrite your configuration much more concisely as:
http
.authorizeRequests()
.antMatchers("/pizzas","/info","/addPizza").hasAnyRole("USER","ADMIN")
.antMatchers("/users","/addUser").hasRole("ADMIN")
.antMatchers("/static/**","/logout","/login").permitAll()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/?logout")
.deleteCookies("remember-me")
.permitAll();
You might consider reading Spring Security Java Config Preview: Readability for details on how to format the configuration to better read it too.

Resources