I am trying to secure my Spring cloud config server. As a first step I followed the tutorials which are pretty simple thing to do. The config server works fine without authorisation. But the problem starts when I want to use a very simple basic authentication. I define my own user and password in properties spring.security.user.name and spring.security.user.password,but the service doesn't seem to work with those.
When I just leave these properties and I use the default "user" and generated password, then everything is fine. I'm stuck with this for a while now, and trying to resolve.
Here is the important part of my config server pom.xml file:
<artifactId>tao-elszamolas-config</artifactId>
<packaging>jar</packaging>
<name>tao-elszamolas-config</name>
<description>Some config service</description>
<parent>
<groupId>com.mycompany.tao</groupId>
<artifactId>tao-elszamolas</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<!-- Spring Cloud config server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- Spring Cloud Eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
And this is the config server application.yml:
server:
port: 9001
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: file://${user.home}/application-config
clone-on-start: true
security:
user:
name: configUser
password: configPassword
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9002/eureka/
The config client is basically the discovery service. (I want to implement a discovery-first model used by other microservices.) Here is important part of pom.xml for the config client service (discovery service in my case):
<name>tao-elszamolas-discovery</name>
<description>Some service discovery</description>
<parent>
<groupId>com.mycompany.tao</groupId>
<artifactId>tao-elszamolas</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<!-- Spring Cloud Netflix Eureka service registry server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- Spring Cloud config client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
And this is the bootstrap.yml for the config client service:
spring:
application:
name: discovery-server
cloud:
config:
name: discovery-server
uri: http://localhost:9001
username: configUser
password: configPassword
So, if I remove the user name and password from application.yml of config server and I use the default user as username, and the generated password in the bootstrap.yml of discovery server, then everything works fine.
Does anyone has any idea why the custom username and password is not working?
Any help would be very much appreciated. I may made a very trivial mistake and just don't really recognise it.
UPDATE:
I debugged the Spring framework a bit. I found some very interesting facts in the latest version of spring-security-core-5.1.1.RELEASE.jar
, more specifically, in the InMemoryUserDetailsManager
class.
When the users are created in this class this method is called:
public void createUser(UserDetails user) {
Assert.isTrue(!userExists(user.getUsername()), "user should not exist");
users.put(user.getUsername().toLowerCase(), new MutableUser(user));
}
The focus should be on this part:
user.getUsername().toLowerCase()
because when it tries to update the password for the user, it uses this method:
@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
String username = user.getUsername();
MutableUserDetails mutableUser = this.users.get(username);
mutableUser.setPassword(newPassword);
return mutableUser;
}
Yes, as you see the updatePassword
method is not consistent with the rest of the class, because it doesn't convert the username
to lowercase. So it will never work if you define your user in properties file like this: configUser
.
After I checked out the Spring source code in Github, I found this commit:
Conclusion:
The problem was the user name and not the password. If you want to get this work, the you have the following options:
If you are on Spring Boot
, use the latest parent version, which is 2.1.1.RELEASE
If you insist on using spring-security-core-5.1.1.RELEASE.jar
, you should use only lower case usernames.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With