Trying to use Oauth2 token with feign client and hystrix - spring

I am trying to call "ServiceB" from "ServiceA", both of the services are resource server, I am trying to make this inter service call through "Feign Client and OAuth2 toke" which is working fine with the below bean implmentation in Configuration class:
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
SecurityContextHolder.getContext().getAuthentication()
.getDetails();
requestTemplate.header("Authorization",
"bearer " + details.getTokenValue());
}
};
}
When I am trying to use Feign client with fallback i.e. Hystrix without OAuth token(i.e. when none of services are Resource Server) that is also working fine.
But while trying to use three of these (i.e. Feignclient, Hystrix and OAuth2)all together, it is not working. Every time it is going to fallback method though all the services are up and running.
Below are my code:
App.java
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import feign.RequestInterceptor;
import feign.RequestTemplate;
#SpringBootApplication
#RestController
#EnableFeignClients
#EnableEurekaClient
#EnableCircuitBreaker
public class App
{
/*#Autowired
#Qualifier("abc")
private GitHubClient gitHub;*/
#Autowired
private CallService callService;
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
#RequestMapping(value="/feign",method=RequestMethod.POST,consumes="application/json")
public String contributors1(#RequestBody JSONObject payLoad) {
String callservice2 = callService.callservice(payLoad);
return callservice2;
}
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
SecurityContextHolder.getContext().getAuthentication()
.getDetails();
requestTemplate.header("Authorization",
"bearer " + details.getTokenValue());
}
};
}
}
Callervice.java
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
#Service
public class CallService {
#Autowired
#Qualifier("abc")
private GitHubClient gitHub;
public String callservice(JSONObject payLoad){
String forObject =gitHub.contributors(payLoad);
return forObject;
}
}
HystrixWrappedClient.java
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
#Component("abc")
public class HystrixWrappedClient implements GitHubClient{
#Autowired
#Qualifier("gitHubClient")
private GitHubClient gitHub;
#Override
#HystrixCommand(fallbackMethod="failure")
public String contributors(JSONObject payLoad) {
return gitHub.contributors(payLoad);
}
public String failure(JSONObject payLoad){
System.out.println(payLoad);
return "Failure";
}
}
GitHubClient.java
import org.json.simple.JSONObject;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#FeignClient("MockRestService")
interface GitHubClient {
#RequestMapping(method = RequestMethod.POST, value = "/test",consumes="application/json")
String contributors(#RequestBody JSONObject payLoad);
}
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>
<!-- For swagger documenation -->
<dependency>
<groupId>com.mangofactory</groupId>
<artifactId>swagger-springmvc</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<!-- NEW -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>1.0.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Please suggest. Whenever trying to use Feignclient, OAuth2 and Hystrix all together it always going to Fallback method.

Did you shared Spring Security context with Hystrix in some way?
Since Spring Cloud Netflix 1.2.0 you can enable sharing of security context with Hystrix using i.e config param
hystrix.shareSecurityContext: true

u can look into this http://kodgemisi.com/2017/02/use-securitycontext-feign-requestinterceptor/
Hystrix execute in another/different thread, transfer your security context by HystrixRequestVariableDefault

Related

Unauthorized and full authentication required to access user's resource

I am working on a resource service that uses Keycloak as the authorization server. The code below is taken from Spring Security in Action by Laurentiu Spilcă. Keycloak is provisioned with some users for testing purposes. So first a token for a user is obtained after login and the token is used to to make a request to retrieve the user's data from the resource server but I keep getting unauthorized and full authentication required. I am unable to figure out what the issue is as I am quite new to Spring.
KeycloakApplication.java
package com.chapter18.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#EnableJpaRepositories(basePackages = {"com.chapter18.repositories"} )
#EntityScan(basePackages = "com.chapter18.entities")
#ComponentScan(basePackages = {"com.chapter18.service", "com.chapter18.config", "com.chapter18.controller"})
public class KeycloakProjectApplication {
public static void main(String[] args) {
SpringApplication.run(KeycloakProjectApplication.class, args);
}
}
ResourceServerConfig.java
package com.chapter18.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore;
import org.springframework.security.web.FilterInvocation;
/**
* created on 21/04/2022
*/
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Value("${claim.aud}")
private String claimAud;
#Value("${jwkSetUri}")
private String urlJwk;
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
System.out.println(claimAud + "\t" + urlJwk);
resources.tokenStore(tokenStore());
resources.resourceId(claimAud);
resources.expressionHandler(handler());
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.headers().frameOptions().disable();
// http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.mvcMatchers(HttpMethod.DELETE, "/**")
.hasAuthority("fitnessadmin")
.antMatchers("/h2-console/**").permitAll()
.anyRequest()
.authenticated();
}
#Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
#Bean
public SecurityExpressionHandler<FilterInvocation> handler() {
return new OAuth2WebSecurityExpressionHandler();
}
#Bean
public TokenStore tokenStore() {
return new JwkTokenStore(urlJwk);
}
}
WorkoutController.java
package com.chapter18.controller;
import com.chapter18.entities.Workout;
import com.chapter18.service.WorkoutService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* created on 21/04/2022
*/
#RestController
#RequestMapping("/workout")
public class WorkoutController {
#Autowired
private WorkoutService workoutService;
#PostMapping("/")
public void add(#RequestBody Workout workout){
workoutService.saveWorkout(workout);
}
#GetMapping("/")
public List<Workout> findAll(){
return workoutService.findWorkouts();
}
#DeleteMapping("/{id}")
public void delete(#PathVariable Integer id){
workoutService.deleteWorkout(id);
}
}
Workout.java
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.time.LocalDateTime;
/**
* created on 21/04/2022
*/
#Getter
#Setter
#Entity
public class Workout {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String user;
private LocalDateTime start;
private LocalDateTime end;
private int diffculty;
}
WorkoutRepository.java
package com.chapter18.repositories;
import com.chapter18.entities.Workout;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/**
* created on 21/04/2022
*/
public interface WorkoutRepository extends JpaRepository<Workout, Integer> {
#Query("SELECT w FROM Workout w WHERE w.user = ?#{authentication.name}")
List<Workout> findAllByUser();
}
WorkoutService
package com.chapter18.service;
import com.chapter18.entities.Workout;
import com.chapter18.repositories.WorkoutRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* created on 21/04/2022
*/
#Service
public class WorkoutService {
#Autowired
private WorkoutRepository workoutRepository;
#PreAuthorize("#workout.user == authentication.name and #oauth2.hasScope('fitnessapp')")
public void saveWorkout(Workout workout){
System.out.println(workout.getUser());
workoutRepository.save(workout);
}
public List<Workout> findWorkouts(){
return workoutRepository.findAll();
}
public void deleteWorkout(Integer id){
workoutRepository.deleteById(id);
}
}
application.yml
spring:
application:
name: FitnessApp
datasource:
url: jdbc:h2:mem:spring
username: sa
password: pass
driverClassName: org.h2.Driver
jpa:
spring:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
defer-datasource-initialization: true
h2:
console:
enabled: true
sql:
init:
mode: ALWAYS
claim:
aud: fitnessapp
jwkSetUri: http://localhost:8080/realms/master/protocol/openid-connect/certs
server:
port: 9090
schema.sql
CREATE SCHEMA IF NOT EXISTS spring;
CREATE TABLE IF NOT EXISTS `spring`.`workout` (
`id` INT NOT NULL AUTO_INCREMENT,
`user` VARCHAR(45) NULL,
`start` DATETIME NULL,
`end` DATETIME NULL,
`difficulty` INT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `spring`.`workout` (`id`, `user`, `start`, `end`, `difficulty`) VALUES (1, 'bill', '2020-06-10 15:05:05', '2020-06-10 16:10:07', '3');
INSERT INTO `spring`.`workout` (`id`, `user`, `start`, `end`, `difficulty`) VALUES (2, 'rachel', '2020-06-10 15:05:10', '2020-06-10 16:10:20', '3');
INSERT INTO `spring`.`workout` (`id`, `user`, `start`, `end`, `difficulty`) VALUES (3, 'bill', '2020-06-12 12:00:10', '2020-06-12 13:01:10', '4');
INSERT INTO `spring`.`workout` (`id`, `user`, `start`, `end`, `difficulty`) VALUES (4, 'rachel', '2020-06-12 12:00:05', '2020-06-12 12:00:11', '4');
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.my</groupId>
<artifactId>KeycloakProject</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>KeycloakProject</name>
<description>KeycloakProject</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-data</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

Using Feign builder requests doesn't send trace-id, span-id to child clients but using rest template is showing all headers on child clients

I'm making a sequential request using Feign Builder. There are no x-b3-traceid,x-b3-spanid .. in the title of the request. That's why the log my last client appears on the zipkin.
I use spring boot 2.4.2 , spring cloud 2020.0.0 , feign-core 10.10.1 , feign-okhttp 10.10.1. I have tried spring-cloud-openfeign and i achieved wanted result. But i don't want to use this lib. There are requests when using Feign Builder and Rest Template in here. I dont' see same log at zipkin.
My Client1 App. I am sending request http://localhost:8082/
import feign.Client;
import feign.Feign;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import sample.feign2.Client2Feign;
import javax.servlet.http.HttpServletRequest;
#RestController
public class SampleController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private RestTemplate restTemplate;
#Autowired
private Client client;
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
#Bean
public OkHttpClient okHttpClient(){
return new OkHttpClient();
}
#GetMapping("/hello/feignBuilder")
public String sayHelloFeignBuilder(HttpServletRequest httpServletRequest){
logger.info("Send to request client2");
Client2Feign client2Feign = Feign.builder()
.client(client)
.decoder(new Decoder.Default())
.encoder(new Encoder.Default())
.target(Client2Feign.class, "http://localhost:8082/");
return client2Feign.sayHelloFeignBuilder();
}
#GetMapping("/hello/say")
public String sayHelloFeignBuilder1(){
return "Hello";
}
#GetMapping("/hello/rest")
public String sayHelloRest(){
System.out.println(tracer.currentSpan());
logger.info("Inside rest 1");
String baseUrl = "http://localhost:8082/sayHelloRest";
String response = (String) restTemplate.exchange(baseUrl, HttpMethod.GET, null, String.class).getBody();
logger.info("The response received by client2 is " + response);
return response;
}
}
This is yml of my client1 app. I use same yml conf on other client apps which client2 and clint3. Only changes port and app name.
server:
port: 8081
spring:
application:
name: euraka-client1
zipkin:
enabled: true
service.name: euraka-client1
sender.type: web
base-url: http://localhost:9411
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
instance:
preferIpAddress: true
This is my Feign at Client2 app.
import feign.RequestLine;
public interface Client2Feign {
#RequestLine("GET /sayHelloBuilder")
String sayHelloFeignBuilder();
}
Here impl of ClientFeign2.
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletRequest;
public interface FeignBuilderController {
#GetMapping("/sayHelloBuilder")
String sayHelloBuilder(HttpServletRequest httpServletRequest);
#GetMapping("/sayHelloRest")
String sayHelloRest(HttpServletRequest httpServletRequest);
}
import feign.Client;
import feign.Feign;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import sample.feign.Client3Feign;
import sample.feign2.Client2Feign;
import javax.servlet.http.HttpServletRequest;
#RestController
public class FeignBuilderControllerImpl implements FeignBuilderController{
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private RestTemplate restTemplate;
#Autowired
private Client client;
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
#Bean
public OkHttpClient okHttpClient(){
return new OkHttpClient();
}
#Override
public String sayHelloBuilder(HttpServletRequest httpServletRequest) {
logger.info("Send to request client3");
Client3Feign client3Feign = Feign.builder()
.client(client)
.decoder(new Decoder.Default())
.encoder(new Encoder.Default())
.target(Client3Feign.class, "http://localhost:8083/");
return client3Feign.sayHelloFeignBuilder3();
}
#Override
public String sayHelloRest(HttpServletRequest httpServletRequest) {
logger.info("Inside rest 2");
String baseUrl = "http://localhost:8083/sayHelloRestClient3";
String response = (String) restTemplate.exchange(baseUrl, HttpMethod.GET, null, String.class).getBody();
logger.info("The response received by client3 is " + response);
return response;
}
This is my Feign at Client3 app.
import feign.RequestLine;
public interface Client3Feign {
#RequestLine("GET /sayHelloBuilderClient3")
String sayHelloFeignBuilder3();
}
Here impl of Client3 Feign.
import javax.servlet.http.HttpServletRequest;
public interface FeignController3 {
#GetMapping("/sayHelloBuilderClient3")
String sayHello3(HttpServletRequest httpServletRequest);
#GetMapping("/sayHelloRestClient3")
String sayHelloRest3(HttpServletRequest httpServletRequest);
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
#RestController
#RequestMapping
public class FeignController3Impl implements FeignController3 {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Override
public String sayHello3(HttpServletRequest httpServletRequest) {
logger.info("Response returned from client3");
return "Hello from client3 using FeignClientBuilder";
}
#Override
public String sayHelloRest3(HttpServletRequest httpServletRequest) {
logger.info("Response returned from client3");
return "Hello from client3 using RestClient";
}
}
pom.xml from client3 and i use client3 at client2/pom and the same as client1.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>oguzhan.example</groupId>
<artifactId>client3</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>build-info</id>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<release>${java.version}</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
diff-feign-rest-image
feign-zipkin-img
feign-request-img
rstlet-rest-tmplte-img
rest-zipkin-img
The problem might be related to the fact that you're creating the Feign builder manually via Feign.builder() factory method. We're unable to instrument that call. You should create a bean (via SleuthFeignBuilder.builder) and inject that into your code.

There was an unexpected error (type=Not Found, status=404). No message available

I am getting below error:
There was an unexpected error (type=Not Found, status=404). No message available
While using Spring Boot + Thymeleaf
Code:
Application class:
package com.mycom.extract;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
#SpringBootApplication(scanBasePackages = "com.mycom.extract")
public class MyAppExtractionWebApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(MyAppExtractionWebApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyAppExtractionWebApplication.class);
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycom.extract</groupId>
<artifactId>MyApp-extraction</artifactId>
<version>1</version>
<packaging>war</packaging>
<name>MyAppExtractionWeb</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SecurityConfig
package com.mycom.extract.config.security;
import org.springframework.beans.factory.annotation.Autowired;
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.web.util.matcher.AntPathRequestMatcher;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyAppAuthenticationProvider authProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**", "/login").permitAll()
.antMatchers("/*").fullyAuthenticated()
.and()
.formLogin().loginPage("/login")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("ipn").passwordParameter("password")
.defaultSuccessUrl("/home").failureUrl("/login?error")
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login");
http.csrf().disable();
}
}
WebMvcConfiguration
package com.mycom.extract.config;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#EnableAutoConfiguration
#Configuration
#ComponentScan(basePackages = { "com.mycom.extract" })
public class MyAppConfiguration extends WebMvcConfigurerAdapter{
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/", "/login");
registry.addViewController("/login").setViewName("index");
registry.addViewController("/home").setViewName("home");
}
}
Referring all same kind of post in Stackoverflow could find solution for myself.
Please suggest.
URL http://localhost:8080/MyAppExtractionWeb/
As I am setting up login, and authentication is handled at security config side, so once it is successful it shows home page. Hence no controller mentioned.
package com.mycom.config.security;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import org.springframework.beans.factory.annotation.Autowired;*/
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import com.mycom.entity.User;
import com.mycom.service.UserService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
#Configuration
public class MyAppAuthenticationProvider implements AuthenticationProvider {
private static final String SECURITY_DOMAIN = "MyApp-ldapdomain";
#Autowired
private UserService userService;
#Override
public Authentication authenticate(Authentication authentication) {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
try {
CallbackHandler handler = new UserPasswordHandler(name, password);
LoginContext lc = new LoginContext(SECURITY_DOMAIN, handler);
lc.login();
if(lc.getSubject() != null) {
User user = userService.loadUserByUsername(name);
Collection<? extends GrantedAuthority> roles = null;
if(!user.getAuthorities().isEmpty()) {
roles = user.getAuthorities();
}
return new UsernamePasswordAuthenticationToken(name, password,roles);
} catch (Exception e) {
return null;
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}

How to enable basic caching with Spring Data JPA

I am trying to enable basic caching with Spring Data JPA. But I cannot understand why the DAO methods are still querying the database instead of using the cache.
Given the following Spring Boot 1.5.1 application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class Server{
public static void main(String[] args) {
SpringApplication.run(Server.class, args);
}
}
Controller
#Controller
public class PasswordsController {
#Autowired
private PasswordService service;
#SuppressWarnings("unchecked")
#RequestMapping("/passwords.htm")
public void passwords(Map model,
HttpServletRequest request) {
model.put("passwords", service.getPasswords(request));
}
...
Service
#Service
#Transactional
public class PasswordService extends BaseService {
#Autowired
private PasswordJpaDao passwordDao;
public Collection<Password> getPasswords(HttpServletRequest request) {
Collection<Password> passwords = passwordDao.getPasswords(params);
return passwords;
}
...
Interface
#Transactional
public interface PasswordJpaDaoCustom {
public Collection<Password> getPasswords(PasswordSearchParameters params);
}
and implementation
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;
import com.crm.entity.Password;
import com.crm.search.PasswordSearchParameters;
#Transactional
#Repository
public class PasswordJpaDaoImpl implements PasswordJpaDaoCustom {
#PersistenceContext
private EntityManager em;
#Override
#Cacheable("passwords")
public Collection<Password> getPasswords(PasswordSearchParameters params) {
System.err.println("got here");
return em.createQuery(hql, Password.class);
}
...
Maven Dependencies
<!-- Spring Boot start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Spring Boot end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
I understand that Spring Boot will implicitly use ConcurrentHashMap for caching without any specific configuration necessary?
But the getPasswords() dao method is always called instead of using the cache. Why is this?
Yes, spring boot by default uses ConcurrentHashMap for caching and the issue with your code is that you did not set any key for your passwords cache, so it is calling the database every time for fetching the data.
So you need to the key (any unique identifier) using the params object variables as shown below:
#Cacheable(value="passwords", key="#params.id")//any unique identifier
public Collection<Password> getPasswords(PasswordSearchParameters params) {
System.err.println("got here");
return em.createQuery(hql, Password.class);
}

Spring MockMvc trimming parameter

I have run into a problem that has me stumped. I am running the latest version of Spring and JUnit. I was experimenting with the MockMvc and Spring integration test classes in order to see if it would fit my companies needs and it was working well, until I tried to submit a request for that contained a #PathVariable which is a version number in the url.
Pom;
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<groupId>asd</groupId>
<artifactId>asd</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>asd</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.1.6.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Example Controller;
import junit.framework.Assert;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ExampleController {
#RequestMapping(value = "/example/appVersion/{applicationVersion}")
public #ResponseBody void example(#PathVariable("applicationVersion") String applicationVersion) {
if (!"1.0.0.0".equals(applicationVersion)) {
Assert.fail("I needed to be 1.0.0.0");
}
}
}
Test Class;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.web.context.WebApplicationContext;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath*:appContext.xml")
#WebIntegrationTest
public class ExampleProblem {
#Autowired
WebApplicationContext context;
#Test
public void test() throws Exception {
String url = "/example/appVersion/1.0.0.0";
// String url = "/example/appVersion/{applicationVersion}";
//#formatter:off
MvcResult result = webAppContextSetup(context)
.build()
.perform(
get(url)
// get(url, "1.0.0.0")
.accept("application/json")
).andDo(print())
.andExpect(status().is(200))
.andReturn();
//#formatter:on
Assert.assertNotNull(result);
}
}
My appContext.xml file only contains this line (other than the initial beans tag and end tag);
<context:component-scan base-package="asd" />
When I view the request coming into my example controller, I can see with the debugger it says /example/appVersion/1.0.0.0 but the path variable is being set to "1.0.0" always. It appears the last .0 is always being dropped because if I pass in "1.0.0.0.0" it trims to the expected "1.0.0.0".
Any insight into what may be causing this or what I am doing wrong would be greatly appreciated.
Thank you.
This happens because Spring interprets the last dot (.) and following characters as a file extension.
In order to fix this, you need to tell Spring that the parameter expects dot characters by adding ":.+" to the end of the controller parameter.
So the annotation now looks like this;
#RequestMapping(value = "/example/appVersion/{applicationVersion:.+}")

Resources