All the source code for this can be found at https://github.com/lispnik/ring-spring-security
Spring Security (formerly Acegi Security) is a great framework for adding authentication and authorization to your Java web app. I wanted to take advantage of it in my Compojure apps. Here's an example of how to do it:
The next step is to wrap this in a ring/ring-adapter-jetty configurator (i.e., a function that takes a server):
To illustrate the integration, here is an example Spring Security configuration which establishes two users and two roles (user and administration) and applies it to URL patterns. If the user issues a HTTP request with a URL pattern that requires authorization and they are not already authenticated, then Spring Security as configured below will actually render a plain-looking login form before proceeding. We get this basic functionality for free with Spring Security although it can be replaced with a custom form of our own design.
The Compojure application this secures looks like this:
Here the HTML rendering prints out the Spring context and security context which contain lots of goodies we can use later:
Your example works, but this occasionally pops up in the logs:
ReplyDeleteException: java.lang.IllegalArgumentException: ServletContext must not be null
Assert.java:112 org.springframework.util.Assert.notNull
WebApplicationContextUtils.java:109 org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext
WebApplicationContextUtils.java:99 org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext
core.clj:31 ring-spring-security.core/application-context
core.clj:50 ring-spring-security.core/wrap-reload-spring[fn]
core.clj:57 ring-spring-security.core/wrap-dump[fn]
reload.clj:14 ring.middleware.reload/wrap-reload[fn]
stacktrace.clj:15 ring.middleware.stacktrace/wrap-stacktrace-log[fn]
stacktrace.clj:79 ring.middleware.stacktrace/wrap-stacktrace-web[fn]
Var.java:365 clojure.lang.Var.invoke
jetty.clj:16 ring.adapter.jetty/proxy-handler[fn]
(Unknown Source) ring.adapter.jetty.proxy$org.mortbay.jetty.handler.AbstractHandler$0.handle
HandlerCollection.java:114 org.mortbay.jetty.handler.HandlerCollection.handle
HandlerWrapper.java:152 org.mortbay.jetty.handler.HandlerWrapper.handle
Server.java:322 org.mortbay.jetty.Server.handle
HttpConnection.java:542 org.mortbay.jetty.HttpConnection.handleRequest
HttpConnection.java:928 org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete
HttpParser.java:549 org.mortbay.jetty.HttpParser.parseNext
HttpParser.java:212 org.mortbay.jetty.HttpParser.parseAvailable
HttpConnection.java:404 org.mortbay.jetty.HttpConnection.handle
SocketConnector.java:228 org.mortbay.jetty.bio.SocketConnector$Connection.run
QueuedThreadPool.java:582 org.mortbay.thread.QueuedThreadPool$PoolThread.run
Any idea why?
Re: Exception: java.lang.IllegalArgumentException: ServletContext must not be null
ReplyDeleteIt seems to be because I'm reloading the Spring application context on every request. The exception seems harmless but you can work around it by not reloading the Spring application context on every request. e.g. In the following (from core.clj), comment out (wrap-reload-spring):
(def app
(-> (handler/site main-routes)
(wrap-reload-spring)
(wrap-dump)
(wrap-reload '(ring-spring-security.core))
(wrap-stacktrace)))