Expose/Filter Controller Request Mappings by Port/Connector - spring

I have a relatively simple Spring Boot application that, by default, is secured over SSL on port 9443 using a self-signed certificate, which works great for serving up APIs to, say, a mobile app. However, I would now like to develop an unsecured web application with its own frontend and serve up a subset of the content I allow over SSL.
This is what I've come up with so far, which enables port 8080 over HTTP in addition to port 9443, the latter I've defined in application.properties:
#SpringBootApplication
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addAdditionalTomcatConnectors(createWebsiteConnector());
return tomcat;
}
private Connector createWebsiteConnector() {
Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
connector.setPort(8080);
return connector;
}
}
I am now faced with the task of only exposing endpoints to the 8080 connection, and all of them to 9443. Obviously, the latter currently works by default, but right now 8080 can access everything 9443 can. Ideally, I would like to control access to certain request mappings defined in a "shared" controller that both connections have access to, i.e. something like this:
#RequestMapping(value = "/public", method = RequestMethod.GET)
public List<String> getPublicInfo() {
// ...
}
#HTTPSOnly
#RequestMapping(value = "/secured", method = RequestMethod.GET)
public List<String> getSecuredInfo() {
// ...
}
I assume something like what I have above isn't actually possible, but does anyone know how I could achieve the same effect?
Thanks in advance for any help!

Alright, I think I actually managed to solve this myself, but I'm open to other suggestions if anyone thinks they have a better solution:
#SpringBootApplication
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addAdditionalTomcatConnectors(createWebsiteConnector());
return tomcat;
}
private Connector createWebsiteConnector() {
Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
connector.setPort(8080);
return connector;
}
private static HashSet<String> uriWhitelist = new HashSet<>(4);
static {
// static website content
uriWhitelist.add("/");
uriWhitelist.add("/index.html");
uriWhitelist.add("/logo_48x48.png");
// public APIs
uriWhitelist.add("/public");
}
private static class PortFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request instanceof RequestFacade) {
RequestFacade requestFacade = (RequestFacade) request;
if (requestFacade.getServerPort() != 9443 && !uriWhitelist.contains(requestFacade.getRequestURI())) {
// only allow unsecured requests to access whitelisted endpoints
return;
}
}
filterChain.doFilter(request, response);
}
}
#Bean
public FilterRegistrationBean portFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
PortFilter filter = new PortFilter();
filterRegistrationBean.setFilter(filter);
return filterRegistrationBean;
}
}

Related

Springboot Jersey - Custom Filter with ServletProperties.FILTER_STATIC_CONTENT_REGEX

I have a Springboot Application running with a Jersey config line property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, "/mywebpage/.*") to enable rendering static html/js/css/image... content for that specific path prefix. All is fine and mywebpage is loading perfectly on /mywebpage/index.html.
Now after running mywebpage for a few months, we want to redirect a percentage of users (those enabled for a beta) to a new webpage (say https://stackoverflow.com/). So I'm trying to write a Filter for the path /mywebpage/index.html to redirect the user to the new page if they are enabled for the beta. Now the part that stumped me, is that for some reason, no filters are being invoked for any calls to /mywebpage/.* (calls made by the browser to get the html/js/css/... content) (checked with breakpoints and dummy logs). I assume the suspect would be the property ServletProperties.FILTER_STATIC_CONTENT_REGEX.
I already have the bit to compute whether the user is beta enabled, and just need to fit it in the Filter.
Here's what I was trying:
#Provider
public class RedirectionFilter implements ContainerRequestFilter {
#Autowired
private BetaAudienceService betaAudienceService;
#Context
private UriInfo info;
private static final Logger log = LogManager.getLogger(getClass());
#Override
public void filter(ContainerRequestContext request) throws IOException {
log.info("Test Log :: Path=" + request.getUriInfo().getAbsolutePath().getPath());
if (request.getUriInfo().getAbsolutePath().getPath().equals("/mywebpage/index.html") && isBetaEnabled(request)) {
try {
request.abortWith(Response.temporaryRedirect(new URI("https://stackoverflow.com/")).build());
} catch (URISyntaxException e) {
throw new IOException("Failed to build or respond with Redirection URI", e);
}
}
}
private boolean isBetaEnabled(ContainerRequestContext request) { ... }
}
and
public class JerseyConfig extends ResourceConfig {
#Autowired
private ApplicationContext appCtx;
#PostConstruct
public void setup() {
register(A.class);
register(B.class);
...
}
public JerseyConfig() {
property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, Constants.STATIC_CONTENT_PATH);
}
}
Any suggestions on how I can get around this? Or probably is my approach wrong altogether?
I was able to figure this out.
In this case, Jersey is set to run as a Filter (since we need to use ServletProperties.FILTER_STATIC_CONTENT_REGEX) and Jersey handles all the service requests except for the specific paths /mywebpage/* where Jersey is configured to ignore the request due to the static content filter property. Hence those ignored/filtered requests are handled by Springboot directly, which means we can just use a Springboot filter.
Here's the code for the Filter:
public class RedirectionFilter extends OncePerRequestFilter /* can also just `implements Filter` instead */ {
public static final String REDIRECT_PATH = "/mywebpage/index.html";
private static final Logger log = LogManager.getLogger(RedirectionFilter.class);
private final MyConfig myConfig;
public UnifiedShellRedirectionFilter(MyConfig myConfig) {
this.myConfig = myConfig;
}
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
log.info("Test Log :: Path: " + request.getPathInfo());
if (isBetaEnabled(request)) {
response.sendRedirect(myConfig.getRedirectionEndpoint() /* "https://stackoverflow.com/" */);
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
log.info("Destructing Filter: " + this.getClass().getSimpleName());
}
private boolean isBetaEnabled(HttpServletRequest request) { ... }
}
And put the following in your #Configuration class:
#Bean
// #Autowired
public FilterRegistrationBean registerRedirectionFilter(MyConfig myConfig) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setName(RedirectionFilter.class.getSimpleName());
registrationBean.setFilter(new UnifiedShellRedirectionFilter(myConfig));
registrationBean.addUrlPatterns(RedirectionFilter.REDIRECT_PATH);
return registrationBean;
}

SPRING-WS No marshaller registered. Caused by SPRING IOC

I have a SOAP client service which works fine.
The SOAP headers and request are managed in a SOAPConnector class.
public class SOAPConnector extends WebServiceGatewaySupport {
public Object callWebService(String url, Object request) {
// CREDENTIALS and REQUEST SETTINGS...
return getWebServiceTemplate().marshalSendAndReceive(url, request, new SetHeader(requestHeader));
}
}
I'm receiving the requested Data once I call my (SoapConnector) service on the main Class.
#SpringBootApplication
public class SpringSoapSecurityDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSoapSecurityDemoApplication.class, args);
}
#Bean
public CommandLineRunner lookup(SOAPConnector soapConnector) {
return args -> {
String hotelCode = "****";
FutureBookingSummaryRequest request = new FutureBookingSummaryRequest();
FetchBookingFilters additionalFilters = new FetchBookingFilters();
// Some additionalFilters settings
request.setAdditionalFilters(additionalFilters);
FutureBookingSummaryResponse response = (FutureBookingSummaryResponse) soapConnector
.callWebService("MY WSDL URL", request);
System.err.println(response.getHotelReservations());
};
}
}
SO FAR IT WORKS FINE.
Then I've tried to create a separate service for the previous request.
BookingService.java
public class BookingService extends WebServiceGatewaySupport {
#Autowired
SOAPConnector soapConnector;
public String getReservations() {
String hotelCode = "****";
FutureBookingSummaryRequest request = new FutureBookingSummaryRequest();
FetchBookingFilters additionalFilters = new FetchBookingFilters();
// Some additionalFilters settings
request.setAdditionalFilters(additionalFilters);
FutureBookingSummaryResponse response = (FutureBookingSummaryResponse) soapConnector
.callWebService("MY WSDL URL", request);
System.err.println(response.getHotelReservations());
};}
In order to inject the SOAPCONNECTOR I've added #Service to SOAPCONNECTOR class , and #Autowired SOAPConnector soapConnector to the service calling it
Now once I call the created BookingService in the main class
#SpringBootApplication
public class SpringSoapSecurityDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSoapSecurityDemoApplication.class, args);
BookingService bookingService = new BookingService();
bookingService.getReservations();
}
}
The SOAPCONNECTOR stops working an I receive the following error :
No marshaller registered. Check configuration of WebServiceTemplate.
I'm pretty sure that's this issue is related to SPRING IOC , dependecy injection .. Since the SOAP service is well configured and working..
Note : I've checked this similiar question
Consuming a SOAP web service error (No marshaller registered. Check configuration of WebServiceTemplate) but the #Autowired didn't solve the issue.
Any help ?
In case someone is facing the same issue, it turned out that I've missed the #Configuration annotation on the beans configuration class. The right one should look like the following:
#Configuration
public class ConsumerConfig {
private String ContextPath = "somePackage";
private String DefaultUri = "someWsdlURI";
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this package must match the package in the <generatePackage> specified in
// pom.xml
marshaller.setContextPath(ContextPath);
return marshaller;
}
#Bean
public SOAPConnector checkFutureBookingSummary(Jaxb2Marshaller marshaller) {
SOAPConnector client = new SOAPConnector();
client.setDefaultUri(DefaultUri);
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}

After enabling Zuul proxy for the Spring boot app (We have set of miroservices), spring mvc interceptor doesnot work

Below is the Application.java. It has code to invoke the interceptor
#EnableEurekaClient
#SpringBootApplication
#EnableZuulProxy
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public TokenValidateInterceptor tokenValidateInterceptor() {
TokenValidateInterceptor localeChangeInterceptor = new TokenValidateInterceptor();
System.out.println("In WEB MVC Intereptor, Interceptor returned");
return localeChangeInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("In WEB MVC Intereptor");
// registry.addInterceptor(tokenValidateInterceptor()).addPathPatterns("/");
registry.addInterceptor(tokenValidateInterceptor()).addPathPatterns("/api/**");
// registry.addInterceptor(new
// TokenValidateInterceptor()).addPathPatterns("/api/**");
}
}
Below is the snippet of the interceptor code:
#Component
public class TokenValidateInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOG = Logger.getLogger(TokenValidateInterceptor.class);
// before the actual handler will be executed ..
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String apikey = request.getHeader("apikey");
// if (LOG.isDebugEnabled()) {
LOG.info("#### Starting TokenValidateInterceptor.preHandle ####");
LOG.info("apikey-->" + apikey);
// }
if (StringUtils.isBlank(apikey) || apikey == null || apikey.isEmpty()) {
return true;
}
}
}
But the call does not reach prehandle of the interceptor.
AFAIK, all requests that are defined in Zuul routing are handled by ZuulServlet.
Spring MVC doesn't handle these requests, so any Spring HandlerInterceptor will not be called for these requests. If you need any preprocessing for API requests, you should implement it in Zuul prefilter or servlet filter.

Springs "#MessagingGateway" annotation on external interfaces

I am about to migrate a project created with an old version of Spring (using XML config) to Spring Boot (using Java config).
The project is using Spring Integration for communication via JMS and AMQP. As far as I understood, I have to replace the
<int:gateway id="someID" service-interface="MyMessageGateway"
default-request-channel="myRequestChannel"
default-reply-channel="myResponseChannel"
default-reply-timeout="20000" />
with
#MessagingGateway(name="someID", defaultRequestChannel = "myRequestChannel",
defaultReplyChannel = "myResponseChannel", defaultReplyTimeout = "20000")
public interface MyMessageGateway{ ...... }
My problem is, that the interface, that is in use now, is placed in a library I can't access.
How can I define this Interface as my MessagingGateway?
Thanks in advance!
I have just tested this trick:
interface IControlBusGateway {
void send(String command);
}
#MessagingGateway(defaultRequestChannel = "controlBus")
interface ControlBusGateway extends IControlBusGateway {
}
...
#Autowired
private IControlBusGateway controlBus;
...
try {
this.bridgeFlow2Input.send(message);
fail("Expected MessageDispatchingException");
}
catch (Exception e) {
assertThat(e, instanceOf(MessageDeliveryException.class));
assertThat(e.getCause(), instanceOf(MessageDispatchingException.class));
assertThat(e.getMessage(), containsString("Dispatcher has no subscribers"));
}
this.controlBus.send("#bridge.start()");
this.bridgeFlow2Input.send(message);
reply = this.bridgeFlow2Output.receive(5000);
assertNotNull(reply);
In other words you can just extends that external interface to your local one. The GatewayProxyFactoryBean will do the proxy magic for you underneath.
Also we have this JIRA for similar use-case: https://jira.spring.io/browse/INT-4134
Use a GatewayProxyFactoryBean; here's a simple example:
#SpringBootApplication
public class So41162166Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(So41162166Application.class, args);
context.getBean(NoAnnotationsAllowed.class).foo("foo");
context.close();
}
#Bean
public GatewayProxyFactoryBean gateway() {
GatewayProxyFactoryBean gateway = new GatewayProxyFactoryBean(NoAnnotationsAllowed.class);
gateway.setDefaultRequestChannel(channel());
return gateway;
}
#Bean
public MessageChannel channel() {
return new DirectChannel();
}
#ServiceActivator(inputChannel = "channel")
public void out(String foo) {
System.out.println(foo);
}
public static interface NoAnnotationsAllowed {
public void foo(String out);
}
}

Netty-Socketio with Spring MVC

I am trying to add sockets to my Spring MVC project. I think I should wright something in servlet-context.xml but don`t know what. So I create classes
#Component
public class Bootstrap {
#Autowired
private SocketIOServer server;
#PostConstruct
public void start() {
server.start();
}
#PreDestroy
public void stop() {
server.stop();
}
}
and
#Configuration
#ComponentScan("controllers")
public class SpringConfig {
#Bean(name="webSocketServer")
public SocketIOServer webSocketServer() {
com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
config.setHostname("localhost");
config.setPort(4443);
final SocketIOServer server = new SocketIOServer(config);
server.addJsonObjectListener(LogFile.class, new DataListener<LogFile>() {
#Override
public void onData(SocketIOClient client, LogFile data, AckRequest ackSender) {
server.getBroadcastOperations().sendJsonObject(data);
}
});
server.addConnectListener(new ConnectListener() {
#Override
public void onConnect(SocketIOClient client) {
LogFile log = new LogFile();
log.setMsg("hello");
server.getBroadcastOperations().sendJsonObject(log);
}
});
return server;
}
}
But nothing gonna work. I added client to resources of project and enter link localhost there and have client on my local machine with right link. But still can`t connect. What I am doing wrong? I believe that I mistaken in configuring beans.
Samples I get from this https://github.com/mrniko/netty-socketio.

Resources