在以前的一篇博客中,Tomcat認證受權與簡單的SSO 我在tomcat cluster上搭建了一個簡單的網站,並試驗了各類認證受權BA,FBA,以及tomcat自身build-in的SSO機制。但就像摘要裏面寫的內容同樣,tomcat自身的FBA並很差用。重複一遍FBA的缺點:html
login的過程沒法被幹預。咱們沒法經過添加filter的形式進行干預。login徹底交給web容器處理,頁面也是有web容器負責展現。java
沒有login地址,用戶沒法bookmark一個Login頁面。直接訪問login.html是沒法提交form的。login只能在訪問受保護資源的時候纔會被觸發。web
所以,我必須尋找一個新的認證方案。ajax
如下幾點是我須要考慮的:spring
JAVA平臺。編程
克服了FBA的缺陷。json
簡單易用。tomcat
穩定,一直存在維護,且充滿活力。安全
能夠跟各類認證系統集成,好比CAS,OpenSSO, JAVASSO, Kerberos, SAML.session
易於擴展,好比remember me, oauth2.
Spring Security 3則是很好的選擇。另外不得不提一下SecurityFilter, SecurityFilter是一個很是老的,基於servlet filter設計的一個認證框架,很是的簡單易上手。惋惜它好像從2005年,就沒再更新了。 若是想本身寫一個認證框架,SecurityFilter是一個很好的模仿對象。它的source如今能夠在這裏下載: http://securityfilter.sourceforge.net
接下來,開始使用Spring Security 3來從新搭建咱們的網站。
本篇文章和下一篇文章所生成的代碼都可以在此下載:http://pan.baidu.com/s/1nthpuDN
Spring使用maven管理本身的發佈。因此基於Spring的開發,最好使用maven,不然理清楚Spring控件之間的依賴關係都要花很大的功夫。我在附件裏面上傳了一個PPT,介紹maven的概念和使用,http://pan.baidu.com/s/1kauU 。
安裝eclipse maven插件。help->install new software->輸入http://download.eclipse.org/technology/m2e/releases。安裝插件。
建立一個」Maven Project「, 選擇下面的archetype:
命名group id與artifect id.
點擊finish之後,一個maven的web項目就建成了。請在java目錄下建立java包和類,在resources下面放置resources,而在webapp下面放置web頁面文件與配置信息。
接下來,咱們須要修改pom.xml,增長對Spring的依賴
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-ldap</artifactId> <version>3.2.0.RELEASE</version> </dependency>
在eclipse中,右鍵點擊project firstWeb --->Maven--->Update project. 這樣把項目全部依賴的jar包都自動下載到項目中。
在進行試驗開發以前,我得嘚吧嘚吧Spring Security 3. 只有文字,沒有代碼的文章,不是好手冊。只有代碼,沒有文字的博客,則不是能夠被容易看懂的文章。
Spring在古老的記憶中,只是一個框架,它具備IoC和AoP的特性。然而近幾年,它已經變成了一個平臺。IoC與AoP依然成爲它的一個子項目Spring Framework。在覈心項目之上,又建立了十幾個很是具備競爭力的項目,好比Spring MVC, Spring Security, 這些項目覆蓋了J2EE領域中最多見的應用領域,包含Mobile,數據訪問。
Spring的上手不是特別簡單,但一旦上手,則是很是的簡單。緣由就在於它的IoC(控制反轉,也稱DI,依賴注入)和AoP(面向切面編程)。由於這兩樣東西,打破了一個程序的常規邏輯,使的邏輯和代碼分散在各個角落。這讓咱們只能看到樹葉,卻看不到大樹。然而,Spring的項目中高度使用了DI和AoP。這讓咱們在使用Spring的時候更注重使用形式而忽略了原理。
另外,Spring是一個很潮的社團,JAVA的新特性老是能獲得大力應用。因而Java annotation在Spring中也普遍的用起來,這更加劇了代碼的支離破碎。這就是爲何Spring上手難的緣由。
Spring Security3做爲Spring下的一個子項目,它既能夠跟Spring其它項目集成起來一塊兒使用,也能夠單獨使用,兩種狀況下的配置是不同的。另外,Spring Security3的配置文件上,又能夠分兩種, 基於XML的配置,和基於JAVA配置。
基於XML的配置易於修改,配置集中。基於Java的配置易於建立,語法簡單。但不管它如何變化無窮,咱們都必須擦亮眼睛,看清它的實質。它跟SecurityFilter和Struts2同樣,都是基於Servlet Filter(springSecurityFilterChain)來對全部的URL進行攔截的,而後再根據本身的配置,對各個URL進行轉發。全部的config只是爲了讓咱們能簡單的聲明URL的攔截和轉發的邏輯。
說了這麼多,恐怕你們都腦子亂掉了。接下來,我將從一個空項目開始,一步一步的搭建個人網站。注,本實驗把Spring Security3.2單獨使用,使用javaconfig。之因此使用javaconfig,是由於網上有太多的XML配置,但卻沒有javaconfig的複雜實例,甚至連官方文檔都沒有詳細的解釋。要想了解和Spring MVC的整合,以及XML的配置方式,請參考官方文檔。
以前一篇文章已經有一個應用sessiontest.如今我不打算再用它。我將從上面新建的maven項目firstWeb開始從新建立一個網站。網站仍是將部署在以前的tomcat集羣上,使用前面文章配置好的OpenLDAP作用戶存貯。OpenLDAP的搭建,請看以前的文章。
首先搭建沒有安全保護的網站。如圖所示:
在webapp下面,存在index.jsp用於默認頁面。ajax目錄下則是給API用的,未來使用BA認證。html目錄下則是瀏覽網頁,將使用FBA認證,登錄頁面爲login.jsp。index.jsp上有一個FORM將提交到submit.jsp.
index.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <title>Your selections</title> </head> <body> <a href="${pageContext.request.contextPath }/html/login.jsp">login</a> <a href="${pageContext.request.contextPath }/html/logout">logout</a> <% String selections = (String)session.getAttribute("selections"); selections=selections==null?"":selections; %> <form action="${pageContext.request.contextPath }/html/submit.jsp" method="post"> <p>What do you prefer?</p> <p><input type="checkbox" name="car" value="car" <% if(selections.indexOf("car")>-1) out.print("checked=\"checked\""); %> />Car</p> <p><input type="checkbox" name="bike" value="bike" <% if(selections.indexOf("bike")>-1) out.print("checked=\"checked\""); %> />Bike</p> <p><input type="checkbox" name="train" value="train" <% if(selections.indexOf("train")>-1) out.print("checked=\"checked\""); %> />Train</p> <p><input type="checkbox" name="plane" value="plane" <% if(selections.indexOf("plane")>-1) out.print("checked=\"checked\""); %> />Plane</p> <input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/> <input type="submit" value="Submit" /> </form> </body> </html>
submit.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Successful</title> </head> <body> <a href="${pageContext.request.contextPath }/index.jsp">Back</a> <a href="${pageContext.request.contextPath }/html/logout">logout</a> <% StringBuffer sb = new StringBuffer(); if (null !=request.getParameter("car"))sb.append("car;"); if (null !=request.getParameter("bike"))sb.append("bike;"); if (null !=request.getParameter("train"))sb.append("train;"); if (null !=request.getParameter("plane"))sb.append("plane;"); session.setAttribute("selections", sb.toString()); %> Successful, please go back to check.<br> </body> </html>
login.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page pageEncoding="UTF-8" %> <html> <head> <title>Login</title> </head> <body onload="document.f.username.focus();"> <h1>Login</h1> <c:if test="${not empty param.login_error}"> <font color="red"> Your login attempt was not successful, try again.<br/><br/> Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>. </font> </c:if> <form name="f" action="${pageContext.request.contextPath }/html/login" method="POST"> <table> <tr><td>User:</td><td><input type='text' name='username' value='<c:if test="${not empty param.login_error}">fbloggs</c:if>'/></td></tr> <tr><td>Password:</td><td><input type='password' name='password'></td></tr> <tr><td><input type="checkbox" name="remember-me"></td><td>Don't ask for my password for two weeks</td></tr> <tr><td colspan='2'><input name="submit" type="submit"></td></tr> <tr><td colspan='2'><input name="reset" type="reset"></td></tr> </table> <input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/> </form> </body> </html>
403.jsp
<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %> <%@ page import="org.springframework.security.core.Authentication" %> <html> <head> <title>Access Denied</title> </head> <body> <h1>Sorry, access is denied</h1> <p> <%= request.getAttribute("SPRING_SECURITY_403_EXCEPTION")%> </p> <p> <% Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { %> Authentication object as a String: <%= auth.toString() %><br /><br /> <% } %> </p> </body> </html>
forbidden.html
<html> <body> If you see this page, that means web security succeeds. </body> </html>
status.jsp
<%@ page pageEncoding="UTF-8" contentType="application/json;charset=UTF-8"%> {}&&{user: "${pageContext.request.remoteUser}"}
建立以上代碼頁面之後,咱們能夠運行maven install來發布web包,而後將web包部署到一個tomcat上。能夠經過http://localhost:8080/firstWeb 來開始瀏覽全部的頁面。這時候,全部的頁面都是公開的,沒有任何保護。
接下來,咱們使用Spring Security 3來配置認證和受權。 首先假設咱們是用XML來配置的話,通常是通過如下幾個步驟:
在web.xml中註冊springSecurityFilterChain.
在web.xml中指定Spring Security所使用的XML配置文件路徑。
建立XML配置文件。下面給出了一個XML的簡單例子。其中使用的是個人OpenLDAP做爲認證和受權。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:s="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-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <s:http> <s:intercept-url pattern="/**" access="ROLE_RED" /> <s:form-login /> <s:anonymous /> <s:logout /> </s:http> <s:authentication-manager> <s:authentication-provider ref='ldapAuthProvider' /> </s:authentication-manager> <!-- Traditional Bean version of the same configuration --> <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> <constructor-arg value="ldap://127.0.0.1:389/dc=mycompany,dc=com"/> <property name="userDn" value="cn=admin,dc=mycompany,dc=com"/> <property name="password" value="admin"/> </bean> <bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"> <constructor-arg> <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> <constructor-arg ref="contextSource"/> <property name="userDnPatterns"><list><value>uid={0},ou=people</value></list></property> </bean> </constructor-arg> <constructor-arg> <bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator"> <constructor-arg ref="contextSource"/> <constructor-arg value="ou=groups"/> <property name="groupRoleAttribute" value="cn"/> <property name="groupSearchFilter" value="uniqueMember={0}"/> </bean> </constructor-arg> </bean> </beans>
由於本文主要是javaconfig,因此上面的xml只是一個例子,對今天的項目並無實際做用。但上面的xml能夠輕鬆的教導咱們如何生成javaconfig。在缺失大量javaconfig的文檔的狀況下,參考xml能夠指導咱們javaconfig所使用的類與方法。