The Blazing Grail – Part 3
Now that we have a working server it’s time to lock it down! The first thing we need is the ACEGI security plugin (Spring Security). These quick-start tutorials were very helpful to get started.
To install type:
. . .
grails create-auth-domains User Role Requestmap
. . .
grails generate-manager
. . .
Now that it’s installed we need to lock down our services. This is a good place to understand a bit about using security with spring-blaze.
It took me a while to realize that for this you HAVE to code to an Interface. That’s good practice right? For my proof-of-concept I really didn’t care about best practices so there was no interface involved. I finally figured it out though.
So lets create an interface. I guess standard Java way is for the interface to be the “ObjectName” and the implemented version “ObjectNameImpl” or some such. Whatever. You’ll get the idea.
I now have an AmfService.groovy
and an AmfServiceImpl.groovy
class AmfServiceImpl implements AmfService
{
boolean transactional = true
String getSomeString()
{
return "Peanut Butter is Awesome"
}
String getSomeOtherString()
{
return "Peanut Butter and Banana Sandwiches are the Best!"
}
}
Wait . . . where did my annotations go?! Well so far I haven’t been able to get security to work with annotations; interfaces or no. Likely it’s just something I’m doing wrong; if you have a working example I would love to see it. But I DID get XML configuration working so that’s what I’m showing here.
The next step is to define the login command in the /project/web-app/WEB-INF/flex/services-config.xml that was created in the previous post. If you copied my XML then it should already be displayed. If you’ve got the XML generated by the blazeds plugin then you need to modify this file so that the security node looks like this:
<login-command class="flex.messaging.security.TomcatLoginCommand" server="Tomcat"/>
</security>
The next change is to the flex-servlet.xml; this is where the bulk of the work is. You’ll have to add a few things:
xmlns:security="http://www.springframework.org/schema/security"
add the security schema locations:
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
entry-point and bean nodes:
<security:http entry-point-ref="preAuthenticatedEntryPoint"/>
<bean id="preAuthenticatedEntryPoint"
class="org.springframework.security.ui.preauth.PreAuthenticatedProcessingFilterEntryPoint"/>
some sample users:
<security:authentication-provider>
<security:user-service>
<security:user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN"/>
<security:user name="user" password="user" authorities="ROLE_USER"/>
</security:user-service>
</security:authentication-provider>
enable annotations (in case I figure out how to make it work):
<security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled"/>
and finally we will mark the message-broker as secured:
<flex:message-broker>
<flex:secured/>
</flex:message-broker>
If you want to just lock down the whole AMF channel and be done with it then you can do this:
<flex:secured-channel channel="my-amf" access="ROLE_USER" />
</flex:message-broker>
Lastly I comment out the context nodes and add a bean node to represent the service class. In that node I mark which methods need to be secured:
<flex:remoting-destination/>
<security:intercept-methods>
<security:protect method="getSomeString" access="ROLE_ADMIN" />
</security:intercept-methods>
</bean>
<!–
<context:annotation-config />
<context:component-scan base-package="com.pbking" />
–>
So with all of these changes my flex-servlet.xml looks like this:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:flex="http://www.springframework.org/schema/flex" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
http://www.springframework.org/schema/flex
http://www.springframework.org/schema/flex/spring-flex-1.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<security:http entry-point-ref="preAuthenticatedEntryPoint"/>
<bean id="preAuthenticatedEntryPoint"
class="org.springframework.security.ui.preauth.PreAuthenticatedProcessingFilterEntryPoint"/>
<security:authentication-provider>
<security:user-service>
<security:user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN"/>
<security:user name="user" password="user" authorities="ROLE_USER"/>
</security:user-service>
</security:authentication-provider>
<security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled"/>
<flex:message-broker>
<flex:secured/>
</flex:message-broker>
<bean id="amfService" class="com.pbking.AmfServiceImpl">
<flex:remoting-destination/>
<security:intercept-methods>
<security:protect method="getSomeString" access="ROLE_ADMIN" />
</security:intercept-methods>
</bean>
<!–
<context:annotation-config />
<context:component-scan base-package="com.pbking" />
–>
</beans>
And that’s it! Your services are locked down! I’ve updated my super-simple flex app to demonstrate that they are. There is a button to call getSomeString (a secured method) getSomeOtherString (a wide open method) and another to login as admin (so that you can successfully call getSomeString).
That app now looks like this:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/halo">
<fx:Declarations>
<s:RemoteObject id="ro"
endpoint="http://localhost:8080/blazing-ahead/messagebroker/amf"
destination="amfService"
fault="resultLabel.text = event.fault.faultString;"
result="resultLabel.text = event.result.toString();"/>
</fx:Declarations>
<s:VGroup>
<mx:Button label="login" click="ro.channelSet.login(‘admin’, ‘admin’);" />
<mx:Button label="getSomeString" click="ro.getSomeString();" />
<mx:Button label="getSomeOtherString" click="ro.getSomeOtherString();" />
<mx:Label id="resultLabel" />
</s:VGroup>
</s:Application>
With that you should be able to fire up your server (grails run-app) and hit it with the Flex app to see the restrictions.
Hope that helps you out!
I’m trying to update my Grails BlazeDS plugin using your configuration but it doesn’t seem to work with Flash Builder flash wizards. Have you tried it?
I get to see the service listed in the wizard but when I click finish, it crashes with a message saying “com.epseelon.grails.blazeds.EchoService contains overloaded method and is not supported for Introspection”.
Hey Jason,
Thanks for the awesome series about getting Spring Security and Blaze working nicely, it really helped me a lot.
Do you know how I would go about changing the auth-service provider to make it use the domain classes created by the plugin (ie. User, Role, etc) instead of the static configuration as above?
Hi Jason,
Have you tried your project using SpringSecurity 3? I think I have pretty much the same setup, but when I access my server via either browser (using the Grails controllers) or via my Flex client, I receive an exception:
org.springframework.web.context.request.ServletRequestAttributes cannot be cast to org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest
Without all is fine. The only obvious difference from what you’ve done is that I’m using SpringSecurity 3.
Anyway, nice to see your post and the valuable information in it.
Thanks,
Larry
Oops, in my previous post it should say Without flex:secured all is fine.