进阶-使用Spring Security3.2搭建LDAP认证授权和Remember-me(1)

回顾

在之前的一篇博客中,Tomcat认证授权与简单的SSO  我在tomcat cluster上搭建了一个简单的网站,并试验了各种认证授权BA,FBA,以及tomcat自身build-in的SSO机制。但就像摘要里面写的内容一样,tomcat自身的FBA并不好用。重复一遍FBA的缺点:

  1. login的过程无法被干预。我们无法通过添加filter的形式进行干预。login完全交给web容器处理,页面也是有web容器负责展示。

  2. 没有login地址,用户无法bookmark一个Login页面。直接访问login.html是无法提交form的。login只能在访问受保护资源的时候才会被触发。

因此,我必须寻找一个新的认证方案。

认证方案的甄选

以下几点是我需要考虑的:

  1. JAVA平台。

  2. 克服了FBA的缺陷。

  3. 简单易用。

  4. 稳定,一直存在维护,且充满活力。

  5. 可以跟各种认证系统集成,比如CAS,OpenSSO, JAVASSO, Kerberos, SAML.

  6. 易于扩展,比如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 。

  1. 安装eclipse maven插件。help->install new software->输入http://download.eclipse.org/technology/m2e/releases。安装插件。

  2. 创建一个”Maven Project“, 选择下面的archetype:进阶-使用Spring Security3.2搭建LDAP认证授权和Remember-me(1)_第1张图片

  3. 命名group id与artifect id.进阶-使用Spring Security3.2搭建LDAP认证授权和Remember-me(1)_第2张图片

  4. 点击finish以后,一个maven的web项目就建成了。请在java目录下创建java包和类,在resources下面放置resources,而在webapp下面放置web页面文件与配置信息。

    进阶-使用Spring Security3.2搭建LDAP认证授权和Remember-me(1)_第3张图片

  5. 接下来,我们需要修改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>
  6. 在eclipse中,右键点击project firstWeb --->Maven--->Update project. 这样把项目所有依赖的jar包都自动下载到项目中。

Spring Security 3介绍

在进行试验开发之前,我得嘚吧嘚吧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的搭建,请看之前的文章。

首先搭建没有安全保护的网站。如图所示:

进阶-使用Spring Security3.2搭建LDAP认证授权和Remember-me(1)_第4张图片

在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来配置的话,一般是经过以下几个步骤:

  1. 在web.xml中注册springSecurityFilterChain.

  2. 在web.xml中指定Spring Security所使用的XML配置文件路径。

  3. 创建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所使用的类与方法。

文字超上限了,下面请看(2)

进阶-使用Spring Security3.2搭建LDAP认证授权和Remember-me(2) 

你可能感兴趣的:(进阶-使用Spring Security3.2搭建LDAP认证授权和Remember-me(1))