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.
>
>

Reply via email to