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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn- boot-spring | |
"Initialize a Jetty server for Spring and also Spring Security" | |
([server handler context-config-location] | |
(let [filter (doto (org.mortbay.jetty.servlet.FilterHolder. org.springframework.web.filter.DelegatingFilterProxy) | |
(.setName "springSecurityFilterChain")) | |
servlet (doto (org.mortbay.jetty.servlet.ServletHolder. (ring.util.servlet/servlet handler)) | |
(.setName "default")) | |
context (doto (org.mortbay.jetty.servlet.Context. server "/" | |
(bit-or org.mortbay.jetty.servlet.Context/SESSIONS | |
org.mortbay.jetty.servlet.Context/SECURITY)) | |
(.addFilter filter "/*" 0) | |
(.addServlet servlet "/") | |
(.addEventListener (org.springframework.web.context.ContextLoaderListener.)))] | |
(when context-config-location | |
(.setInitParams context {"contextConfigLocation" context-config-location})) | |
(.addHandler server context))) | |
([server handler] | |
(boot-spring server handler nil))) |
The next step is to wrap this in a ring/ring-adapter-jetty configurator (i.e., a function that takes a server):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn boot [] | |
(jetty/run-jetty #'app | |
{:port 9090 | |
:join? false | |
:configurator (fn [server] (boot-spring server #'app "classpath:security.xml")) | |
:default-handler? false})) |
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans" | |
xmlns="http://www.springframework.org/schema/security" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd | |
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> | |
<authentication-manager> | |
<authentication-provider> | |
<user-service> | |
<user name="user" password="1234" authorities="ROLE_USER"/> | |
<user name="admin" password="1234" authorities="ROLE_ADMIN,ROLE_USER"/> | |
</user-service> | |
</authentication-provider> | |
</authentication-manager> | |
<http auto-config="true"> | |
<intercept-url pattern="/user**" access="ROLE_USER"/> | |
<intercept-url pattern="/admin**" access="ROLE_ADMIN"/> | |
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/> | |
</http> | |
</beans:beans> |
The Compojure application this secures looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defroutes main-routes | |
(GET "/" req (index-html req)) | |
(GET "/admin" req (example-html req)) | |
(GET "/user" req (example-html req)) | |
(GET "/other" req (example-html req)) | |
(route/resources "/") | |
(route/not-found "Page not found")) |
Here the HTML rendering prints out the Spring context and security context which contain lots of goodies we can use later: