I have the following sitebricks servlet. Foo.get() is accessible as a GET at /foo/bar. I deployed the servlet to GAE.
#Service
#At("/foo")
#Singleton
public class Foo {
#Get
#At("/bar")
public Reply<?> bar(Request<String> request, HttpSession session) {
// access request scoped HttpSession
}
}
If I understand sitebricks correctly, both Request and HttpSession are injected by sitebricks (potentially with the help of Guice). It will also ensure that the HttpSession is local to the current request. Concurrent requests will be executed on the same instance of Foo since the class is annotated with #Singleton (see Guice docs). But even with concurrent requests arriving on the same JVM, each invocation of bar() will have it's own HttpSession based on the JSESSIONID passed in by the client. Are all those assumptions valid?
When running load tests against my app, I have noticed that at a very low rate the HttpSession passed in by sitebricks/Guice is null. I am troubleshooting this with Google's support at the moment. But apart from GAE - What could cause this from the perspective of sitebricks/Guice?
I found a code snippet that injects a Provider into the constructor. Does that mean I can/should get the HttpSession by calling Provider.get() instead of letting sitebricks inject it as a method parameter?
Related Questions:
Provider<HttpSession> not getting injected
How to #Inject a HttpSession object in a service layer (DAO) class in GWT using Guice?
Inject #SessionScoped value into filter with Guice
Updates
I removed the HttpSession parameter from all the servlet methods like bar. I injected a Provider<HttpSession> into the servlet and call provider.get() to get the session. The tests that I ran so far indicate that this is more reliable than getting HttpSession out of the parameters. That said, I am not sure if the session is provided by sitebricks or GAE itself. Is the HttpSession provided by the servlet container?
Normally I inject HttpServletRequest into the service class first, and then get HttpSession object from the request, which has been working well for me.
For example:
#At("/preview")
#Service
public class PreviewService {
#Inject
private HttpServletRequest request;
// It's similar if we need to modify response directly
#Inject
private HttpServletResponse response;
#Get
public Reply<?> get() {
HttpSession session = request.getSession();
... ...
}
}
Hope it helps.
Related
I have created REST API with Spring Boot and Single Page Application powered by AngularJS.
The question is how to prevent everyone from using my REST api which is available publicly in the internet? I want it to be allowed for usage only from my webpage.
I can not use any secret/password/token from angular side as it would be visible to anyone.
Spring security can help with that. You can define some urls accessible by only certain users having certain roles.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configures(HttpSecurity http) throws Exception {
http.exceptionHandling()
.accessDeniedPage("/error").and()
.authorizeRequests()
.antMatchers("/api/**").hasAnyRole("USER_ROLE");
}
}
So that, only those with role "USER_ROLE" can access any url starts with "/api".
In order to have this functionality, you have to implement a login system which assign the "USER_ROLE" to the users after successful login.
On AngularJs part, it is quite easy. You just make a http request to the REST api, since the browser holds cookies and JSESSIONID, it will be sent along with the request in the header. Spring picks it up and checks if the user having that JSESSIONID has authority to access the url.
I created a web service client to handle cxf soap web services with apache camel.
String serviceUri = "cxf:http://localhost:10000/myservice?serviceClass=" +
MyRequest.class.getCanonicalName();
from(uri).to("mock:xyz");
The web service receives the soap call but throws an exception since the request requires a handling for wss.
org.apache.cxf.binding.soap.SoapFault: MustUnderstand headers: [{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood.
The reason is, that the service requires ws security, which can be seen by lloking at the request.
<SOAP-ENV:Header><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" SOAP-ENV:mustUnderstand="1">
I found out that I need to implement an interceptor to handle header properties.
My questions:
How can I add an interceptor to handle the header attributes with Camel Java-DSL?
Will this be sufficient to get rid of the SOAP Fault?
You can do it through
cxfEndpointConfigurer option #see: Camel-CXF configuration
(I use Spring (it is much easier)), but I guess for DSL URI will look like:
String serviceUri = "cxf:http://localhost:10000/myservice?serviceClass=" +
MyRequest.class.getCanonicalName() +
"&cxfEndpointConfigurer="+ MyConfigurer.class.getCanonicalName();
by implementing org.apache.camel.component.cxf.CxfEndpointConfigurer you have ability to add an Interceptor inside configureServer method
server.getEndpoint().getInInterceptors().add(new MyJAASLoginInterceptor());
if you run your Camel in container with JAAS (like JBOSS) you can use extension from
org.apache.cxf.interceptor.security.JAASLoginInterceptor
with needed callback handler.
Simple example which validates user/password from WSS header against JBOSS users:
public class MyJAASLoginInterceptor extends javax.security.auth.callback.JAASLoginInterceptor {
#Override
protected CallbackHandler getCallbackHandler(String name, String password) {
return new org.apache.cxf.interceptor.security.NamePasswordCallbackHandler(name, password, "setCredential");
}
}
I have a project starts up with Spring Boot.
It has some restful API via Spring Integration inbound gateway.
Afterward, some webservice endpoint added to the project with CXF.
When I setup the CXFServlet mapping, all the restful API became 404.
Only I suspend the CXF config the restful API available again.
May I know if there is anything block the restful API or the spring integration inbound gateway during using CXF?
CXFServlet and Bus
#Configuration
#ComponentScan("com.kennie")
#ImportResource("classpath:cxf-services.xml")
public class SimbaAdapterApplicationConfiguration {
#Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
}
#Bean(name=Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
SpringBus bus = new SpringBus();
bus.getInInterceptors().add(new LoggingInInterceptor());
bus.getOutInterceptors().add(new LoggingOutInterceptor());
return bus;
}
XML configuration
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
<jaxws:server id="MyService" address="/ws/MyService"
serviceClass="com.kennie.IMyService" >
<jaxws:serviceBean>
<ref bean="myServiceImpl" />
</jaxws:serviceBean>
</jaxws:server>
Service Interface
#WebService
public interface IMyService{
#WebMethod
public #WebResult(name = "Response") Response doRequest(
#WebParam(name = "Request", mode = WebParam.Mode.IN)
Request request
);
}
I'm not familiar with CXF, but I know that Spring Integration HTTP is fully based on Spring MVC. So, if you can configure Spring MVC over CXF, all those Spring Integration HTTP Inbound Gateways will be available there as well.
I think your problem is somewhere with distinguishing Servlet mapping.
Looks like your REST API is routed through the CXF Servlet and that one doesn't like it, hence rejecting.
When you add CXF to your code, all the RESTful APIs will be routed through it.
I see two contradictory settings with the way you have configured CXF -
The url-mapping . You are injecting CXF with this code:
#Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
}
Meaning the url at which CXF is listening is /ws/*.
The jax-ws server! Firstly, you need to change it to jax-rs . WS is for SOAP. RS is for Restful. You have defined its address as:
<jaxws:server id="MyService" address="/ws/MyService"
Meaning the server is listening at /ws/MyService
CXF and the jax-rs server are both listening at ws/ something. Now, this is not really a problem. You just need to add this to the URL you are hitting so that the complete URL is something like this:
http:<server>:<port>/<context-root>/<CXF Endpoint>/<jax-rs server address>/<REST API endpoint>
I am guessing you don't want ws appearing twice in the URL. Remove it from the jax-rs address.
Hope this helps.
You can register more one servlet for http rest api, this method is tested and OK:
#SpringBootApplication(
//scanBasePackages = {"com.huawei.manage"}
)
public class Application {
#Bean
public ServletRegistrationBean dispatcherServlet() {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.scan("com.huawei.manage.hsf.controller");
DispatcherServlet rest_dispatcherServlet = new DispatcherServlet(applicationContext);
ServletRegistrationBean registrationBean = new ServletRegistrationBean(rest_dispatcherServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/*");
return registrationBean;
I have a REST provider implemented using RestEasy. Currently the API uses method level security but we prefer URL-based security of Shiro. Our application uses CDI for dependency injection and it would be really great if currently logged in users can be injected into the beans since that would allow us to alter our responses based on user roles.
Are there any tutorials or examples that show how this can be done?
You can inject the SecurityContext to obtain the UserPrincipal:
#Context
private SecurityContext securityContext;
...
Principal principal = securityContext.getUserPrincipal();
If you need more information about the user you can use a CDI producer which fetches the User from e.g. a database:
public class UserProducer {
#Inject
private HttpServletRequest request;
#Produces
public User getUser() {
Principal principal = request.getUserPrincipal();
User user = db.fetchUser(principal.getName());
return user;
}
}
Then inject the User like this:
#Inject
private User user;
The problem is to get the CSRF tokens working between Spring Security and Angular.
Spring Security CSRF Token Interceptor for Angular seems like something that should do the job, but there is no 'X-CSRF-TOKEN' in the HEAD response from the server.
My current tiny implementation is available in GitHub (Tag v.1.0) and I would appreciate a lot if somebody who knows the topic would have a quick look on the code, the problem should be easy to spot.
Based on the documentation, I am under the impression that CSRF should have been enabled automatically, but that seems not to be the case.
I am using Spring Boot and prefer the annotation-based configuration over XML, if something needs to be configured differently.
Any other approaches to make Spring Security work against Angular?
Angular looks for a cookie called "XSRF-TOKEN" I believe, so the easiest thing to do for the client is to send that. You can do it in a Filter for instance (example from https://github.com/spring-guides/tut-spring-security-and-angular-js/blob/master/single/src/main/java/demo/UiApplication.java#L65):
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = new Cookie("XSRF-TOKEN", csrf.getToken());
cookie.setPath("/");
response.addCookie(cookie);
}
filterChain.doFilter(request, response);
}
};
}
Update: since spring security 4.2 the correct cookie name for angular is used by default if you use the cookie csrf repository(the link is still the best source), i.e. there is no longer any need for a custom filter. Example:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
I am answering the question myself as there was a hidden one in the original GitHub repository: Issue #1.
The solution was to add a couple of lines of Java code that adds the CSRF parameters as Http message headers.
I added a working solution to the GitHub repo with Tag v.2.0.