Hi
It is brilliant, thanks. it would be nice to have this feature and a WSS4J spring security aware password callback handler
contributed to CXF but the issue is we don't actually ship SpringSecurity...
Dan - would it make sense to introduce a 'provided' dependency on the Spring security (core) modules ? And then modules like
ws-security or jaxrs can have contributions in ....spring.security packages ?
Alternatively we can have this feature and handler demonstrated perhaps in the system tests area, with the contributions from
Andreas being acknowledged ?
cheers, Sergey
----- Original Message -----
From: "Andreas Veithen" <[email protected]>
To: <[email protected]>
Sent: Sunday, September 20, 2009 10:16 PM
Subject: Re: CXF + JAX-RS + Spring Security (Acegi) for authorization
I improved this a bit further by creating a feature that adds the invoker proxy:
public class AuthorizationFeature extends AbstractFeature {
@Override
public void initialize(Server server, Bus bus) {
Service service = server.getEndpoint().getService();
service.setInvoker(new
SpringSecurityInvokerProxy(service.getInvoker()));
}
}
Now the configuration looks like this:
<jaxrs:server address="/rest">
<jaxrs:serviceBeans>
...
</jaxrs:serviceBeans>
<jaxrs:providers>
...
</jaxrs:providers>
<jaxrs:features>
<bean class="myapp.security.AuthorizationFeature"/>
</jaxrs:features>
</jaxrs:server>
The advantage is that it works in exactly the same way for all
frontends (I tested this successfully with JAX-RS and the simple
frontend) and that there is no need to figure out how to set up the
target invoker.
Does the code in AuthorizationFeature look good?
I also have a working integration between WSS4J and Spring Security
which plays nicely with
SpringSecurityInvokerProxy/AuthorizationFeature. Here is the
configuration for a simple scenario:
<security:authentication-provider>
<security:user-service>
<security:user name="joe" password="password"
authorities="ROLE_USER,ROLE_ADMIN"/>
<security:user name="bob" password="password" authorities="ROLE_USER"/>
</security:user-service>
</security:authentication-provider>
<simple:server serviceClass="myapp.MyService" address="/myservice">
<simple:serviceBean>
<bean class="myapp.MyServiceImpl"/>
</simple:serviceBean>
<simple:inInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken"/>
<entry key="passwordType" value="PasswordText"/>
<entry key="passwordCallbackRef">
<ssec:server-password-callback-handler
logExceptions="true" nestExceptions="false"/>
</entry>
</map>
</constructor-arg>
</bean>
</simple:inInterceptors>
</simple:server>
ssec:server-password-callback-handler is a Spring handler that creates
a password callback handler that delegates authentication to Spring
Security.
If there is interest, I can contribute the corresponding code.
Andreas
On Wed, Sep 16, 2009 at 10:54, Sergey Beryozkin
<[email protected]> wrote:
Hi Andreas
This does look like a neat solution. I think it's worth documenting what you
suggested.
Note that we don't support in-only operations for JAX-RS (just yet), but
either way, what you've done seems to cover all the variations. In one of
the system tests I added a custom invoker which extends JAXRSInvoker but
your approach works well too and is more generic.
- The service is not necessarily invoked in the same thread as the
interceptor
I thought that interceptors and the service were actually invoked on the
same thread. It is transport threads like Jetty threads won't necessarily
end up invoking on the service.
Dan, can it be that a thread which invoked a given interceptor won't invoke
the service endpoint ?
By the way there's also a similar test showing how the spring security can
be used without using annotations :
systest/jaxrs/src/test/resources/jaxrs_security_no_annotations/WEB-INF/beans.xml
cheers, Sergey
Andreas Veithen-2 wrote:
I'm currently trying to integrate JAX-RS with Spring Security for
authorization (authorization only; I use a custom authentication
mechanism). I found the following resources describing integration
between CXF and Spring Security:
-
http://www.nabble.com/Re:-CXF%2BACEGI-%2B-Anybody-out-there--p12759358.html
(WS-Security)
- http://www.emforge.org/wiki/WebServicesImplementation (WS-Security)
- There is also a JAX-RS systest (see
systest/jaxrs/src/test/resources/jaxrs_security/WEB-INF/beans.xml in
the trunk) that integrates Spring Security with JAX-RS.
In order for (annotation driven) authorization to work, it is
necessary to use SecurityContextHolder to associate the
SecurityContext/Authentication with the current thread. In the first
two references, this is done in a custom interceptor, while the
systest uses a servlet filter (that implements HTTP basic
authentication). I see two issues with these approaches:
- The service is not necessarily invoked in the same thread as the
interceptor or servlet filter (e.g. in-only operations). If that
happens, the security context will not be set up correctly.
- The code in the first two references never resets the authentication
in the SecurityContext (by calling
SecurityContextHolder.getContext().setAuthentication(null)). I fear
that it is therefore possible that a service may accidentally get the
authentication from a previous request. This is only a problem when
using an interceptor, but using a servlet filter may not always be
possible (e.g. for WS-Security).
The approach that I use to avoid these problems is to insert a proxy
in front of the Invoker (JAXRSInvoker in my case). This proxy looks as
follows:
public class SpringSecurityInvokerProxy implements Invoker {
private Invoker target;
public Invoker getTarget() { return target; }
public void setTarget(Invoker target) { this.target = target; }
public Object invoke(Exchange exchange, Object o) {
Authentication authentication = exchange.get(Authentication.class);
SecurityContext securityContext =
SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
try {
return target.invoke(exchange, o);
} finally {
securityContext.setAuthentication(null);
}
}
}
The Authentication object is added to the exchange by an interceptor
that implements the custom authentication mechanism. The try/finally
block here makes sure that the security context is reset right after
the invocation of the service. The corresponding configuration is:
<jaxrs:server address="/rest">
<jaxrs:serviceBeans>
...
</jaxrs:serviceBeans>
<jaxrs:providers>
...
</jaxrs:providers>
<jaxrs:invoker>
<bean class="myapp.security.SpringSecurityInvokerProxy">
<property name="target">
<bean class="org.apache.cxf.jaxrs.JAXRSInvoker"/>
</property>
</bean>
</jaxrs:invoker>
</jaxrs:server>
This works well for me, but I would like to know if there is a
better/easier way to achieve this.
Regards,
Andreas
--
View this message in context:
http://www.nabble.com/CXF-%2B-JAX-RS-%2B-Spring-Security-%28Acegi%29-for-authorization-tp25462665p25468445.html
Sent from the cxf-user mailing list archive at Nabble.com.