How single authentication can work for multiple server nodes in Spring Security - spring-boot

I am using weblogic for deploying my spring boot application, and my same application is deployed on multiple nodes.
For example the two node in which the application is deployed is 9001 and 9002.
With basic security even if I am authenticated on the Node 9001 and trying to access the same URL on second node i.e on 9002, I am again getting redirected again to spring login page for authentication.
I want that once I authenticate using username and password on any node. I need not to authenticate again, Even if I am requesting to any other node.
Any kind of clue or help will be appreciated.
Thanks in advance.
The Security configuration file is
package com.config;
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("test")
.password("{noop}test")
.authorities("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/userdetail").authenticated()
.anyRequest().permitAll()
.and()
.formLogin();
}
}

In my case it worked for both node when I enabled RedisHttpSession.
Below is the code which worked for me.
#Configuration
#EnableRedisHttpSession
public class RedisConfig {
#Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
}
also in pom.xml I needed to make two dependencies(For Spring boot).
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
You can also take reference about EnableRedisHttpSession from spring docs, and about spring session from
https://docs.spring.io/spring-session/docs/current/api/org/springframework/session/data/redis/config/annotation/web/http/EnableRedisHttpSession.html
https://www.baeldung.com/spring-session

Related

AuthorizeHttpRequest in Springboot Security

I want the root only to be publicly accessible, this is how I'm doing it, but it doesn't seem to work as every URL including the root requires me to pass by the login interface
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
public class ApplicationSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http)throws Exception{
http.authorizeHttpRequests()
.requestMatchers("/")
.permitAll()
.anyRequest().authenticated()
.and().httpBasic();
return http.build();
}
}
.requestMatchers("/**") should fix it.
More details:
#Configuration
#EnableWebSecurity
#EnableMethodSecurity
public class SecurityConfig {
Add the EnableMethodSecurity another to allow some PreAuths on API pathing via your controllers, I like to batch do this via Interfaces as Spring Boot now allows it.
#PostMapping("/URL")
#PreAuthorize("hasRole('ROLE')")
public ResponseEntity<T> method(
The PreAuthorize annotation is basically short way of doing hasRole in your security filter chain.

spring boot rest app secured by keycloak 18 return always error 401 Unauthorized

I'm developing microservices some of them with spring boot web and other with spring boot data rest and I want secure them with keycloak server 18 through OpenId protocol. The microservices endpoint can be accessed from frontend adding in the "Authorization header" the bearer token obtained from post request to the url http://localhost:8280/auth/realms/--realm_name--/.well-known/openid-configuration inserting in the body request the key client_id, username, password, grant_type, client_secret.
I have create
1.a realm,
2.a client named "springboot-mc-dev" (with Access Type = confidential;,
"Root URL" and "Valid Redirect URIs" both setted to "http://localhost:8490",
"Standard Flow Enabled", "Direct Access Grants Enabled", "Service Accounts Enabled" and "Authorization Enabled" setted to "ON"),
3.before a role inside client ("named springboot-mc-dev-role" composite False) and after a role inside realm (named always "springboot-mc-dev-role" composite true that is associated Client Roles "springboot-mc-dev-role" of client springboot-mc-dev),
4.user mapped to role "springboot-mc-dev-role" in "realm roles"
After I have imported the following dependency in parent pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java-version>1.8</java-version>
<keycloak.version>18.0.2</keycloak.version>
</properties>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Here there is the code of SecurityConfig.java class
package it.organization.project.microservice.datamart.config.security;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
#KeycloakConfiguration
#Configuration
#EnableWebSecurity
#Import(KeycloakSpringBootConfigResolver.class)
#EnableGlobalMethodSecurity(jsr250Enabled = true)
#ConditionalOnProperty(name = "ms-security-enable", havingValue = "true")
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
grantedAuthorityMapper.setPrefix("ROLE_");
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
//keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
/*protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}*/
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/**").hasRole("springboot-mc-dev-role")
//.anyRequest().hasRole("springboot-mc-dev-role")
//.anyRequest().//authenticated()
//permitAll()
;
http.csrf().disable();
}
#Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
//public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
this is the code of main class
package it.organization.project.microservice.datamart;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class MAINDataMartApplication {
public static void main(String[] args) {
Security.addProvider(new BouncyCastleProvider());
SpringApplication.run(MAINDataMartApplication.class, args);
}
}
and last there is the application.yml with keycloak settings
#KEYCLOAK CONFIGURATION
keycloak:
auth-server-url: http://localhost:8280/auth
#ssl-required: external
realm: realmname
bearer-only: true
#public-client: true
use-resource-role-mappings: true
resource: springboot-mc-dev
credentials:
secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
this is the result
What is wrong?
Keycloak spring adapter is deprecated. Don't use it.
Have look at those tutorials for alternatives.
We are missing important info to answer preecisely:
Spring versions?
reason for 401 (from Postman console, value of WWW-Authenticate response header)?
There can be few reasons for 401 Unauthorized:
missing authorization header
expired or not yet valid token (for instance because of timezone misconfiguration on either authorization or resource server)
different issuer in access-token claim and Spring config: host, port, etc must be exactly the same
Spring configured for a different type of authentication than what authorization server supports (i.e. opaque token with introspaction and one side and JWT on the other)
...
If you're using Keycloak 18 with Quarkus, it is likely that your conf should reference http://localhost:8280 as issuer (and not http://localhost:8280/auth).
So, look at Postman console, open your access-token with a tool like https://jwt.io and compare values you find there with what you have in spring conf and logs.

401 unauthorized page for swagger?

I am enable swagger2 by #EnableSwagger2. However, when I try to hit "/swagger-ui.html", it first hit my Authentication Filter. Then, I wrote the following code to bypass the authentication check
String resourcePath = new UrlPathHelper().getPathWithinApplication(httpRequest);
if ("/swagger-ui.html".equalsIgnoreCase(resourcePath)) {
filterChain.doFilter(request, response);
}
I can see the filterChain.doFilter(request, response); was hit. However, when I let the debug go, it returns a page with information below
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Apr 04 15:41:50 EDT 2018
There was an unexpected error (type=Unauthorized, status=401).
No message available
Any idea, guys?
I encountered the same issue in my project and discovered this solution, so first add this config file to the project
package bla.bla.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#Configuration
#EnableSwagger2
public class Swagger2Config {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors
.basePackage("bla.bla.controllers"))
.paths(PathSelectors.any())
.build();
}
}
and then add this code block to you WebSecurityConfig
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().mvcMatchers(HttpMethod.OPTIONS, "/**");
web.ignoring().mvcMatchers("/swagger-ui.html/**", "/configuration/**", "/swagger-resources/**", "/v2/api-docs","/webjars/**");
}
my problem fixed
source : swagger.io
Think you can add your ownWebSecurityConfig extends WebSecurityConfigurerAdapter, than override configure(WebSecurity web) method and there put web.ignoring().antMatchers("/swagger-ui.html") ofc annotate that class with #Configuration
I have the same error and I add this code inside the class websecurityConfig
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/test/**").permitAll() // permit the class of test
.antMatchers("/**").permitAll() // permit all the routers after swagger-ui.html
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
As answered by Georgi Stoyanov , adding that much code removed Whitelabel Error Page error but my swagger UI home page was blank as there was 401 issue in loading some css & js files.
Swagger-ui with Spring security
Also, important point that I want to mention is that my swagger UI was working for Weblogic deployment without above code (only HttpSecurity override was enough ) and I was facing issue only when running app in embedded tomcat.
Spring Boot Version : 1.5.2.RELEASE
SpringFox Version 2.8.0
So I had to made code changes as answered by me in linked question to load all CSS & JS files also.
I had the same problem and this was my solution.
if do you have a Spring security config, you must to give authorization at all urls that swagger needs
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/api/loggin").permitAll()
.antMatchers(HttpMethod.GET,"/swagger-resources/**").permitAll()
.antMatchers(HttpMethod.GET,"/swagger-ui/**").permitAll()
.antMatchers(HttpMethod.GET,"/v2/api-docs").permitAll()
.anyRequest()
.authenticated();
}
and in your class main you shuld to add this notation
#EnableSwagger2
and finally in yor pom.xml this dependencies.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
for use http://localhost:8090/swagger-ui/

Class file for org.springframework.security.authentication.encoding.PasswordEncoder not found

I've made a simple restAPI with Spring-boot and spring security, implementing Basic Authentication. I want to use inMemoryAuthentication, and bcryptPasswordEncoder, but for some reason my compiler is looking for the passwordEncoder in the wrong place, See title vs security.crypto.password.PasswordEncoder.
My Basic auth looks like this:
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
Configuration
#EnableWebSecurity
public class BasicAuthConfig extends WebSecurityConfigurerAdapter {
// Authentication : User --> Roles
// NoOpPasswordEncoder has been deprecated in Spring security so {noop} is being used to avoid errors
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("user1")
.password("{noop}secret1")
.roles("USER")
.and().withUser("admin1")
.password("{noop}secret1")
.roles("USER", "ADMIN");
}
// Authorization : Role -> Access
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and().authorizeRequests()
.antMatchers("/tokenservice/**")
.hasRole("USER")
.antMatchers("/**")
.hasRole("ADMIN")
.and().csrf()
.disable()
.headers()
.frameOptions()
.and().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
I get this error when trying to compile:
Error:(21, 7) java: cannot access org.springframework.security.authentication.encoding.PasswordEncoder
class file for org.springframework.security.authentication.encoding.PasswordEncoder not found
Disregard the {noop} in front of the password. is there for testing without passwordEncoder.
I'm very new to Spring and gradle, and java all in all. Could i be using a wrong version of Spring? If thats the case, how do i fix it?
If your using latest spring-security version, you should find PasswordEncoder class in org.springframework.security.crypto.password package.
PasswordEncoder in package org.springframework.security.authentication.encoding.PasswordEncoder has been deprecated
Looks like it is caused by incompatible versions of spring-security-config (4.0.2.RELEASE) and spring-security-core (5.0.3.RELEASE)
The newer version of spring-security-core only accepts org.springframework.security.crypto.password.PasswordEncoder
I fixed this by downgrading spring-security-core to match spring-security-config.
It was due to version mismatch in spring security config jar.
When you are upgrading your spring version from 4.x to 5.x , this issue occurs as PasswordEncoder class object is restricted in use.
In spring-security-config 4.x version in AbstractDaoAuthenticationConfigurer the passwordEncoder() is overloading both the org.springframework.security.crypto.password.PasswordEncoder and org.springframework.security.authentication.encoding.PasswordEncoder, where else in spring-security-config 5.x it is only the org.springframework.security.crypto.password.PasswordEncoder
spring-security-config 4.x
public C passwordEncoder(org.springframework.security.crypto.password.PasswordEncoder passwordEncoder)
{
this.provider.setPasswordEncoder(passwordEncoder);
return this;
}
public C passwordEncoder(org.springframework.security.authentication.encoding.PasswordEncoder passwordEncoder)
{
this.provider.setPasswordEncoder(passwordEncoder);
return this;
}
spring-security-config 5.x
import org.springframework.security.crypto.password.PasswordEncoder;
public C passwordEncoder(PasswordEncoder passwordEncoder)
{
this.provider.setPasswordEncoder(passwordEncoder);
return this;
}
Most probably solution is to downgrade your spring version to 4.x.

Spring Boot JS App wont work after securing rest-api with Spring Security

I created a simple Spring Boot/ JS App. In a next step I tried to implement an usermanagement feature to handle multiple users.
So I implemented a usermodel and controller and secured all rest-api calls via authentication of spring security.
#Configuration
#EnableWebSecurity
#ComponentScan("package.packagename")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception{
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select email, password, active from accounts where email=?")
.authoritiesByUsernameQuery("select email, role from account_roles where email=?");
}
#Override
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().permitAll()
.and()
.authorizeRequests()
.antMatchers("/index.html", "/").permitAll()
.anyRequest().authenticated()
.and()
.logout();
}
}
Additionally to this file I have the SecurityWebApplicationInitializer
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
My problem now is the fact, that if I start the application and try to access it via localhost:8080 I face an 404 Error.
Usually the app should work even without login and it seems that with springsecurity enabled the app is not able to load the js stuff in resources/public directory.
Reading the logs showed the following:
No mapping found for HTTP request with URI [/] in DispatcherServlet with name 'dispatcherServlet'
If I start the App without spring security it works without any problems.
The securing of the api-calls works like a charm - I'm able to login, receive a cookie and use this cookie to authenticate against the api-functions which are secured by springsecurity.
I hope you can help me to resolve my (hopefully small) problem.
Thanks in advance
When you use .formLogin() you need to define the login page or use .httpBasic() auth. So you can use something like this:
.formLogin()
.and()
.httpBasic();
or
.formLogin()
.loginPage("/login.html")
You can read more here
http://docs.spring.io/spring-security/site/docs/3.2.x/guides/form.html
http://www.baeldung.com/spring-security-login
I figured out that I have to add a WebConfig.java class like this:
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
#ComponentScan
public class WebConfig extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter {
}
Now Spring is able to resolve the / call properly. I just have to make sure to open the access to all files in public for all users (permitall() function).
Anyway thanks for your help :-)

Resources