Jax-rs WriterInterceptor/ ReaderInterceptor never get registered in grizzly connector - jersey

Created a super simple ReaderInceptor and registered it with my Jaxrs client using Grizzly as provider. Does not work. Interceptors never seem to get picked up, however Jaxrs filters do.
jersey-grizzly-connector 2.33
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(MyInterceptor.class);
clientConfig.connectorProvider(new GrizzlyConnectorProvider());
Client client = ClientBuilder.newClient(clientConfig);
public class MyInterceptor implements ReaderInterceptor {
#Override
public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
System.out.println("here i am");
return context.proceed();
}
Anyone see what I am doing wrong?

Related

Camel REST and Spring Security Java Configuration

I'm trying to set up Camel REST to use basic auth with a simple username/password from my application.properties and can't for the life of me seem to configure Camel Spring Security to do that. I'm trying to follow the Spring Security component documentation which seems to be missing the example of configuring the required beans. I found the missing example here under 'Controlling access to Camel routes' but this only shows the xml configuration.
How do I set up the required SpringSecurityAuthorizationPolicy bean? It needs an AuthenticationManager and an AccessDecisionManager and it also seems to require that I set its SpringSecurityAccessPolicy which I have no idea how to do.
I haven't gotten to test these yet, because I can't get my beans set up, but my rest route looks like:
rest("/ingest")
.post("/json").consumes("application/json")
.route()
.process(authProcessor)
.policy(authPolicy) // this is the bean I don't know how to configure
.to("direct:ingest")
.endRest();
and my AuthProcessor (taken from the camel component doc) looks like:
#Component
public class AuthProcessor implements Processor {
public void process(Exchange exchange) {
String userpass = new String(Base64.decodeBase64(exchange.getIn().getHeader("Authorization", String.class)));
String[] tokens = userpass.split(":");
// create an Authentication object
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(tokens[0], tokens[1]);
// wrap it in a Subject
Subject subject = new Subject();
subject.getPrincipals().add(authToken);
// place the Subject in the In message
exchange.getIn().setHeader(Exchange.AUTHENTICATION, subject);
}
}
and here's my broken bean configuration for what it's worth:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public SpringSecurityAuthorizationPolicy springSecurityAuthorizationPolicy(
AuthenticationManager authenticationManager, AccessDecisionManager accessDecisionManager) {
SpringSecurityAuthorizationPolicy policy = new SpringSecurityAuthorizationPolicy();
SpringSecurityAccessPolicy springSecurityAccessPolicy = new SpringSecurityAccessPolicy();
policy.setAuthenticationManager(authenticationManager);
policy.setAccessDecisionManager(accessDecisionManager);
policy.setSpringSecurityAccessPolicy(????);
return policy;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("pass").roles("USER");
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AccessDecisionManager accessDecisionManager() {
AffirmativeBased affirmativeBased = new AffirmativeBased(ImmutableList.of(
new RoleVoter()
));
affirmativeBased.setAllowIfAllAbstainDecisions(true);
return affirmativeBased;
}
}
I've been banging my head against the wall trying to understand this so an example of how to do this would be amazing. It looks like the xml configuration for what I want to do (in the second link) is simple enough but I can't seem to replicate it in Java configuration.
I know it's an old topic, but I ran into similar questions. I managed to get it working. Not by overriding the accessDecisionManager() method within the WebSecurityConfigurerAdapter class, but by constructing a new instance while building my SpringSecurityAuthorizationPolicy:
#Bean
public Policy adminPolicy(AuthenticationManager authenticationManager) {
RoleVoter roleVoter = new RoleVoter();
SpringSecurityAuthorizationPolicy policy = new SpringSecurityAuthorizationPolicy();
policy.setAuthenticationManager(authenticationManager);
policy.setAccessDecisionManager(new UnanimousBased(List.of(roleVoter)));
policy.setSpringSecurityAccessPolicy(new SpringSecurityAccessPolicy(roleVoter.getRolePrefix() + "<ROLE_NAME>");
return policy;
}

Is it possible to run SpringFox' swagger and swagger-ui on a different port than the main application?

We are using SpringBoot and SpringFox using #EnableSwagger2 to expose the swagger-ui.html API documentation (we don't need it to automate client code, just as documentation and test ui).
Is it possible to run all swagger related endpoints under a different port (for example the spring boot management/monitoring port) than the main application?
I researched a bit, but did not find a way in swagger's/springfox' configuration to do it. Is there a spring way to do this?
Yes, there is a Spring way of doing this:
Step 1. Adding an additional Tomcat connector
To add a port to the embedded server an additional connector needs to be configured.
We will do it by providing custom WebServerFactoryCustomizer:
#Component
public class TomcatContainerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Value("${swagger.port}")
private int swaggerPort;
#Override
public void customize(TomcatServletWebServerFactory factory) {
Connector swaggerConnector = new Connector();
swaggerConnector.setPort(swaggerPort);
factory.addAdditionalTomcatConnectors(swaggerConnector);
}
}
Now Tomcat listens on two ports but it serves the same content on both of them. We need to filter it.
Step 2. Adding a filter
Adding a servlet filter is pretty straightforward with a FilterRegistrationBean.
It can be created anywhere, I added it directly to the TomcatContainerCustomizer.
#Component
public class TomcatContainerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Value("${swagger.port}")
private int swaggerPort;
#Value("${swagger.paths}")
private List<String> swaggerPaths;
#Override
public void customize(TomcatServletWebServerFactory factory) {
Connector swaggerConnector = new Connector();
swaggerConnector.setPort(swaggerPort);
factory.addAdditionalTomcatConnectors(swaggerConnector);
}
#Bean
public FilterRegistrationBean<SwaggerFilter> swaggerFilterRegistrationBean() {
FilterRegistrationBean<SwaggerFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new SwaggerFilter());
filterRegistrationBean.setOrder(-100);
filterRegistrationBean.setName("SwaggerFilter");
return filterRegistrationBean;
}
private class SwaggerFilter extends OncePerRequestFilter {
private AntPathMatcher pathMatcher = new AntPathMatcher();
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
boolean isSwaggerPath = swaggerPaths.stream()
.anyMatch(path -> pathMatcher.match(path, httpServletRequest.getServletPath()));
boolean isSwaggerPort = httpServletRequest.getLocalPort() == swaggerPort;
if(isSwaggerPath == isSwaggerPort) {
filterChain.doFilter(httpServletRequest, httpServletResponse);
} else {
httpServletResponse.sendError(404);
}
}
}
}
The properties swagger.port and swagger.paths are configured in the application.yaml:
server.port: 8080
swagger:
port: 8088
paths: |
/swagger-ui.html,
/webjars/springfox-swagger-ui/**/*,
/swagger-resources,
/swagger-resources/**/*,
/v2/api-docs
So far so good: the swagger-ui is served on the port 8088, our api on the 8080.
But there is a problem: when we try to connect to the api from the swagger-ui,
the requests are sent to the 8088 instead of 8080.
Step 3. Adjusting SpringFox config.
Swagger assumes that the api runs on the same port as the swagger-ui.
We need to explicitly specify the port:
#Value("${server.port}")
private int serverPort;
#Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.host("localhost:" + serverPort);
}
And the last problem: as the ui runs on a different port than the api,
the requests are considered cross-origin. We need to unblock them.
It can be done globally:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**/*").allowedOrigins("http://localhost:" + swaggerPort);
}
};
}
or by adding annotations to the controllers:
#CrossOrigin(origins = "http://localhost:${swagger.port}")
Versions used: SpringBoot 2.2.2.RELEASE, springfox-swagger2 2.9.2
For a working example see https://github.com/mafor/swagger-ui-port
I don't think so. When you're setting the Spring Boot management port (management.server.port), a second application server gets started to serve the actuator stuff. As far as I know there is no possibility (apart from custom actuator endpoints) to publish something on that server.
What is your use case exactly? Do you want to prevent access to Swagger in production or for non-authenticated users?

Grpc Spring Boot Starter

I am trying to integrate a grpcService to my spring boot application. In this service class i have a jpaRepository which is #Autowired. When i run the server this repository is not injected( is null when i'm trying to use it).
#Service
public class MedicationPlanService extends medicationPlanGrpc.medicationPlanImplBase {
#Autowired
private MedicationPlanRepository medicationPlanRepository;
#Override
public void hello(MedicationPlan.HelloRequest request, StreamObserver<MedicationPlan.HelloResponse> responseObserver) {
List<MedicationPlan> medicationPlans = medicationPlanRepository.findAll();
MedicationPlan.HelloResponse.Builder response = MedicationPlan.HelloResponse.newBuilder();
response.setResponseMessage("hello");
responseObserver.onNext(response.build());
responseObserver.onCompleted();
}
}
#Component
public class GrpcServerStartConfiguration {
#PostConstruct
public void startGrpcServer() throws IOException, InterruptedException {
Server server = ServerBuilder.forPort(9091).addService(new MedicationPlanService()).build();
server.start();
System.out.println("gRPC server started");
server.awaitTermination();
}
}
When i try to use the medicationPlanRepository i realized that it is null.
Thanks in advance :).
Because you're creating MedicationPlanService with "new", it becomes a simple object, not a bean. And things such as DI don't work in this way.
Initialize these 2 classes correctly (via #ComponentScan or #Bean in #Configuration class). Then inject MedicationPlanService into GrpcServerStartConfiguration. The latter you can mark as #Configuration for better understaning btw.

Spring Boot - access component inside a servlet

I have a spring boot application that serves my jersey based api. I have a requirement to have the services layer serve blob data to a client as a stream. I wrote a servlet to do that and configured it as follows.
#Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(new BlobReaderServlet(),"/blobReader/*");
}
However, in the servlet code I can't seem to inject any components (they are all null). I need to inject a component that actually loads the blob data from the database.
#WebServlet(name = "BlobReaderServlet",
urlPatterns = {"/blobreader"})
#Component
public class BlobReaderServlet extends HttpServlet {
Logger logger = Logger.getLogger(this.getClass().getName());
#Inject
DocumentLoaderComponent blobLoader;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
I strongly suspect the servlet isn't a spring managed component after all and dependency injection isn't working. How can I get access to a component from the context?
UPDATE
It was much simpler than I thought.
#Override
public void init() throws ServletException {
ApplicationContext ac = (ApplicationContext) getServletConfig().getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
this.documentBlob = (DocumentBlob) ac.getBean("documentBlobBean");
}
You are correct that your servlet isn't a Spring managed bean. That is because you are instantiating the instance directly (i.e., calling new BlobReaderServlet() in your bean method). Another solution is to change your configuration class as follows:
#Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(blobReaderServlet(),"/blobReader/*");
}
#Bean
public BlobReaderServlet blobReaderServlet(){
return new BlobReaderServlet();
}
This will allow Spring to manage the instance and perform autowiring on it.

How to redirect automatically to https with Spring Boot

How I can easily configure the embedded tomcat server to redirect all http traffic to https? I have Spring Boot running on an ec2 instance that is behind an elastic load balancer. I have configured the ELB to handle ssl for me (which is awesome) and it sets the X-FORWARDED-PROTO header to "https". I want to detect when that isn't set, and redirect the user to force them to use https if they aren't already.
So far, I have tried adding the following to my application.properties file with no luck:
server.tomcat.protocol-header=x-forwarded-proto
security.require-ssl=true
My answer is a little late but I just recently had this problem and want to post a solution which worked for me.
Originally, I thought that setting tomcat up to use the X-Forwarded headers would suffice but the RemoteIPValve from Tomcat, which should normally handle this case, didnt work for me.
My solution was to add an EmbeddedServletContainerCustomizer and add a ConnectorCustomizer:
(note that I am using Tomcat 8 here)
#Component
public class TomcatContainerCustomizer implements EmbeddedServletContainerCustomizer {
private static final Logger LOGGER = LoggerFactory.getLogger(TomcatContainerCustomizer.class);
#Override
public void customize(final ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
final TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container;
tomcat.addConnectorCustomizers(connector -> {
connector.setScheme("https");
connector.setProxyPort(443);
});
LOGGER.info("Enabled secure scheme (https).");
} else {
LOGGER.warn("Could not change protocol scheme because Tomcat is not used as servlet container.");
}
}
}
The important thing is that you not only set the Scheme to https but also the ProxyPort without which all internal redirects from Spring Boot were routed to port 80.
The configuration property security.require-ssl doesn't work when basic authentication is disabled (at least on old versions of Spring Boot). So you probably need to secure all the requests manually with code similar to this one:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Inject private SecurityProperties securityProperties;
#Override
protected void configure(HttpSecurity http) throws Exception {
if (securityProperties.isRequireSsl()) http.requiresChannel().anyRequest().requiresSecure();
}
}
You can check my full answer here: Spring Boot redirect HTTP to HTTPS
You will need a keystore file and few config classes.
The below link explains it in detail.
Https on embedded tomcat
Spring Boot 2.0 redirection of http to https:
Add the following to the #Configuration
#Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector(
TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}

Resources