In continuation of my earlier blog Container based Security and Spring Security, in this blog, I will demonstrate how you can achieve Certificates Authentication and Authorization in Spring Security. As with all my blogs, the sample code for this is @ Github.
As mentioned in Enabling CLIENT-CERT based authorization on Tomcat,
- You need to create keystore information
- You need to change the connector configuration in tomcat to work with SSL
- We dont need to configure MemoryRealm, because we use Spring Security for authentication
Continue and do the below steps,
- Go to spring-mvc-client3 folder in the sample codebase and run “mvn clean package -DskipTests” it will create a war file in target folder, copy the war file into tomcat webapps folder.
- Go to spring-mvc-client4 and repeat the maven command and copy the war file into tomcat webapps folder.
- Start the tomcat server by going to <tomcat installed folder>/bin/startup.bat
- Go to spring-mvc-client3 folder and run “mvn test” and notice all the tests are successful
In the web.xml you need to put the following code,
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> </listener>
To understand what is going open spring-mvc-client3/src/main/webapp/WEB-INF/applicationContext-security.xml, you will notice below configuration,
<!-- There is no security requirement to access the html resources like css, js etc and maybe loggedout.jsp page --> <http pattern="/static/**" security="none"/> <http pattern="/loggedout.jsp" security="none"/> <http use-expressions="true"> <!-- Only supervisor can access secure1 webresources and all the resources under it and this resource can be accessed only if the request is https--> <intercept-url pattern="/secure1/**" access="hasRole('supervisor')" requires-channel="https"/> <!-- You have to be logged in to access secure folder and all resources under it --> <intercept-url pattern="/secure/**" access="isAuthenticated()" requires-channel="https"/> <!-- You have to be logged in to access secure folder and all resources under it --> <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="https"/> <!-- Here is where you provide a regular expression to extract user identity from the certificate and pass it to a authentication provider, in this example, there is a dummy authentication provider as below, in real example, the auth provider is something like LDAP --> <x509 subject-principal-regex="CN=(.*?)," user-service-ref="accountService" /> </http> <authentication-manager> <authentication-provider> <!-- Dummy anthentication provider --> <user-service id="accountService"> <user name="client1" password="" authorities="supervisor" /> <user name="client2" password="" authorities="user" /> </user-service> </authentication-provider> </authentication-manager>
Open the JUnit test @ spring-mvc-client3/src/test/java/com/goSmarter/springsecurity/SecureHttpClient1Test.java and see based on the above configuration, I have 2 positive testcases and 1 negative testcase. If you notice testSecurePage, user “client1” can access “secure1” folder because he is a “supervisor”, it is returning http OK(200). If you notice testSecurePageNegativeCase, user “client2” cannot access “secure1” folder because he is a “user”, it is giving forbidden(403) http code.
I hope this blog helped you.