port 8080 is overlapped by port 8180
This is the first time I'm launching an application with keycloack, spring boot and docker. Setting up and working with keycloack itself did not cause any problems. Docker runs smoothly. But when I request from postman to spring methods, I get an error Error: connect ECONNREFUSED 127.0.0.1:8080
docker-compose.yml
version: '3.9'
services:
keycloak:
container_name: keycloak-auth
image: quay.io/keycloak/keycloak:22.0.1
command:
- "start-dev"
ports:
- "8180:8080"
networks:
- keycloak
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: password
KC_DB: postgres
KC_DB_URL_HOST: keycloak-db
KC_DB_URL_DATABASE: keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: password
KC_HEALTH_ENABLED: true
depends_on:
- keycloak-db
keycloak-db:
image: postgres:14-alpine
container_name: keycloak-db
ports:
- "5433:5432"
volumes:
- postgres_data_keycloak:/var/lib/postgresql/data
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
networks: [ keycloak ]
healthcheck:
test: [ "CMD", "pg_isready", "-q", "-d", "postgres", "-U" ]
timeout: 45s
interval: 10s
retries: 10
activemq:
image: webcenter/activemq:latest
ports:
# mqtt
- "1883:1883"
# amqp
- "5672:5672"
# ui
- "8161:8161"
# stomp
- "61613:61613"
# ws
- "61614:61614"
# jms
- "61616:61616"
networks: [ activemq ]
volumes: [ "activemq-data:/opt/activemq/conf", "activemq-data:/data/activemq", "activemq-data:/var/log/activemq" ]
environment:
ACTIVEMQ_REMOVE_DEFAULT_ACCOUNT: "true"
ACTIVEMQ_ADMIN_LOGIN: admin
ACTIVEMQ_ADMIN_PASSWORD: password
ACTIVEMQ_WRITE_LOGIN: write
ACTIVEMQ_WRITE_PASSWORD: password
ACTIVEMQ_READ_LOGIN: read
ACTIVEMQ_READ_PASSWORD: password
ACTIVEMQ_JMX_LOGIN: jmx
ACTIVEMQ_JMX_PASSWORD: password
ACTIVEMQ_STATIC_TOPICS: static-topic-1;static-topic-2;autoTopic
ACTIVEMQ_STATIC_QUEUES: static-queue-1;static-queue-2
ACTIVEMQ_ENABLED_SCHEDULER: "true"
ACTIVEMQ_MIN_MEMORY: 512
ACTIVEMQ_MAX_MEMORY: 2048
networks:
keycloak:
name: keycloak
driver: bridge
activemq: { }
volumes:
postgres_data_keycloak:
driver: local
activemq-data:
driver: local
securityConfig
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
//TODO: security without @PreAuthorize
/* .authorizeHttpRequests(registry -> registry
.requestMatchers(HttpMethod.GET, "/api/**").hasRole("USER")
.requestMatchers(HttpMethod.POST, "/api/**").hasRole("PERSON")
.anyRequest().authenticated()
)*/
.oauth2ResourceServer(oauth2Configurer -> oauth2Configurer
.jwt(jwtConfigurer -> jwtConfigurer
.jwtAuthenticationConverter(jwt -> {
Map<String, Collection<String>> realmAccess = jwt.getClaim("realm_access");
Collection<String> roles = realmAccess.get("roles");
var grantedAuthorities = roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
return new JwtAuthenticationToken(jwt, grantedAuthorities);
})));
return httpSecurity.build();
}
}
Controller
@RestController
@RequestMapping(value = "/api", produces = MediaType.APPLICATION_JSON_VALUE)
@RequiredArgsConstructor
@Slf4j
public class AutomobileRestController implements AutomobileResource, AutomobileOpenApi, JMSPublisher {
private final AutomobileRepository repository;
private final JmsTemplate jmsTemplate;
public static double getTiming(Instant start, Instant end) {
return Duration.between(start, end).toMillis();
}
@Transactional
@PostConstruct
public void init() {
repository.save(new Automobile(1L, "Ford", "Green", LocalDateTime.now(), LocalDateTime.now(), true, false));
}
@PostMapping("/automobiles")
@ResponseStatus(HttpStatus.CREATED)
@PreAuthorize("hasRole('ADMIN')")
//@RolesAllowed("ADMIN")
public Automobile saveAutomobile(@Valid @RequestBody Automobile automobile) {
log.info("saveAutomobile() - start: automobile = {}", automobile);
Automobile savedAutomobile = repository.save(automobile);
log.info("saveAutomobile() - end: savedAutomobile = {}", savedAutomobile.getId());
return savedAutomobile;
}
@GetMapping("/automobiles")
@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasRole('USER')")
public Collection<Automobile> getAllAutomobiles() {
log.info("getAllAutomobiles() - start");
Collection<Automobile> collection = repository.findAll();
log.info("getAllAutomobiles() - end");
return collection;
}
@GetMapping("/automobiles/{id}")
@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasRole('PERSON')")
public Automobile getAutomobileById(@PathVariable Long id) {
log.info("getAutomobileById() - start: id = {}", id);
Automobile receivedAutomobile = repository.findById(id)
.orElseThrow(ThereIsNoSuchAutoException::new);
if (receivedAutomobile.getDeleted()) {
throw new AutoWasDeletedException();
}
log.info("getAutomobileById() - end: Automobile = {}", receivedAutomobile.getId());
return receivedAutomobile;
}
@Hidden
@GetMapping(value = "/automobiles", params = {"name"})
@ResponseStatus(HttpStatus.OK)
public Collection<Automobile> findAutomobileByName(@RequestParam(value = "name") String name) {
log.info("findAutomobileByName() - start: name = {}", name);
Collection<Automobile> collection = repository.findByName(name);
log.info("findAutomobileByName() - end: collection = {}", collection);
return collection;
}
@PutMapping("/automobiles/{id}")
@ResponseStatus(HttpStatus.OK)
//@CachePut(value = "automobile", key = "#id")
public Automobile refreshAutomobile(@PathVariable Long id, @RequestBody Automobile automobile) {
log.info("refreshAutomobile() - start: id = {}, automobile = {}", id, automobile);
Automobile updatedAutomobile = repository.findById(id)
.map(entity -> {
entity.checkColor(automobile);
entity.setName(automobile.getName());
entity.setColor(automobile.getColor());
entity.setUpdateDate(automobile.getUpdateDate());
if (entity.getDeleted()) {
throw new AutoWasDeletedException();
}
return repository.save(entity);
})
//.orElseThrow(() -> new EntityNotFoundException("Automobile not found with id = " + id));
.orElseThrow(ThereIsNoSuchAutoException::new);
log.info("refreshAutomobile() - end: updatedAutomobile = {}", updatedAutomobile);
return updatedAutomobile;
}
@DeleteMapping("/automobiles/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@CacheEvict(value = "automobile", key = "#id")
public String removeAutomobileById(@PathVariable Long id) {
log.info("removeAutomobileById() - start: id = {}", id);
Automobile deletedAutomobile = repository.findById(id)
.orElseThrow(ThereIsNoSuchAutoException::new);
deletedAutomobile.setDeleted(Boolean.TRUE);
repository.save(deletedAutomobile);
log.info("removeAutomobileById() - end: id = {}", id);
return "Deleted";
}
@Hidden
@DeleteMapping("/automobiles")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void removeAllAutomobiles() {
log.info("removeAllAutomobiles() - start");
repository.deleteAll();
log.info("removeAllAutomobiles() - end");
}
@GetMapping(value = "/automobiles", params = {"color"})
@ResponseStatus(HttpStatus.OK)
public Collection<Automobile> findAutomobileByColor(
@Parameter(description = "Name of the Automobile to be obtained. Cannot be empty.", required = true)
@RequestParam(value = "color") String color) {
Instant start = Instant.now();
log.info("findAutomobileByColor() - start: time = {}", start);
log.info("findAutomobileByColor() - start: color = {}", color);
Collection<Automobile> collection = repository.findByColor(color);
Instant end = Instant.now();
log.info("findAutomobileByColor() - end: milliseconds = {}", getTiming(start, end));
log.info("findAutomobileByColor() - end: collection = {}", collection);
return collection;
}
@GetMapping(value = "/automobiles", params = {"name", "color"})
@ResponseStatus(HttpStatus.OK)
public Collection<Automobile> findAutomobileByNameAndColor(
@Parameter(description = "Name of the Automobile to be obtained. Cannot be empty.", required = true)
@RequestParam(value = "name") String name, @RequestParam(value = "color") String color) {
log.info("findAutomobileByNameAndColor() - start: name = {}, color = {}", name, color);
Collection<Automobile> collection = repository.findByNameAndColor(name, color);
log.info("findAutomobileByNameAndColor() - end: collection = {}", collection);
return collection;
}
@GetMapping(value = "/automobiles", params = {"colorStartsWith"})
@ResponseStatus(HttpStatus.OK)
public Collection<Automobile> findAutomobileByColorStartsWith(
@RequestParam(value = "colorStartsWith") String colorStartsWith,
@RequestParam(value = "page") int page,
@RequestParam(value = "size") int size) {
log.info("findAutomobileByColorStartsWith() - start: color = {}", colorStartsWith);
Collection<Automobile> collection = repository
.findByColorStartsWith(colorStartsWith, PageRequest.of(page, size, Sort.by("color")));
log.info("findAutomobileByColorStartsWith() - end: collection = {}", collection);
return collection;
}
@GetMapping("/automobiles-names")
@ResponseStatus(HttpStatus.OK)
public List<String> getAllAutomobilesByName() {
log.info("getAllAutomobilesByName() - start");
List<Automobile> collection = repository.findAll();
List<String> collectionName = collection.stream()
.map(Automobile::getName)
.sorted()
.collect(Collectors.toList());
log.info("getAllAutomobilesByName() - end");
return collectionName;
}
@Override
@PostMapping("/message")
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Automobile> pushMessage(@RequestBody Automobile automobile) {
try {
Topic autoTopic = Objects.requireNonNull(jmsTemplate
.getConnectionFactory()).createConnection().createSession().createTopic("AutoTopic");
Automobile savedAutomobile = repository.save(automobile);
log.info("\u001B[32m" + "Sending Automobile with id: " + savedAutomobile.getId() + "\u001B[0m");
jmsTemplate.convertAndSend(autoTopic, savedAutomobile);
return new ResponseEntity<>(savedAutomobile, HttpStatus.OK);
} catch (Exception exception) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Application.yml
spring:
application:
name: App
profiles:
active: development
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/automobiles
username: postgres
password: postgres
jpa:
hibernate:
ddl-auto: update
show-sql: true
database: postgresql
database-platform: org.hibernate.dialect.PostgreSQLDialect
security:
oauth2:
resource-server:
jwt:
issuer-uri: http://localhost:8180/realms/automobile-realm
jms:
pub-sub-domain: true
activemq:
broker-url: tcp://localhost:61616
server:
port: 8080
servlet:
context-path: /demo
logging:
pattern:
console: "%d %-5level %logger : %msg%n"
level:
org.springframework: info
org.springframework.security: debug
org.springframework.security.oauth2: debug
springdoc:
swagger-ui:
path: /swagger-ui.html
api-docs:
path: /v3/api-docs.yaml
management:
endpoints:
web:
exposure:
include: "*"
When I make a request I get an error:
In keycloak itself the following port settings
I checked that the port is not busy during the request, and keycloak works on port 8180. Maybe I missed some setting?
DockerFile
FROM openjdk:17
ADD /target/spring-boot-keycloak-docker-postgres.jar spring-boot-keycloak-docker-postgres.jar
ENTRYPOINT ["java", "-jar", "spring-boot-keycloak-docker-postgres.jar"]
docker-compose
The problem is, your spring boot app is not getting started via your docker-compose file. This file is only starting up keycloak, it's DB and active mq services.
You should package your spring boot app as a docker image (https://spring.io/guides/topicals/spring-boot-docker/) and use that as another service in your docker compose file.