eureka: Custom clientFilter is useless - spring-cloud

For more complex needs you can create a #Bean of type
DiscoveryClientOptionalArgs and inject ClientFilter instances into it,
all of which will be applied to the calls from the client to the
server.
this description in spring-cloud wiki
#Bean
public DiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
DiscoveryClientOptionalArgs discoveryClientOptionalArgs = new DiscoveryClientOptionalArgs();
discoveryClientOptionalArgs.setAdditionalFilters(Collections.singletonList(new IpCilentFilter()));
return discoveryClientOptionalArgs;
}
public class IpCilentFilter extends ClientFilter {
#Override
public ClientResponse handle(ClientRequest clientRequest) throws ClientHandlerException {
// here How to disallow clients to connect
return getNext().handle(clientRequest);
}
}
How to disallow clients to connect?
throws ClientHandlerException useless
return null useless
what should I do?

Related

Consuming SOAP Web Services with ActiveMQ in Spring Boot

I am new to Web Service development, currently building a SOAP Web Service with Spring Boot 2.7.0, Java 17.
As well as a client application that communicates with this soap service via JMS.
But I do not know the procedure of the process.
The way I see it -> The client application (Producer) sends a message to a queue that lives on the server side (Consumer), the queue pops the message when ready to consume and redirects it to the endpoint handler method and then sends a response in the response queue back to the client side.
However, I don't know how to redirect the JMS message to the endpoint. Nor do I know how to send it back. I have read all of the documentations related to "SOAP over JMS", CXF-SOAP-JMS", "ActiveMQ with Spring", etc... None of them helped me fix this problem.
Using SOAP with http is pretty easy by exploiting the "WebServiceTemplate", provided by Spring-WS API. But when I tried using it over JMS I encountered several problems, including the following:
What to do with the JMS Message once in the destination object?
How do I send it specifically to my endpoint handler method?
What and how do I send back to the response destination?
Sample code of what I've tried latest
CLIENT APP
Client Configuration
#Configuration
public class SoapClientConfiguration {
#Value("${spring.activemq.broker-url}")
private String activeMqUrl;
#Value("${spring.activemq.user}")
private String userName;
#Value("${spring.activemq.password}")
private String password;
#Bean
Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.mile.soap.client.app.quiz");
return marshaller;
}
#Bean
WebServiceTemplate template() {
return new WebServiceTemplate(jaxb2Marshaller());
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(mqConnectionFactory());
return factory;
}
#Bean
public SingleConnectionFactory mqConnectionFactory(){
SingleConnectionFactory factory = new SingleConnectionFactory();
ActiveMQConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory();
mqConnectionFactory.setBrokerURL(activeMqUrl);
mqConnectionFactory.setUserName(userName);
mqConnectionFactory.setPassword(password);
factory.setTargetConnectionFactory(mqConnectionFactory);
return factory;
}
#Bean
public JmsTemplate jmsTemplate(){
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(mqConnectionFactory());
return template;
}
Client Service
#Service
public class SoapClient extends WebServiceGatewaySupport{
#Autowired WebServiceTemplate template;
#Autowired JmsTemplate jmsTemplate;
public CategoriesResponse getCategories() {
CategoriesResponse response = new CategoriesResponse();
try {
SAAJResult soapRequest = new SAAJResult();
template.getMarshaller().marshal(new GetCategoriesRequest(), soapRequest);
Message m = jmsTemplate.sendAndReceive("example.queue", new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
return session.createObjectMessage(soapRequest.toString());
}
});
response = m.getBody(CategoriesResponse.class);
}
catch (Exception e) {
e.printStackTrace();
}
return response;
}
SERVER SIDE APP
ActiveMQ Configuration
#Configuration #EnableJms
public class ActiveMqConfig {
#Value("${spring.activemq.broker-url}")
private String activeMqUrl;
#Value("${spring.activemq.user}")
private String userName;
#Value("${spring.activemq.password}")
private String password;
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(mqConnectionFactory());
return factory;
}
#Bean
public SingleConnectionFactory mqConnectionFactory(){
SingleConnectionFactory factory = new SingleConnectionFactory();
ActiveMQConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory();
mqConnectionFactory.setBrokerURL(activeMqUrl);
mqConnectionFactory.setUserName(userName);
mqConnectionFactory.setPassword(password);
factory.setTargetConnectionFactory(mqConnectionFactory);
return factory;
}
}
Main Configuration (WSDL/SERVLET)
#Configuration
#EnableWs
public class SoapConfiguration extends WsConfigurerAdapter{
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus(){
SpringBus bus = new SpringBus();
return bus;
}
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(
ApplicationContext applicationContext, SpringBus springBus){
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/*");
}
//wsdl
#Bean(name = "quiz") #SneakyThrows
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema schema) {
DefaultWsdl11Definition defaultWsdl11Definition = new DefaultWsdl11Definition();
defaultWsdl11Definition.setPortTypeName("QuizMainEndPoint");
defaultWsdl11Definition.setLocationUri("/");
defaultWsdl11Definition.setTargetNamespace("http://www.mile.com/collection/management/soap/Quiz");
defaultWsdl11Definition.setTransportUri("http://www.openuri.org/2002/04/soap/jms/");
defaultWsdl11Definition.setSchema(schema);
return defaultWsdl11Definition;
}
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
EndpointInterceptor endpointInterceptor = new PayloadRootSmartSoapEndpointInterceptor(
new QuizMainEndpointInterceptor(), "http://www.mile.com/collection/management/soap/Quiz", "GetCategoriesRequest");
interceptors.add(endpointInterceptor);
}
#Bean
public XsdSchema schema() {
return new SimpleXsdSchema(new ClassPathResource("/schemas/QuizSchema/quiz.xsd"));
}
}
Listener
#Component
public class Listener {
#JmsListener(destination = "example.queue")
public void listenRequests(Message message) {
System.out.println(message.toString());
/*I RECEIVE THE MESSAGE BUT I HAVE NO IDEA WHAT TO DO WITH IT.
* HOW DO I CONSUME IT?
*/
}
}
Method in a class annotated with #Endpoint
#ResponsePayload
#PayloadRoot(namespace = NAMESPACE, localPart = "GetCategoriesRequest")
public CategoriesResponse getCategories( #RequestPayload GetCategoriesRequest request) {
CategoriesResponse response = new CategoriesResponse(service.getCategories());
/*
* How to CONVERT my JMS Message living in the Destination Object - "example.queue" To a SOAP Message
* and be RECOGNISED by this exact method??
Do i send a JMS response here or somewhere else?
Is it sent by default?
*/
return response;
}
Thank you for reading thoroughly. I'd appreciate any kind of help.

Vertx web routes and Reactive Pg Client issue in Quarkus

The application is based on the following stack:
Quarkus 1.5.0
Extensions: vertx-web, reactive-pgclient
The complete codes is here.
I created a Router by #Observes Router.
#ApplicationScoped
public class RoutesObserver {
#Inject PostsHandlers handlers;
public void route(#Observes Router router) {
router.get("/posts").produces("application/json").handler(handlers::getAll);
router.post("/posts").consumes("application/json").handler(handlers::save);
router.get("/posts/:id").produces("application/json").handler(handlers::get);
router.put("/posts/:id").consumes("application/json").handler(handlers::update);
router.delete("/posts/:id").handler(handlers::delete);
router.get("/hello").handler(rc -> rc.response().end("Hello from my route"));
}
}
And extracted the handlers into a standalone bean.
#ApplicationScoped
class PostsHandlers {
private static final Logger LOGGER = Logger.getLogger(PostsHandlers.class.getSimpleName());
PostRepository posts;
ObjectMapper objectMapper;
#Inject
public PostsHandlers(PostRepository posts, ObjectMapper objectMapper) {
this.posts = posts;
this.objectMapper = objectMapper;
}
public void getAll(RoutingContext rc) {
this.posts.findAll().thenAccept(
data -> rc.response()
.write(toJson(data))
.end()
);
}
//... other methods.
}
And the PostRepository used the Java 8 CompletionStage API.
#ApplicationScoped
public class PostRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(PostRepository.class);
private final PgPool client;
#Inject
public PostRepository(PgPool _client) {
this.client = _client;
}
public CompletionStage<List<Post>> findAll() {
return client.query("SELECT * FROM posts ORDER BY id ASC")
.execute()
.thenApply(rs -> StreamSupport.stream(rs.spliterator(), false)
.map(this::from)
.collect(Collectors.toList())
);
}
And when I ran this application and tried to access the /posts. It is frozen and no response printed.
When using the write method, you need to set (beforehand) the content-length header.
There are a several approaches to handle this:
You can use .end(toJson(data)) instead of write(...).end() - it will computed the length automatically
You can use .putHeader("transfer-encoding", "chunked") and you write(...).end() - if you plan to retrieve multiple results, it's interesting as it writes each chunk to the client one by one, avoiding sending a large payload in one go
you can set the content-length as in:
String result = toJson(data);
rc.response()
.putHeader("content-length", Long.toString(result.length()))
.write(result)
.end();

Temporary redirect with JAX-RS client API

I am trying integrate my current project with a external authentication API, and right now my goal it is redirect to a external url:
https://auth.mercadolivre.com.br/authorization?response_type=code&client_id=$APP_ID
where the autorization process takes place, after that it's redirect back to my application, with an url like that:
http://YOUR_REDIRECT_URI?code=SERVER_GENERATED_AUTHORIZATION_CODE
where I need store this code variable internally.
I got this code so far, based on the examples available here and here:
public String getCode() throws ApiException, URISyntaxException {
Client client = ClientBuilder.newClient();
WebTarget resourceTarget = client.target(getApi().getLocation());
// Build a HTTP GET request that accepts "text/plain" response type
// and contains a custom HTTP header entry "Foo: bar".
Invocation invocation = resourceTarget.request("text/plain").buildGet();
// Invoke the request using generic interface
String response = invocation.invoke(String.class);
return response;
}
#POST
public Response getApi() throws ApiException, URISyntaxException {
getAuthUrl();
URI targetURIForRedirection = new URI(auth_url);
return Response.temporaryRedirect(targetURIForRedirection).build();
}
But, despite the application reaching the destination, instead of being open in the browser, the html is dumped on the console and an error is issued (something like an invalid character on the code dumped on the console).
I just wan, from the methods above, redirect the user to the authorization page (first link), and when the process ends, execute the rest of the code, storing the value returned for future uses.
For reference, this code it is called from the AuthenticationManager in my spring-security layer. The implementation I got so far:
#Configuration
#EnableWebSecurity
public class Security extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return new AuthManager();
}
#Override
public void configure(HttpSecurity http) throws Exception {
...
}
#Override
public void configure(WebSecurity web) throws Exception {
...
}
public class AuthManager implements AuthenticationManager {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
MercadoLivre mercadoLivre = new MercadoLivre();
try {
mercadoLivre.getAccessToken();
UserResponse data = (UserResponse) mercadoLivre.GET("/users/"+mercadoLivre.getUserId().toString());
return new AuthResponse(data);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class AuthResponse implements Authentication {
...
}
}
the method is called from inside getAccessToken().

Connecting OAuth2 resource server with authentication server

I'm trying to make a sample OAuth2 Spring authorization and resource server. My intention is to implement two separate applications - one representing authorization server ant the other representing resource server. Since I'm quite a beginner in Spring Security, I guess I need some guidance to complete my task.
I already managed to implement a simple authorization server using in-memory token store (app named "OAuth").
AuthServerOAuth2Config.java
#Configuration
#EnableAuthorizationServer
public class AuthServerOAuth2Config extends AuthorizationServerConfigurerAdapter {
private static final String RESOURCE_ID = "myResource";
#Autowired
private UserApprovalHandler handler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.inMemory()
.withClient("test")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds(RESOURCE_ID)
.secret("test")
.accessTokenValiditySeconds(300).//invalid after 5 minutes.
refreshTokenValiditySeconds(600);//refresh after 10 minutes.
// #formatter:on
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).userApprovalHandler(handler).authenticationManager(authManager);
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
OAuth2SecurityConfig.java
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(OAuth2SecurityConfig.class);
#Autowired
private ClientDetailsService clientService;
#Autowired
private DataSource dataSource;
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
// #formatter:off
auth.inMemoryAuthentication()
.withUser("javabycode").password("123456").roles("USER")
.and()
.withUser("admin").password("admin123").roles("ADMIN");
// #formatter:on
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll();
// #formatter:on
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientService));
handler.setClientDetailsService(clientService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
Accessing http://localhost:9081/OAuth/oauth/token?grant_type=password&username=admin&password=admin123 returns token as expected, so I'm guessing that authorization server is configured ok.
Now there's a resource server part (app named "RestTest"). I've managed to find some examples using RemoteTokenServices to access token service that resides in another app. So here's my resource server so far.
OAuth2ResourceConfig.java
#Configuration
#EnableResourceServer
#EnableWebSecurity
public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "myResource";
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.
anonymous().disable()
.requestMatchers().antMatchers("/v1/**")
.and().authorizeRequests()
.antMatchers("/v1/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws
Exception {
resources.tokenServices(tokenService()).resourceId(RESOURCE_ID).stateless(true);
}
#Primary
#Bean
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl("http://localhost:9081/OAuth/oauth/check_token/");
tokenService.setClientId("test");
tokenService.setClientSecret("test");
return tokenService;
}
}
I'm trying to secure my REST API (http://localhost:9081/RestTest/v1/foobar) so I believe that configuration above is correct, right? Problem is that when I access v1/foobar endpoint (via Postman) it's accessible without any authentication. So I think I'm simply missing some part of configuration, but I can't figure it out how to connect to authorization server correctly. One more thing to mention - I'm not using Spring Boot!
I'd really appreciate some guidance to make my sample work. Thanks!
EDIT1: I've added resourceId to both authentication and resource server - no luck. Is resourceId even mandatory?
You should add RESOURCE_ID both in ResourceServer and AuthorizationServer in a way that, (you updated your question though with that snippet)
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenService()).resourceId(RESOURCE_ID).stateless(true);
}
And in your auth server
.scopes("read", "write", "trust").resourceIds(RESOURCE_ID)
Add a springSecurityFilterChain as you missing that in web.xml that you already said in comment
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
From spring docs:
It creates a Servlet Filter known as the springSecurityFilterChain which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, etc) within your application.

Get AccessToken from spring cloud zuul API Gateway

We are using zuul as API gateway in spring cloud. Now we want to extract access token from zuul for further implementation.Please provide suggestion how we want to implement. Thank you
To read the authorization header you will need to create a filter in ZUUL my thought is you will need a pre filter you can change it based on your need. Here is what you will need.
public class TestFilter extends ZuulFilter {
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
final RequestContext ctx = RequestContext.getCurrentContext();
final HttpServletRequest request = ctx.getRequest();
//Here is the authorization header being read.
final String xAuth = request.getHeader("Authorization");
//Use the below method to add anything to the request header to read downstream. if needed.
ctx.addZuulRequestHeader("abc", "abc");
return null;
}
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 1;
}
}
You will need a #Bean declaration for Filter in the class where you have #EnableZuulProxy
#Bean
public TestFilter testFilter() {
return new TestFilter();
}
Hope this helps.!!!