Spring-Security-3-x-秘籍-全-

Spring Security 3.x 秘籍(全)

原文:zh.annas-archive.org/md5/805128EFB9E241233881DA578C0077AD

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

介绍

Spring Security 是 Spring 框架提供的安全层。Spring 框架是一个活跃的开源项目,使应用程序的进一步开发变得更加容易。它提供了各种层来处理项目设计和实施生命周期中面临的不同场景和挑战。

Spring 框架的 Spring Security 层与 Spring 框架的耦合度非常低,因此可以轻松地集成到其他应用程序中。

在本书中,我们将把 Spring Security 与其他框架集成,并通过编码示例进行演示。

本书涵盖的内容

第一章,基本安全,介绍了 J2ee 应用程序中安全性的基础知识。它向读者介绍了各种应用安全性的机制,以对用户进行身份验证和授权。它还解释了容器管理安全性。

第二章,使用 Struts 2 的 Spring 安全性,提供了在 Struts 2 应用程序中集成 Spring Security 的步骤。它演示了使用 Spring 框架提供的其他安全机制进行数据库身份验证和 LDAP 身份验证和授权。

第三章,使用 JSF 的 Spring 安全性,解释了在 JSF 应用程序中使用 Spring Security 的所有方面。它展示了如何使 JSF 应用程序使用监听器与 Spring Security 进行通信。

第四章,使用 Grails 的 Spring 安全性,演示了 grails 应用程序如何与 Spring Security 无缝集成。我们还展示了 Spring Security UI 如何提供屏幕来创建用户和角色。我们演示了在 GSP 页面中使用 Spring Security 标签。

第五章,使用 GWT 的 Spring 安全性,专注于 GWT 框架。GWT 框架与 GWT 集成,Spring Security 可用于对访问 GWT 应用程序的用户进行身份验证和授权。

第六章,使用 Vaadin 的 Spring 安全性,提出了将 Spring Security 与 Vaadin 框架集成的各种选项。我们创建了一个示例产品目录应用程序,以演示 Spring Security 与 Vaadin 框架的集成。

第七章,使用 Wicket 的 Spring 安全性,演示了将 wicket 框架与 Spring Security 集成。Wicket 本身具有内置的身份验证和授权框架,但挑战在于使 wicket 使用外部框架进行身份验证和授权。

第八章,使用 ORM 和 NoSQL DB 的 Spring 安全性,解释了在使用 Spring Security API 类进行身份验证和授权时,使用 Hibernate 和 MongoDB。

第九章,使用 Spring Social 的 Spring 安全性,介绍了 Spring Social,这是由 Spring Source 开发的一个框架,用于提供对社交网络站点的集成。Spring Social 使用 Spring Security 进行身份验证和授权。该章节演示了 Spring Social 和 Spring Security 如何通过演示 Facebook 登录应用程序进行集成。

第十章,使用 Spring Web Services 的 Spring 安全性,解释了保护 RESTFUL 和基于 SOAP 的 Web 服务的各种选项。

第十一章,更多关于 Spring 安全性,是一个杂项章节。它解释了如何将 Spring Security 与 Kaptcha API 集成,并提供多个输入身份验证。

您需要为本书准备什么

为了完成本书中的所有示例,您需要了解以下内容:

  • JBOSS 服务器

  • Netbeans

  • Maven

  • Java

  • Tomcat

  • Open LDAP

  • Apache DS

  • Eclipse IDE

这本书是为谁写的

这本书适用于所有基于 Spring 的应用程序开发人员,以及希望使用 Spring Security 将强大的安全机制实施到 Web 应用程序开发中的 Java Web 开发人员。

读者被假定具有 Java Web 应用程序开发的工作知识,对 Spring 框架有基本了解,并且对 Spring Security 框架架构的基本知识有一定了解。

对其他 Web 框架(如 Grails 等)的工作知识将是利用本书提供的全部食谱的额外优势,但这并非强制要求。

约定

在本书中,您将找到许多不同类型信息之间的区别的文本样式。以下是一些这些样式的示例,以及它们的含义解释。

文本中的代码单词显示如下:“我们可以通过使用include指令包含其他上下文。”

代码块设置如下:

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page language="java" %>
<html >
  <HEAD>
    <TITLE>PACKT Login Form</TITLE>
    <SCRIPT>
      function submitForm() {
        var frm = document. myform;
        if( frm.j_username.value == "" ) {
          alert("please enter your username, its empty");
          frm.j_username.focus();
          return ;
        }

当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示:

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page language="java" %>
<html >
  <HEAD>
    <TITLE>PACKT Login Form</TITLE>
    <SCRIPT>
      function submitForm() {
        var frm = document. myform;
        if( frm.j_username.value == "" ) {
          alert("please enter your username, its empty");
          frm.j_username.focus();
          return ;
        }

任何命令行输入或输出都以以下形式编写:

[INFO] Parameter: groupId, Value: com.packt
[INFO] Parameter: artifactId, Value: spring-security-wicket
[INFO] Parameter: version, Value: 1.0-SNAPSHOT

新术语重要单词以粗体显示。例如,屏幕上看到的单词,例如菜单或对话框中的单词,会在文本中显示为:“单击提交后,我们需要获得经过身份验证的会话。”

注意

警告或重要说明会以这样的框出现。

提示

提示和技巧会以这种形式出现。

第一章:基本安全

在本章中,我们将涵盖:

  • 基于 JAAS 的 JSP 的安全身份验证

  • 基于 JAAS 的 servlet 安全身份验证

  • 基于 servlet 的基本容器身份验证

  • 基于 servlet 的基于表单的身份验证

  • 使用开放 LDAP 和 servlet 进行基于表单的身份验证

  • 在 servlet 上进行哈希/摘要身份验证

  • JAX-WS 和 JAX-RS 的基本身份验证

  • 启用和禁用文件列表

介绍

身份验证和授权已成为所有 Web 应用程序的重要组成部分。身份验证涉及检查谁正在访问应用程序。授权是检查用户访问权限的过程。在本机方法中,我们通常将用户的信息存储在数据库中,并在应用程序中编写代码。我们还为用户创建角色并进行映射。在这里,它与应用程序紧密耦合,因为当我们连接到新数据库或使用其他工具(如 LDAP 或 Kerbose)时,我们必须重写整个代码。但是有高级选项来处理身份验证和授权。 J2EE 容器通过配置 XML 文件提供了不同的用户身份验证方式。我们可以将身份验证分类为两种类型,即基于容器的身份验证和授权以及应用程序级别的身份验证和授权。

J2EE 容器提供接口和类来提供身份验证。在本章中,我们将看到如何使用 JAAS,基本身份验证和基于表单的身份验证来对用户进行身份验证。

在本书中,我们使用了 JAAS,因为它是身份验证的标准框架。 JAAS 基于PAM(可插入身份验证模块)框架工作。

身份验证和授权可以通过以下方式提供:

  • 基本身份验证:在这种技术中,应用程序服务器提供带有用户名和密码文本框的登录表单,因此您无需自己创建登录页面。您还将知道调用者身份。

  • 基于表单的身份验证:在这种技术中,容器处理身份验证,但登录表单由用户提供为 JSP 页面。

  • 基于摘要的身份验证:在这种方法中,用户凭据使用特定算法进行哈希处理。

  • 基于证书的身份验证:在这种技术中,客户端和服务器交换证书以验证其身份。获得 SSL 证书使网络上的数据传输安全。

基于 JAAS 的 JSP 的安全身份验证

部署描述符是所有 Web 应用程序的主要配置文件。容器在启动任何应用程序之前首先查找部署描述符。

部署描述符是WEB-INF文件夹中的 XML 文件web.xml

如果查看web.xml文件的 XSD,可以看到与安全相关的模式。

可以使用以下 URL 访问模式:java.sun.com/xml/ns/j2ee/web-app_2_4.xsd

以下是 XSD 中可用的模式元素:

<xsd:element name="security-constraint" type="j2ee:security-constraintType"/>
<xsd:element name="login-config" type="j2ee:login-configType"/>
<xsd:element name="security-role "type="j2ee:security-roleType"/>

准备就绪

您将需要以下内容来演示身份验证和授权:

  • JBoss 7

  • Eclipse Indigo 3.7

  • 创建一个动态 Web 项目,命名为Security Demo

  • 创建一个包,com.servlets

  • WebContent文件夹中创建一个 XML 文件,jboss-web.xml

  • 创建两个 JSP 页面,login.jsplogoff.jsp

如何做...

执行以下步骤以实现 JSP 的基于 JAAS 的安全性:

  1. 编辑login.jsp文件,使用输入字段j_usernamej_password,并将其提交给SecurityCheckerServlet
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page language="java" %>
<html >
  <HEAD>
    <TITLE>PACKT Login Form</TITLE>
    <SCRIPT>
      function submitForm() {
        var frm = document. myform;
        if( frm.j_username.value == "" ) {
          alert("please enter your username, its empty");
          frm.j_username.focus();
          return ;
        }

        if( frm.j_password.value == "" ) {
          alert("please enter the password,its empty");
          frm.j_password.focus();
          return ;
        }
        frm.submit();
      }
    </SCRIPT>
  </HEAD>
  <BODY>
    <FORM name="myform" action="SecurityCheckerServlet" METHOD=get>
    <TABLE width="100%" border="0" cellspacing="0" cellpadding="1" bgcolor="white">
    <TABLE width="100%" border="0" cellspacing="0" cellpadding="5">
    <TR align="center">
    <TD align="right" class="Prompt"></TD>
    <TD align="left">
      <INPUT type="text" name="j_username" maxlength=20>
    </TD>
    </TR>
    <TR align="center">
    <TD align="right" class="Prompt"> </TD>
    <TD align="left">
    <INPUT type="password"name="j_password" maxlength=20 >
    <BR>
    <TR align="center">
    <TD align="right" class="Prompt"> </TD>
    <TD align="left">
    <input type="submit" onclick="javascript:submitForm();" value="Login">
    </TD>
    </TR>
    </TABLE>
    </FORM>
  </BODY>
</html>

j_usernamej_password是使用基于表单的身份验证的指示符。

  1. 让我们修改web.xml文件以保护所有以.jsp结尾的文件。如果您尝试访问任何 JSP 文件,您将收到一个登录表单,该表单反过来调用SecurityCheckerServlet文件对用户进行身份验证。您还可以看到角色信息被显示。按照以下代码片段中所示更新web.xml文件。我们使用了2.5 xsd。以下代码需要放置在web.xml文件中的webapp标签之间:
<display-name>jaas-jboss</display-name>
 <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
 </welcome-file-list>

 <security-constraint>
    <web-resource-collection>
     <web-resource-name>something</web-resource-name>
     <description>Declarative security tests</description>
     <url-pattern>*.jsp</url-pattern>
     <http-method>HEAD</http-method>
     <http-method>GET</http-method>
     <http-method>POST</http-method>
     <http-method>PUT</http-method>
     <http-method>DELETE</http-method>
    </web-resource-collection>
    <auth-constraint>
     <role-name>role1</role-name>
    </auth-constraint>
    <user-data-constraint>
     <description>no description</description>
     <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
 </security-constraint>
 <login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
     <form-login-page>/login.jsp</form-login-page>
     <form-error-page>/logoff.jsp</form-error-page>
    </form-login-config>
 </login-config>
 <security-role>
    <description>some role</description>
    <role-name>role1</role-name>
 </security-role>
 <security-role>
    <description>packt managers</description>
    <role-name>manager</role-name>
 </security-role>
 <servlet>
    <description></description>
    <display-name>SecurityCheckerServlet</display-name>
    <servlet-name>SecurityCheckerServlet</servlet-name>
    <servlet-class>com.servlets.SecurityCheckerServlet</servlet-class>
 </servlet>
 <servlet-mapping>
    <servlet-name>SecurityCheckerServlet</servlet-name>
    <url-pattern>/SecurityCheckerServlet</url-pattern>
 </servlet-mapping>
  1. JAAS 安全检查器和凭证处理程序:Servlet 是一个安全检查器。由于我们正在使用 JAAS,这是用于身份验证的标准框架,为了执行以下程序,您需要导入org.jboss.security.SimplePrincipalorg.jboss.security.auth.callback.SecurityAssociationHandle并添加所有必要的导入。在以下的SecurityCheckerServlet中,我们从 JSP 文件获取输入并将其传递给CallbackHandler

然后我们将 Handler 对象传递给LoginContext类,该类具有login()方法来进行身份验证。在成功身份验证后,它将为用户创建SubjectPrincipal,并提供用户详细信息。我们使用迭代器接口来迭代LoginContext对象,以获取用于身份验证的用户详细信息。

SecurityCheckerServlet类中:

package com.servlets;
public class SecurityCheckerServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

    public SecurityCheckerServlet() {
      super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       char[] password = null;
       PrintWriter out=response.getWriter();
       try
       {

         SecurityAssociationHandler handler = new SecurityAssociationHandler();
         SimplePrincipal user = new SimplePrincipal(request.getParameter("j_username"));
         password=request.getParameter("j_password").toCharArray();
         handler.setSecurityInfo(user, password);
         System.out.println("password"+password);

         CallbackHandler myHandler = new UserCredentialHandler(request.getParameter("j_username"),request.getParameter("j_password"));
         LoginContext lc = new LoginContext("other", handler);
         lc.login();

         Subject subject = lc.getSubject();
         Set principals = subject.getPrincipals();

         List l=new ArrayList();
         Iterator it = lc.getSubject().getPrincipals().iterator();
         while (it.hasNext()) {
           System.out.println("Authenticated: " + it.next().toString() + "<br>");
           out.println("<b><html><body><font color='green'>Authenticated: " + request.getParameter("j_username")+"<br/>"+it.next().toString() + "<br/></font></b></body></html>");
              }
           it = lc.getSubject().getPublicCredentials(Properties.class).iterator();
           while (it.hasNext()) System.out.println(it.next().toString());

           lc.logout();
       }     catch (Exception e) {
             out.println("<b><font color='red'>failed authenticatation.</font>-</b>"+e);

       }
    }
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   }

}

创建UserCredentialHandler文件:

package com.servlets;
class UserCredentialHandler implements CallbackHandler {
  private String user, pass;

  UserCredentialHandler(String user, String pass) {
    super();
    this.user = user;
    this.pass = pass;
  }
  @Override
  public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
      for (int i = 0; i < callbacks.length; i++) {
        if (callbacks[i] instanceof NameCallback) {
          NameCallback nc = (NameCallback) callbacks[i];
          nc.setName(user);
        } else if (callbacks[i] instanceof PasswordCallback) {
          PasswordCallback pc = (PasswordCallback) callbacks[i];
          pc.setPassword(pass.toCharArray());
        } else {
        throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
      }
    }
  }
 }

jboss-web.xml文件中:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>java:/jaas/other</security-domain>
</jboss-web>

Other是在login-config.xml文件中定义的应用程序策略的名称。

所有这些将被打包为.war文件。

  1. 配置 JBoss 应用服务器。转到 JBoss 中的jboss-5.1.0.GA\server\default\conf\login-config.xml。如果您查看文件,您可以看到用于数据库 LDAP 的各种配置以及使用属性文件的简单配置,我已在以下代码片段中使用:
<application-policy name="other">
  <!-- A simple server login module, which can be used when the number of users is relatively small. It uses two properties files:
  users.properties, which holds users (key) and their password (value).
  roles.properties, which holds users (key) and a comma-separated list of
  their roles (value).
  The unauthenticatedIdentity property defines the name of the principal
  that will be used when a null username and password are presented as is
  the case for an unauthenticated web client or MDB. If you want to allow such users to be authenticated add the property, e.g.,
    unauthenticatedIdentity="nobody"
  -->
  <authentication>
  <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
    flag="required"/>
    <module-option name="usersProperties">users.properties</module-option>
    <module-option name="rolesProperties">roles.properties</module-option>
    <module-option name="unauthenticatedIdentity">nobody</module-option> 
  </authentication>
</application-policy>
  1. 在相同的文件夹中创建users.properties文件。以下是带有用户名映射角色的Users.properties文件。

User.properties

anjana=anjana123

roles.properties

anjana=role1
  1. 重新启动服务器。

提示

下载示例代码

您可以从您在www.PacktPub.com购买的所有 Packt 图书的帐户中下载示例代码文件。如果您在其他地方购买了本书,您可以访问www.PacktPub.com/support并注册以直接通过电子邮件接收文件。

它是如何工作的...

JAAS 由一组接口组成,用于处理身份验证过程。它们是:

  • CallbackHandlerCallback接口

  • LoginModule接口

  • LoginContext

CallbackHandler接口获取用户凭据。它处理凭据并将它们传递给LoginModule,后者对用户进行身份验证。

JAAS 是特定于容器的。每个容器都将有自己的实现,这里我们使用 JBoss 应用服务器来演示 JAAS。

在我的先前的示例中,我已经明确调用了 JASS 接口。

UserCredentialHandler实现了CallbackHandler接口。

因此,CallbackHandler是用户凭据和LoginModule的存储空间对用户进行身份验证。

LoginContextCallbackHandler接口与LoginModule连接起来。它将用户凭据传递给LoginModule接口进行身份验证:

CallbackHandler myHandler = new UserCredentialHandler(request.getParameter("j_username"),request.getParameter("j_password"));
  LoginContext lc = new LoginContext("other", handler);
  lc.login();

web.xml文件定义了安全机制,并指向我们应用程序中的受保护资源。

以下屏幕截图显示了一个身份验证失败的窗口:

它是如何工作的...

以下屏幕截图显示了一个成功的身份验证窗口:

它是如何工作的...

参见

  • 基于 servlet 的 JAAS 安全身份验证的方法

  • 基于容器的 servlet 基本身份验证的方法

  • 基于表单的 servlet 身份验证的方法

  • 基于表单的 LDAP 和 servlet 身份验证的方法

  • 在 servlet 上进行哈希/摘要身份验证的方法

  • JAX-WS 和 JAX-RS 的基本身份验证的方法

  • 启用和禁用文件列表的方法

基于 JAAS 的 servlet 安全身份验证

基于 JAAS 的 servlet 安全身份验证是对 JSP 的基于 JAAS 的安全身份验证的扩展。在本节中,我们演示了我们甚至可以在 servlet 上应用安全性。

准备工作

  • 在 Eclipse 中创建一个新的Web 项目

  • 创建一个名为com.packt.security.servlets的包

  • 创建一个名为ProtectedServlets的 Servlet

如何做...

以下是 servlet 的基于 JAAS 的安全性步骤:

  1. 创建一个名为ProtectedServlets的 servlet:
public class ProtectedServlets extends HttpServlet {
  private static final long serialVersionUID = 1L;

  public ProtectedServlets() {
    super();

  }
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    PrintWriter out=response.getWriter();
    try
    {
      out.println("Hello User");
      out.println("Authtype:"+request.getAuthType());
      out.println("User Principal:"+request.getUserPrincipal());
      out.println("User role:"+request.isUserInRole("role1"));
    }
    catch (Exception e) {
      out.println("<b><font color='red'>failed authenticatation</font>-</b>"+e);

    }
  }

  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
  }

}
  1. 现在,编辑web.xml文件以保护 servlet:
<web-resource-collection>
<web-resource-name>Servlet Protection</web-resource-name>
<description>Declarative security tests</description>
<url-pattern>/ProtectedServlets</url-pattern>
<http-method>HEAD</http-method>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>

它是如何工作的...

重新启动服务器并访问 URL:http://localhost:8080/jaas-jboss/ProtectedServlets

您将获得一个登录表单,该表单将对用户进行身份验证。Servlet 是受保护的资源,任何访问该 servlet 的人都将被要求登录。身份验证由 JAAS API 处理,该 API 是特定于应用服务器的。每个应用服务器都将有自己的安全实现。

另请参阅

  • 基于容器的 servlet 基本身份验证食谱

  • servlet 上的基于表单的身份验证食谱

  • 使用开放 LDAP 和 servlet 进行基于表单的身份验证食谱

  • 在 servlet 上进行哈希/摘要身份验证食谱

  • JAX-WS 和 JAX-RS 的基本身份验证食谱

  • 启用和禁用文件列表食谱

基于容器的 servlet 基本身份验证

在我们之前的示例中,我们使用了 JAAS 提供的接口来通过loginform.jsp进行身份验证。先前的应用程序具有自定义的登录表单设计,身份验证由应用服务器提供的 JAAS API 处理。

准备工作

  • 创建一个简单的 Web 应用程序项目

  • 创建一个 servlet 类

  • 编辑web.xml文件以进行基本身份验证

  • 添加约束以限制用户访问 servlet

如何做...

现在,我们将看到基本身份验证。容器提供登录表单并对用户进行身份验证,验证成功后将用户重定向到 servlet。这里不涉及登录表单。

web.xml文件中进行以下更改:

<login-config>
   <auth-method>BASIC</auth-method>
<form-login-config>  

.war文件导出到 JBoss,重新启动服务器,并访问 servlet。

它是如何工作的...

在先前的示例中,容器通过读取web.xml文件决定了对 servlet 进行身份验证的机制。这里的<auth-method>标签已将BASIC定义为身份验证的模式。当我们访问受保护的资源时,应该会弹出一个登录对话框。

以下截图显示了实现的工作流程:

它是如何工作的...它是如何工作的...

另请参阅

  • servlet 上的基于表单的身份验证食谱

  • 使用开放 LDAP 和 servlet 进行基于表单的身份验证食谱

  • 在 servlet 上进行哈希/摘要身份验证食谱

  • JAX-WS 和 JAX-RS 的基本身份验证食谱

  • 启用和禁用文件列表食谱

servlet 上的基于表单的身份验证

在前几节中,我们演示了 servlet 和 JSP 上的基本身份验证。现在让我们在 servlet 上使用基于表单的身份验证。

准备工作

让我们在 servlet 上应用基于表单的身份验证。您将需要一个简单的 Web 应用程序,其中包括一个 servlet、一个 Web 容器来处理身份验证,以及告诉容器要进行身份验证的web.xml文件。

如何做...

让我们看一些在 servlet 上实现基于表单的身份验证的简单步骤:

  1. 创建一个名为Containerform.jsp的 JSP 文件:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form method="POST" action="j_security_check">
Username:<input type="text" name="j_username">
password:<input type="password" name="j_password">
<input type=submit>
</form>
</body>
</html>

您在先前的代码中观察到了什么?

action=j_security_check是默认的 URL,被 Web 容器识别。它告诉容器它有用户凭据需要进行身份验证。

  1. 现在,编辑web.xml文件:
<login-config>
  <auth-method>FORM</auth-method>
  <form-login-config>
    <form-login-page>/Containerform.jsp</form-login-page>
    <form-error-page>/logoff.jsp</form-error-page>
  </form-login-config>
</login-config>

构建项目并将.war文件导出到 JBoss。

它是如何工作的...

先前的示例演示了基于表单的身份验证。J2EE 容器读取web.xml文件,<auth-method>标签具有设置为form属性。然后它进一步寻找需要显示以进行基于表单的身份验证的login.jsp文件。<form-error-page><form-login-page>具有登录文件名和在身份验证失败时需要显示的错误页面。当用户尝试访问受保护的资源时,J2EE 容器将请求重定向到登录页面。用户凭据提交给j_security_check操作。容器识别此操作并进行身份验证和授权;成功后,用户被重定向到受保护的资源,失败时会显示错误页面。

以下是工作流程的屏幕截图,显示用户的登录页面,并在成功验证时显示用户信息:

它是如何工作的...它是如何工作的...

参见

  • 使用 open LDAP 和 servlet 进行基于表单的身份验证配方

  • 在 servlet 上进行哈希/摘要身份验证配方

  • JAX-WS 和 JAX-RS 的基本身份验证配方

  • 启用和禁用文件列表配方

使用 open LDAP 和 servlet 进行基于表单的身份验证

在本节中,我们将看到如何通过检索存储在 open LDAP 和 JAAS 中的用户信息来对用户进行身份验证。Open LDAP,顾名思义,是轻量级用户目录协议的免费版本,允许我们创建组并向其中添加用户。

准备工作

下载 open LDAP,创建角色、组和用户。

在 JBoss 应用服务器中,编辑login-config.xml文件。

如何做...

执行以下步骤配置应用服务器从 Open LDAP 检索用户:

  1. login-config.xml文件中提供 LDAP 端口的 URL、凭据和需要搜索的域,以找到应用程序提供的用户名和密码:
<application-policy name="example">
 <authentication>
 <login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required" >
 <module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
 <module-option name="java.naming.provider.url">ldap://localhost:389</module-option>
 <module-option name="java.naming.security.authentication">simple</module-option>
 <module-option name="bindDN">cn=Manager,dc=maxcrc,dc=com</module-option>
 <module-option name="bindCredential">secret</module-option>
 <module-option name="baseCtxDN">ou=People,dc=maxcrc,dc=com</module-option>
 <module-option name="baseFilter">(uid={0})</module-option>

 <module-option name="rolesCtxDN">ou=Roles,dc=maxcrc,dc=com</module-option>
  <module-option name="rolesCtxDN">ou=Department,dc=maxcrc,dc=com</module-option>
 <module-option name="roleFilter">(member={1})</module-option>
 <module-option name="roleAttributeID">cn</module-option>
 <module-option name="searchScope">ONELEVEL_SCOPE</module-option>
 <module-option name="allowEmptyPasswords">true</module-option>
 </login-module>
</authentication>
</application-policy>
  1. jboss-web.xml文件中,我们将为 JAAS 指定查找名称:
jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>java:/jaas/example</security-domain>
</jboss-web>

它是如何工作的...

在 JBoss 上构建和部署 WAR,重新启动服务器,并访问浏览器。

您将收到一个登录表单,并且 JBoss 根据提供的 open LDAP 凭据对用户进行身份验证。用户被检索并根据应用程序策略中提到的角色进行授权。容器为身份验证提供了内置的 API。模块org.jboss.security.auth.spi.LdapExtLoginModule处理 LDAP 身份验证过程。

参见

  • 在 servlet 上进行哈希/摘要身份验证配方

  • JAX-WS 和 JAX-RS 的基本身份验证配方

  • 启用和禁用文件列表配方

在 servlet 上进行哈希/摘要身份验证

在先前的身份验证机制中,客户端发送用户凭据,容器进行验证。

客户端不尝试加密密码。

因此,我们的应用程序仍然不安全,容易受到攻击。

本节是关于向服务器传递加密的用户凭据,并告诉服务器可以使用哪种加密算法来解密数据。

JBoss 是我选择来演示的应用服务器。

准备工作

  • 修改Login-config.xml

  • 创建encrypt-users. properties

  • 创建encrypt-roles. properties

如何做....

  1. 修改web.xml文件:
<login-config>
    <auth-method>DIGEST</auth-method>
    <realm-name>PACKTSecurity</realm-name>
</login-config>
  1. 现在,修改jboss-web.xml文件。领域名称用于哈希:
<?xml version="1.0" encoding="UTF-8"?>
<!-- <jboss-web> -->
<!-- <security-domain>java:/jaas/other</security-domain> -->
<!-- </jboss-web> -->
<jboss-web>
<security-domain>java:/jaas/encryptme</security-domain>
</jboss-web>
  1. 修改login-config.xml文件
<application-policy name="encryptme">
    <!--this is used to demonstrate DIGEST Authentication
    -->
    <authentication>
      <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
        flag="required"/>
    <module-option name="usersProperties">encrypt-users.properties</module-option>
    <module-option name="rolesProperties">encrypt-roles.properties</module-option>
    <module-option name="hashAlgorithm">MD5</module-option>
    <module-option name="hashEncoding">rfc2617</module-option>
    <module-option name="hashUserPassword">false</module-option>
    <module-option name="hashStorePassword">true</module-option>
    <module-option name="passwordIsA1Hash">true</module-option>
   <module-option name="storeDigestCallback">
                org.jboss.security.auth.spi.RFC2617Digest
    </module-option>	
    </authentication>
  </application-policy>
  1. 现在,我们需要告诉 JBoss 加密用户的密码。要做到这一点,执行以下步骤:
  • 转到E:\JBOSS5.1\jboss-5.1.0.GA\common\lib

  • 打开jbosssx-server.jar

  • 转到安装 JBoss 的文件夹。我已经在我的E:上安装了 JBoss

  • 现在在命令行上,写cd E:\JBOSS5.1\jboss-5.1.0.GA>

  • 然后粘贴以下命令:java -cp client/jboss-logging-spi.jar;common/lib/jbosssx-server.jar org.jboss.security.auth.spi.RFC2617Digest anjana "PACKTSecurity" role1如何做...

  • 现在编辑Encrypt-users. properties

anjana=e3b6b01ec4b0bdd3fc1ff24d0ccabf1f
  • 加密角色并更新roles.properties

它是如何工作的...

前面的示例演示了摘要身份验证机制。在 J2EE 容器中给定的密码使用 MD5 算法进行加密。容器对其进行解密,并根据解密后的密码验证用户凭据。身份验证机制是digest,容器弹出一个与基本身份验证机制类似的摘要机制登录对话框。

以下屏幕截图显示了工作流程:

它是如何工作的...

它的行为类似于基本身份验证,但使用加密密码以及领域名称进行解密。

另请参阅

  • JAX-WS 和 JAX-RS 的基本身份验证配方

  • 启用和禁用文件列表配方

JAX-WS 和 JAX-RS 的基本身份验证

JAX-WS 和 JAX-RS 的身份验证配置保持不变。

我们需要在<web-resource collection>中给出 JAX-WS 或 JAX-RS URL。

Auth_type可以是基本的。容器将提供一个表单,供用户输入用户名和密码。

由容器处理的身份验证

我们将首先创建一个 Web 服务,然后让容器处理其安全性。

让我们创建一个将公开service方法的接口,然后声明一个implementation类。

让我们使用 Tomcat 6.0 来演示这一点。

准备工作

  • 在 Eclipse-Indigo 中,创建一个动态 Web 项目

  • 服务器:Tomcat 6

  • 要添加到 Tomcat lib文件夹的 JAR 文件:jax-ws.java.net/2.2.7/

  • 下载项目并复制lib文件夹

如何做...

  1. 创建一个interface和一个implementation类。为其添加@WebService注释。创建一个名为com.packt.ws的包。创建一个名为EmployeeProfile的接口和一个implementation类:

接口:

package com.packt.ws;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
@WebService
@SOAPBinding(style = Style.RPC)
public interface EmployeeProfile {
  @WebMethod
  String getSalary();
}

实施:

package com.packt.ws;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService(endpointInterface = "com.packt.ws.EmployeeProfile")
public class EmployeeProfileImpl implements EmployeeProfile {
         @Override
public String getSalary() {
    return "no salary for the month";
}
}
  1. 还在WEB-INF下添加sun-jaxws.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<endpoints

  version="2.0">
  <endpoint
      name="EmployeeProfile"
      implementation="com.packt.EmployeeProfileImpl"
      url-pattern="/employee"/>
</endpoints>
  1. 修改web.xml文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>JAX-WS-Authentication-Tomcat</display-name>
   <listener>
        <listener-class>
           com.sun.xml.ws.transport.http.servlet.WSServletContextListener
        </listener-class>
    </listener>
    <servlet>
        <servlet-name>employee</servlet-name>
        <servlet-class>
        com.sun.xml.ws.transport.http.servlet.WSServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>employee</servlet-name>
        <url-pattern>/employee</url-pattern>
    </servlet-mapping>
   <security-role>
     <description>Normal operator user</description>
     <role-name>operator</role-name>
   	</security-role>

<security-constraint>
      <web-resource-collection>
        <web-resource-name>Operator Roles Security</web-resource-name>
        <url-pattern>/employee</url-pattern>
      </web-resource-collection>

      <auth-constraint>
        <role-name>operator</role-name>
      </auth-constraint>
      <user-data-constraint>
          <transport-guarantee>NONE</transport-guarantee>
      </user-data-constraint>
   </security-constraint>

<login-config>
      <auth-method>BASIC</auth-method>
   </login-config>

</web-app>
  1. 验证 Web 服务。编辑tomcat-users.xml文件并将其添加到server.xml
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
             resourceName="UserDatabase"/>

它是如何工作的...

通过访问以下 URL,您应该会被提示登录。

每个 Web 服务 URL 都经过身份验证。

您将被提示输入登录页面(http://localhost:8080/EmployeeProfile/employee

另请参阅

  • 启用和禁用文件列表配方

启用和禁用文件列表

通常不建议在应用程序中启用目录列表。默认情况下,JBoss 上将禁用目录列表。

如果启用了,转到您的 JBoss 安装文件夹。

如何做...

以下步骤将帮助在应用程序服务器中禁用和启用文件列表:

  1. 浏览到路径\server\default\deployers\jbossweb.deployer

  2. WEB-INF文件夹中打开web.xml

  3. 将列表设置为false

<servlet>
      <servlet-name>default</servlet-name>
      <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
      <init-param>
         <param-name>debug</param-name>
         <param-value>0</param-value>
      </init-param>
      <init-param>
         <param-name>listings</param-name>
         <param-value>false</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
   </servlet>

另请参阅

  • Spring Security with Struts2配方

第二章:Struts 2 中的 Spring Security

在本章中,我们将涵盖:

  • 将 Struts 2 与 Spring Security 集成

  • 具有基本 Spring Security 的 Struts 2 应用程序

  • 在 Struts 2 中使用基于摘要/哈希的 Spring Security

  • 在 Struts 2 中使用 Spring Security 注销

  • 使用 Struts 2 和 Spring Security 进行数据库身份验证

  • 在 Struts 2 中使用 Spring Security 获取已登录用户信息

  • 在 Struts 2 中显示自定义错误消息以处理身份验证失败

  • 使用 ApacheDS 进行 Spring Security 和 Struts 2 应用程序的身份验证

介绍

我们在第一章中学习了安全的基础知识,基本安全,这有助于我们更好地理解 Spring Security,也了解了 Spring 框架中 Spring Security 组件的起源。

在本章中,让我们看看如何在基于 Struts 2 框架的 Web 应用程序中使用 Spring Security 来对用户进行身份验证。

Apache Struts 2 可以与 JSF 和 Spring 集成。它是一个非常灵活的基于 POJO Action 的 MVC 框架。POJO 本身扮演一个动作类的角色来满足请求。Struts 2 源自另一个称为 WebWork 的框架,它与 Servlet 过滤器一起工作,拦截请求和响应。

探索 Spring 包

您可以直接从 MAVEN 下载 JAR 文件,或者在您的 POM 文件中添加依赖项。

我们更喜欢使用最新的 JAR 文件 3.1.4,从mvnrepository.com/artifact/org.springframework.security/spring-security-core/下载:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>3.1.4.RELEASE</version>
 </dependency> 
 <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>3.1.4.RELEASE</version>
  </dependency> 
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>3.1.4.RELEASE</version>
  </dependency>

Spring Security 中的主要包

  • org.springframework.security.authentication:这是我们感兴趣的领域

  • org.springframework.security.crypto:这用于加密和解密

  • org.springframework.security.util:这是 Spring Security API 中使用的通用实用程序类

  • org.springframework.security.core:这包含与身份验证和授权相关的安全核心类

  • org.springframework.security.access:这包含基于投票者的安全访问控制注释和决策接口

  • org.springframework.security.provisioning:这包含用户和组配置接口

Spring Security 的关键特性

  • 支持 JAAS。

  • 支持数据库。

  • 支持 MongoDB 身份验证。

  • 提供 OpenID 身份验证。

  • 演示多租户。

  • 提供基本身份验证。

  • 提供摘要身份验证。

  • Spring Security 像一个独立的模块一样工作。身份验证代码由 Spring Security 框架独立处理。

  • 支持与 ApacheDS 进行身份验证。

  • 支持 Open LDAP 身份验证。

身份验证机制

  1. 用户提交他们的凭据到系统中;也就是说,用户名和密码。

  2. org.springframework.security.authentication.UsernamePasswordAuthenticationToken接受凭据并将它们传递给org.springframework.security.authentication.AuthenticationManager进行验证。

  3. 系统对用户进行身份验证。

  4. 凭据流如下:UsernamePasswordAuthenticationToken | AuthenticationManager | Authentication

  5. 最后返回一个完全加载的身份验证实例。

  6. SecurityContextHolder接受身份验证实例。

  7. 系统还会检查角色或组的授权。

  8. 最后,根据用户的授权,允许用户访问系统。

将 Struts 2 与 Spring Security 集成

让我们首先设置一个 Struts 2 应用程序,并将 Spring Security 与其集成。

准备工作

  • Eclipse Indigo 或更高版本

  • JBoss 作为服务器

  • Struts 2 JARs:2.1.x

  • Spring-core JAR 文件 3.1.4。发布和 Spring-Security 3.1.4。发布

  • Struts 2 Spring 插件 jar

如何做...

在本节中,我们将学习如何使用基于表单的 Spring Security 设置 Struts 2 应用程序:

  1. 在您的 Eclipse IDE 中,创建一个动态 Web 项目并命名为Spring_Security_Struts2

  2. src/main/java下创建一个源文件夹。

  3. 在源文件夹src/main/java下创建一个struts.xml文件。

  4. 要将 Struts 2 与 Spring 应用程序集成,需要在此处添加application-context.xml文件引用。

  5. web.xml中添加 Struts 过滤器映射。还需要在web.xml文件中添加 Spring 监听器。监听器条目应位于 Struts 2 过滤器条目之上。

  6. contextLoaderListener将告诉servletcontainer有关springcontextLoader,并且它将跟踪事件。这还允许开发人员创建BeanListeners,以便跟踪 Bean 中的事件。

  7. web.xml文件中,添加以下代码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Struts2x</display-name>
<listener>  
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
</listener>
<!—to integrate spring with struts2->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>

<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>
  1. 要设置基于表单的安全性,我们需要创建login.jsp。表单操作为j_spring_security_check
<%@ taglib prefix="c" url="http://java.sun.com/jsp/jstl/core"%>
<html>
  <head>
  <title>Login Page</title>
  <style>
    .errorblock {
      color: #ff0000;
      background-color: #ffEEEE;
      border: 3px solid #ff0000;
      padding: 8px;
      margin: 16px;
    }
  </style>
  </head>
  <body onload='document.f.j_username.focus();'>
    <h3>Login with Username and Password (Custom Page)</h3>
    <% String error=request.getParameter("error");

    if(error!=null){
      %>

      <div class="errorblock">
      Your login attempt was not successful, try again.<br /> Caused :

      </div>

    <%} %>
    <form name='f' action="<c:url value='/j_spring_security_check'/>"
    method='POST'>

    <table>
      <tr>
        <td>User:</td>
        <td><input type='text' name='j_username' value=''>
        </td>
      </tr>
      <tr>
        <td>Password:</td>
        <td><input type='password' name='j_password' />
        </td>
      </tr>
      <tr>
        <td colspan='2'><input name="submit" type="submit"
        value="submit" />
        </td>
      </tr>
      <tr>
        <td colspan='2'><input name="reset" type="reset" />
        </td>
      </tr>
    </table>

    </form>
  </body>
</html>
  1. 创建一个名为secure/hello.jsp的文件夹。

  2. login操作与login.jsp进行映射。

  3. loginfailed操作与login.jsp?error=true进行映射。

  4. welcome操作与secure/hello.jsp进行映射,操作类为HelloWorld

struts.xml

<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
  <package name="default" namespace="/" extends="struts-default">
  <action name="helloWorld">
    <result>success.jsp</result>
  </action>

  <action name="login">
    <result>login.jsp</result>
  </action>

  <action name="loginfailed">
    <result>login.jsp?error=true</result>
  </action>

  <action name="welcome" >
    <result>secure/hello.jsp</result>
  </action>

  </package>
</struts>
  1. login page URL 与 Struts 2 操作'/login'进行了映射。

  2. 安全性应用于 Struts 2 操作'/welcome'

  3. 用户将被提示登录。

  4. 具有role_user的用户将被授权访问页面

Applicationcontext-security.xml

<beans:beans xmlns="http://www.springframework.org
/schema/security"
   xmlns:beans="http://www.springframework.org
/schema/beans" 

   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-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">
        <!-- AspectJ pointcut expression that locates our "post" method and applies security that way
        <protect-pointcut expression="execution(* bigbank.*Service.post*(..))" access="ROLE_TELLER"/>
        -->
    </global-method-security>
   <http auto-config="true" use-expressions="true" >
          <intercept-url pattern="/welcome" 
access="hasRole('ROLE_USER')"/>
          <form-login login-page="/login" default-target-
url="/welcome" authentication-failure-
url="/loginfailed?error=true" />
          <logout/>
   </http>
    <authentication-manager>
     <authentication-provider>
       <user-service>
          <user name="anjana" password="packt123" authorities="ROLE_USER" />
       </user-service>
     </authentication-provider>
   </authentication-manager>

</beans:beans>

工作原理...

只需运行应用程序。您将获得一个链接来访问受保护的页面。点击链接后,将提示您登录。这实际上是基于表单的登录。

在提交后,操作被发送到 Spring 框架进行用户身份验证。

成功后,用户将看到经过身份验证的页面。

Struts 2 框架与 Spring 框架及其模块非常容易融合,只需进行非常小的修改。

工作原理...工作原理...工作原理...

另请参阅

  • 具有基本 Spring Security 的 Struts 2 应用程序配方

  • 使用基于摘要/哈希的 Spring Security 与 Struts 2配方

  • 在 Struts 2 中显示自定义身份验证失败消息配方

  • 使用 Struts 2 和 Spring Security 进行数据库身份验证配方

  • 使用 Spring Security 和 Struts 2 应用程序进行 ApacheDS 身份验证配方

  • 使用 Spring Security 与 Struts 2 进行注销配方

  • 在 Struts 2 中获取 Spring Security 中已登录用户信息配方

具有基本 Spring Security 的 Struts 2 应用程序

在本节中,我们将演示如何在 Struts 2 中进行基本的 Spring Security 身份验证。我们将创建一个示例 Struts 2 应用程序,并向操作添加 Spring Security 功能,使其受到保护。只有经过身份验证的授权用户才能访问它。

准备工作

  • 更新Applicationcontext-security.xml文件

  • 在 Eclipse 中创建一个新的动态项目:Struts2_Spring_BASIC_Security_Recipe2

如何做...

执行以下步骤,将 Struts 2 应用程序与 Spring Security 集成以实现基本身份验证:

  1. 修改applicationcontext-security.xml文件以支持基本安全性:

Applicationcontext-security.xml

<beans:beans 

   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-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">
        <!-- AspectJ pointcut expression that locates our "post" method and applies security that way
        <protect-pointcut expression="execution(* bigbank.*Service.post*(..))" access="ROLE_TELLER"/>
        -->
    </global-method-security>

  <http>
   <intercept-url pattern="/welcome" access="ROLE_TELLER" />
   <http-basic />
  </http>
   <authentication-manager>
     <authentication-provider>
       <user-service>
         <user name="anjana" password="123456" authorities="ROLE_TELLER" />
       </user-service>
     </authentication-provider>
   </authentication-manager>
</beans:beans>

工作原理...

当用户运行 Struts 2 应用程序并尝试访问受保护的资源时,Spring Security 上下文将被初始化,并且 Spring 的登录对话框将中断 Struts 2 操作,该对话框将请求用户名和密码。验证成功后,用户将被重定向到 Struts 2 操作页面。

以下是应用程序的工作流程:

在浏览器上的 Struts 2 和 Spring 基本安全性:

工作原理...

另请参阅

  • 使用基于摘要/哈希的 Spring Security 与 Struts 2配方

使用基于摘要/哈希的 Spring Security 与 Struts 2

使用基于表单或基本身份验证并不会使 Struts 2 应用程序变得安全,因为密码会以明文形式暴露给用户。Spring Security JAR 中有一个加密包。该包可以解密加密的密码,但我们需要告诉 Spring Security API 有关加密算法的信息。

准备工作

  • 在 Eclipse 中创建一个动态 Web 项目

  • 添加 Struts 2 JAR 包

  • 添加与 Spring Security 相关的 JAR 包

  • web.xmlstruts2.xml和 JSP 设置与先前的应用程序相同

如何做...

让我们加密密码:packt123456

我们需要使用外部 JAR,JACKSUM,这意味着 Java 校验和。它支持 MD5 和 SHA1 加密。

下载jacksum.zip文件(www.jonelo.de/java/jacksum/#Download)并解压缩 ZIP 文件。

packt>java -jar jacksum.jar -a sha -q"txt:packt123456"

如何做...

更新applicationcontext-security.xml文件:

<beans:beans 

   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-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">
        <!-- AspectJ pointcut expression that locates our "post" method and applies security that way
        <protect-pointcut expression="execution(* bigbank.*Service.post*(..))" access="ROLE_TELLER"/>
        -->
    </global-method-security>
  <http>
   <intercept-url pattern="/welcome" access="ROLE_TELLER" />
   <http-basic />
  </http>
   <authentication-manager>
      <authentication-provider>
   <password-encoder hash="sha" />
      <user-service>
         <user name="anjana" password="bde892ed4e131546a2f9997cc94d31e2c8f18b2a" 
          authorities="ROLE_TELLER" />
      </user-service>
   </authentication-provider>
   </authentication-manager>
</beans:beans>

它是如何工作的...

我们需要更新Applicationcontext-security.xml文件。注意,认证类型是基本的,但密码是使用算法进行哈希处理。我们希望 Spring Security 使用 SHA 算法对其进行解密并对用户进行身份验证。

Spring Security 在处理摘要身份验证方面非常灵活。您还可以看到没有基于容器的依赖关系。

可以在以下截图中看到来自浏览器的基本身份验证:

它是如何工作的...

Spring 已通过解密密码对用户进行了身份验证:

它是如何工作的...

另请参阅

  • 在 Struts 2 中显示自定义错误消息以处理身份验证失败配方

  • 使用 Struts 2 和 Spring Security 进行身份验证数据库配方

  • 使用 Spring Security 和 Struts 2 应用程序进行 ApacheDS 身份验证配方

  • 在 Struts 2 中使用 Spring Security 注销配方

  • 在 Struts 2 中使用 Spring Security 获取已登录用户信息配方

在 Struts 2 中使用 Spring Security 注销

在本节中,让我们实现一个注销场景,已登录用户将从应用程序中注销。注销操作将由 Spring Security 框架处理。我们需要配置struts.xml文件以处理j_spring_security_logout操作。

准备工作

  • 在 Eclipse 中创建一个动态 Web 项目

  • 添加与 Struts 2 相关的 JAR 包

  • 添加与 Spring Security 相关的 JAR 包

  • web.xmlstruts2.xml和 JSP 设置与先前的应用程序相同

如何做...

  1. 让我们更新安全页面hello.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page import="java.security.Principal" %>
<html>
<body>
Hello .You are seeing a secured Page now.

   <a href="<c:url value="/j_spring_security_logout" />" > Logout</a>
 </body>
</html>
  1. 让我们将j_spring_security_logoutstruts.xml文件进行映射:

当用户点击注销时,用户将被注销并重定向到index.jsp

<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
   <package name="default" namespace="/" extends="struts-default">
        <action name="helloWorld">
            <result>success.jsp</result>
        </action>

      <action name="login">
                <result>login.jsp</result>
         </action>

         <action name="loginfailed">
                <result>login.jsp?error=true</result>
         </action>

         <action name="welcome" >
         <result>secure/hello.jsp</result>
         </action>

   <action name="j_spring_security_logout">
   <result>index.jsp</result>
         </action>
    </package>
</struts>
  1. 更新applicationcontext-security.xml文件:
<beans:beans 

   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-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">
    </global-method-security>
  <http>
   <intercept-url pattern="/welcome" access="ROLE_TELLER" />
   <logout logout-success-url="/helloWorld" />
   <http-basic />
  </http>
   <authentication-manager>
      <authentication-provider>
   <password-encoder hash="sha" />
      <user-service>
         <user name="anjana" password="bde892ed4e131546a2f9997cc94d31e2c8f18b2a" 
             authorities="ROLE_TELLER" />
      </user-service>
   </authentication-provider>
   </authentication-manager>
</beans:beans>

它是如何工作的...

Spring Security 还提供了处理注销的选项。当用户点击注销时,用户将被重定向到指定页面。

j_spring_secuurity_logout为 Struts 2 应用程序提供了注销选项。

Struts 2 应用程序具有其操作的地图和 URL。

注销选项通常在受保护的页面中提供。

还有更多...

到目前为止,我们已将身份验证信息存储在.xml文件中。我们还对密码进行了哈希处理。如何在外部系统上存储信息并获取它呢?让我们看看 Struts 2 如何在以下部分与此数据库身份验证一起工作。

另请参阅

  • 在 Struts 2 中显示自定义错误消息以处理身份验证失败配方

  • 在 Struts 2 中使用 Spring Security 进行身份验证数据库配方

  • 使用 Spring Security 和 Struts 2 应用程序进行 ApacheDS 身份验证配方

  • 在 Struts 2 中使用 Spring Security 获取已登录用户信息配方

使用 Struts 2 和 Spring Security 进行身份验证数据库

在本节中,让我们使用存储在数据库中的信息对登录到 Struts 2 应用程序的用户进行授权。Spring Security 需要在 Struts 2 应用程序中进行配置,以便它了解数据库的位置和需要执行的 SQL,以使用 Spring Security 对用户进行身份验证。

准备工作

  • 在 Eclipse 中创建一个动态 Web 项目:Struts2_Spring_DBAuthentication_Recipe4

  • struts.xml文件复制到src/main/java

  • db-beans.xml文件添加到WEB-INF

  • 从上一个配方中复制webContent文件夹

  • 将以下 JAR 文件添加到lib文件夹中,或者如果使用 maven,则更新您的 POM 文件:

  • spring-jdbc-3.0.7.RELEASE

  • mysql-connector-java-5.1.17

  • commons-dbcp

  • commons-pool-1.5.4

如何做...

  1. 要使用 Struts 2 和 Spring 进行数据库身份验证,我们需要创建一个db-beans.xml文件。db-beans.xml文件将包含数据库信息:
<beans 

   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="MySqlDatasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver" />
   <property name="url" value="jdbc:mysql://localhost:3306/test1" />
   <property name="username" value="root" />
   <property name="password" value="prdc123" />
   </bean>
 </beans>
  1. 在与applicationcontext-security.xml相同的位置添加db-beans.xml文件。更新web.xml文件以读取db-beans.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>SpringStruts2Security</display-name>
 <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>
                /WEB-INF/db-beans.xml,
                /WEB-INF/applicationContext-security.xml
          </param-value>
   </context-param>

  <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>
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  </filter>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <error-page>
          <error-code>403</error-code>
          <location>/secure/denied.jsp</location>
   </error-page>

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  1. 在数据库中运行以下 SQL 脚本:
CREATE TABLE `users1` (  `USER_ID` INT(10) UNSIGNED NOT NULL,
  `USERNAME` VARCHAR(45) NOT NULL,
  `PASSWORD` VARCHAR(45) NOT NULL,
  `ENABLED` tinyint(1) NOT NULL,
  PRIMARY KEY (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_roles` (
  `USER_ROLE_ID` INT(10) UNSIGNED NOT NULL,
  `USER_ID` INT(10) UNSIGNED NOT NULL,
  `ROLE` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`USER_ROLE_ID`),
  KEY `FK_user_roles` (`USER_ID`),
  CONSTRAINT `FK_user_roles` FOREIGN KEY (`USER_ID`) REFERENCES `users` (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO test1.users (USER_ID, USERNAME,PASSWORD, ENABLED)
VALUES (100, 'anjana', 'packt123456', TRUE);

INSERT INTO test1.user_roles (USER_ROLE_ID, USER_ID,AUTHORITY)
VALUES (1, 100, 'ROLE_TELLER');
  1. 更新applicationContext-security.xml文件以读取数据库配置:
<beans:beans 

   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-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">
        <!-- AspectJ pointcut expression that locates our "post" method and applies security that way
        <protect-pointcut expression="execution(* bigbank.*Service.post*(..))" access="ROLE_TELLER"/>
        -->
    </global-method-security>

  <http>
   <intercept-url pattern="/welcome" access="ROLE_TELLER" />
   <logout logout-success-url="/helloWorld" />
   <http-basic />
  </http>

   <authentication-manager> 
      <authentication-provider> 
         <jdbc-user-service data-source-ref="MySqlDS" 

            users-by-username-query=" 
                select username,password, enabled   
               from users1 where username=?"  

            authorities-by-username-query=" 
               select u.username, ur.role from users1 u, user_roles ur  
         where u.user_id = ur.user_id and u.username =?  "  
         /> 
      </authentication-provider>
   </authentication-manager>
</beans:beans>

它是如何工作的...

Struts 2 框架提供了一个链接来访问受保护的页面。但是 Spring Security 框架会中断并提供身份验证对话框。身份验证由 Spring Security 框架通过查询数据库完成。身份验证管理器配置了数据源引用,该引用将加载用于基于查询对用户进行身份验证的安全框架的信息。

还有更多...

到目前为止,我们只是在 JSP 文件中应用了安全性,该文件在struts2.xml中没有操作映射。让我们看看如何将操作类与 JSP 映射,然后与 Spring Security 集成。理想情况下,它应该以相同的方式工作。让我们在操作类中获取已登录用户的信息并在浏览器上显示出来。

另请参阅

  • 在 Struts 2 中显示身份验证失败的自定义错误消息示例

  • 使用 Spring Security 和 Struts 2 应用程序进行 ApacheDS 身份验证示例

  • 在 Struts 2 中使用 Spring Security 获取已登录用户信息示例

在 Struts 2 中使用 Spring Security 获取已登录用户信息

到目前为止,在我们的示例中,我们还没有使用任何 Struts 2 操作类。

让我们创建一个操作类并查看安全性如何与此操作类一起运行。我们将在此示例中使用基于表单的身份验证。

准备工作

到目前为止,在我们的示例中,我们还没有使用任何 Struts 2 操作类。

让我们创建一个操作类并查看安全性如何与此操作类一起运行。我们将在此示例中使用基于表单的身份验证:

  • 创建一个动态 Web 项目:Struts2_Spring_Security_Recipe5

  • 创建一个包:com.packt.action

  • 从上一个示例中复制struts.xml文件到src/main/java

  • 还要复制WebContent文件夹

  • 我们需要向包中添加一个操作类

  • 更新struts.xml文件

如何做...

  1. HelloAction文件如下:
package com.packt.action;
public class HelloAction {
         public String execute(){
         return "SUCCESS";
   }
}
  1. 使用HelloAction更新Struts.xml文件。因此,当用户经过身份验证时,它将将请求传递给操作类,该操作类将执行execute()方法,然后将重定向到hello.jsp
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
   <package name="default" namespace="/" extends="struts-default">
        <action name="helloWorld">
            <result>success.jsp</result>
        </action>

      <action name="login">
               <result>login.jsp</result>
         </action>

         <action name="loginfailed">
               <result>login.jsp?error=true</result>
         </action>

         <action name="welcome" class="com.packt.action.HelloAction">
         <result name="SUCCESS">secure/hello.jsp</result>
         </action>

    </package>
</struts>
  1. 获取已登录用户:

我们可以在操作类中获取已登录的用户名,并在页面上显示它,或者在我们的应用程序中进一步使用它。

我们可以在我们的操作类中使用request.getUserPrincipal来获取已登录用户的信息。

  1. 对于项目设置:
  • 在 Eclipse 中创建一个动态 Web 项目:Struts2_Spring_Security_Recipe6

  • 从上一个示例中复制src/main/java文件夹

  • 从上一个示例中复制Web content文件夹

  • 修改HelloAction.java文件

package com.packt.action;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
public class HelloAction {
   private String name;
               public String execute(){
               HttpServletRequest request = ServletActionContext.getRequest();
               String logged_in_user=request.getUserPrincipal().getName();
               setName(logged_in_user);
               return "SUCCESS";
         }

         public String getName() {
               return name;
         }

         public void setName(String name) {
               this.name = name;
         }
}
  • 修改secure/Hello.jsp文件:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="/struts-tags" prefix="s" %>
<%@page import="java.security.Principal" %>
<html>
  <body>
    Hello <h1><s:property value="name" /></h1>.You are seeing a secured Page now.
    <a href="<c:url value="/j_spring_security_logout" />" > Logout</a>
  </body>
</html>

它是如何工作的...

用户信息存储在 principal 中:

它是如何工作的...

在浏览器上显示已登录用户:

它是如何工作的...

还有更多...

显示用户信息后,我们可以在身份验证失败时向用户显示自定义错误消息。

另请参阅

  • 在 Struts 2 中显示身份验证失败的自定义错误消息示例

  • 使用 Spring Security 和 Struts 2 应用程序进行 ApacheDS 身份验证示例

在 Struts 2 中显示身份验证失败的自定义错误消息

在本节中,我们将在 Struts 2 应用程序中捕获 Spring Security 的身份验证失败消息,并查看如何将其显示给用户。

准备工作

  • 在身份验证失败时重定向到失败操作

  • 向用户显示自定义消息

如何做...

执行以下步骤以捕获 Spring Security 在 JSP 应用程序中的身份验证失败消息:

  1. applicationcontext.xml文件中,我们可以将 URL 重定向到另一个操作:Authentication-failure-url="/loginfailed? error=true"
<http auto-config="true" use-expressions="true" >
         <intercept-url pattern="/welcome" access="hasRole('ROLE_TELLER')"/>
         <form-login login-page="/login" default-target-url="/welcome" authentication-failure-url="/loginfailed?error=true" />
         <logout/>
   </http>
  1. 使用以下代码更新login.jsp页面:
<% String error=request.getParameter("error");

 if(error!=null){
 %>

          <div class="errorblock">
                Your login attempt was not successful, try again.<br /> Caused :

          </div>

 <%} %>

它是如何工作的...

登录失败操作与struts2.xml中的login.jsp文件进行了映射。application-context.xml中添加了authentication-failure-url。当用户输入错误的凭据时,身份验证失败,用户将被重定向到带有错误消息的登录页面。

错误消息配置在 JSP 文件中完成。

它是如何工作的...

另请参阅

  • 使用 Spring Security 和 Struts 2 应用程序进行 ApacheDS 身份验证的食谱

使用 Spring Security 和 Struts 2 应用程序进行 ApacheDS 身份验证

在本节中,我们将在 Apache 目录服务器中存储用户凭据和角色信息。Spring Security 必须找到服务器并登录到服务器。它应该通过比较用户提交的凭据和 Apache 目录服务器中存在的凭据和角色信息来对用户进行身份验证。

准备就绪

  • 在 Eclipse 中创建一个动态 Web 项目

  • src/main/java文件夹和WebContent文件夹保持不变

  • 安装 Apache 目录工作室:1.5.3

  • 安装 Apache 目录服务器:2.0

  • 10389 是 apache-ds 端口

  • 将与 LDAP 相关的安全 JAR 添加到WebContent Lib文件夹中。

  • spring-ldap-core-tiger-1.3.X 版本

  • spring-ldap-odm-1.3.X 版本

  • spring-security-ldap-1.3.X 版本

  • spring-ldap-ldif-batch-1.3.X 版本

  • spring-ldap-test-1.3.X 版本

  • spring-ldap-core-1.3.X 版本

  • spring-ldap-ldif-core-1.3.X 版本

如何做...

执行以下步骤设置 Apache 目录以使用 Spring Security 在 Struts 2 应用程序中对用户进行身份验证:

  1. 在安装了上述先决条件之后配置 Apache DS 服务器。

  2. 使用以下步骤创建一个分区:

  • 打开server.xml文件:C:\Program Files\Apache Directory Server\instances\default\conf\server.xml

  • 添加 JDM 分区:<jdbmPartition id="packt" suffix="o=packt"/>

  • 您可以重新启动 Apache DS 服务器以查看更改。然后使用 Apache 目录工作室连接到 Apache DS。右键单击DIT。从Scratch创建Entry。选择Organization,选择o,在Value中输入packt。选择Finish并刷新DIT以查看更新。

  1. 配置 Apache 目录工作室。

  2. 连接到 Apache 目录服务器。

  3. Apache DS 运行在 10389 端口。

  4. 创建两个组ou=groupsou=user如何做...如何做...如何做...

  5. 在这里,对象类是用于向ou=groups添加条目,因为这维护了角色:如何做...

  6. 在这里,对象类是为了向ou=people添加条目:如何做...

  7. 通过向cn=admin添加UniqueMember为用户分配角色。

Spring-security-ldap.xml

<beans:beans 

   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-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">
        <!-- AspectJ pointcut expression that locates our "post" method and applies security that way
        <protect-pointcut expression="execution(* bigbank.*Service.post*(..))" access="ROLE_TELLER"/>
        -->
    </global-method-security>
   <http auto-config="true" use-expressions="true" >
          <intercept-url pattern="/welcome" access="hasRole('ROLE_ADMIN')"/>
<!--            <intercept-url pattern="/admin" access="hasRole('ROLE_admin')"/> -->

         <form-login login-page="/login" default-target-url="/secure/common.jsp" authentication-failure-url="/loginfailed?error=true" />

<authentication-manager>
           <ldap-authentication-provider 
                            user-search-filter="(mail={0})" 
                            user-search-base="ou=people"
                            group-search-filter="(uniqueMember={0})"
                      group-search-base="ou=groups"
                      group-role-attribute="cn"
                      role-prefix="ROLE_">
           </ldap-authentication-provider>
   </authentication-manager>

   <ldap-server url="ldap://localhost:10389/o=sevenSeas" manager-dn="uid=admin,ou=system" manager-password="secret" />
</beans:beans>

它是如何工作的...

Spring Security-ldap.xml将包含有关服务器位置和域的详细信息。它应该连接以检索用户信息。域是sevenSeas。1039 是 LDAP 服务器的端口号。Spring Security 使用ldap-server标签提供 LDAP 信息。它还提供密码和将连接的域。Struts 2 请求将被 Spring Security 中断,并且对于身份验证,将从登录页面接收用户信息。Spring Security 需要 LDAP 来获取用户名;成功后,用户将获得对受保护资源的访问权限。

另请参阅

  • 第三章, 使用 JSF 的 Spring 安全性

第三章:JSF 的 Spring Security

在本章中,我们将涵盖:

  • 将 JSF 与 Spring Security 集成

  • JSF 与基于表单的 Spring Security

  • 使用 JSF 和基于表单的 Spring Security 进行身份验证以显示已登录用户

  • 使用 JSF 与基于摘要/哈希的 Spring Security

  • 使用 Spring Security 在 JSF 中注销

  • 使用 Spring Security 和 JSF 进行身份验证

  • 使用 JSF 和 Spring Security 进行 ApacheDS 身份验证

  • JSF 和 Spring Security 的身份验证错误消息

介绍

有许多在 Apache Faces/JSF 中开发的应用程序。它不像 Struts 2 那样是一个面向动作的框架,而纯粹是为了视图层。要在 JSF 中实现 Spring Security,我们需要找出一些解决方法。让我们看看关于 JSF 和 Spring Security 的一些配方。

我使用了最新的稳定版本的 Spring Security 和 Spring-core。如果您想更新您的库,可以阅读以下部分。对于 Maven 用户,这一切都是关于更新依赖项,对于普通的 Eclipse 用户,这是将.jar文件添加到lib文件夹。

在 Eclipse 上设置 JSF 应用程序

  1. 使用 Eclipse Java EE 开发人员工具并设置一个动态 Web 项目。

  2. 给项目命名:JSf_Spring_Security_Chapter_3_Recipe1

  3. 选择动态 Web 模块版本 2.5。

  4. 配置:JavaServer Faces v1.2 项目。

  5. 在下一个新动态 Web 项目窗口中,单击下载库

  6. 选择 Apache MyFaces 库。

Spring Security MAJOR/MINOR/PATCH 版本

当我为我的应用程序设置安全性时,我遇到了很多与模式版本相关的错误。

Spring 源提供了关于要下载哪个版本的很好描述。它建议使用 PATCH 版本是最安全的,不会影响现有代码,因为它将使用向后兼容性。MINOR 版本带有设计更改,MAJOR 版本带有主要 API 更改。对于 JSF 配方,我一直在使用 3.1.4 安全版本,并且已经下载了与 Spring-3.1.4 相关的 JAR 文件。

您可以下载 spring-security-3.1.4.RELEASE-dist,其中包含所有最新的 JAR 文件。

JAR 文件:

  • spring-security-config执行命名空间解析,并将读取spring-security.xml文件

  • Spring Security web 与 Web 应用程序过滤器进行交互

  • Spring Security 核心

将这些 JAR 文件保存在您的 Web 应用程序的WEB-INF/lib文件夹中。

将 JSF 与 Spring Security 集成

让我们在 Eclipse 中创建一个简单的 Apache MyFaces 应用程序。还让我们将 Spring Security 集成到 JSF 中,然后演示基本身份验证。

准备工作

  • 您将需要 Eclipse Indigo 或更高版本

  • 创建一个动态 Web 项目 JSF

  • 在您的 Eclipse IDE 中,创建一个动态 Web 项目:JSf_Spring_Security_Chapter_3_Recipe1

  • 创建一个源文件夹:src/main/java

  • 创建一个包:com.packt.jsf.bean

  • 创建一个托管 Bean:User.java

  • 使用 Tomcat 服务器部署应用程序

如何做...

执行以下步骤来实现 JSF 和 Spring Security 的基本身份验证机制:

  1. User.java是应用程序的托管 Bean。它有两个方法:sayHello()reset()

User.java 类

package com.packt.jsf.bean;
public class User {
   private String name;
   private boolean flag= true; 
   public String getName() {
         return this.name;
   }
   public void setName(String name) {
         this.name = name;
   }
    public String  sayHello(){
          flag= false;
          name="Hello "+ name;
         return this.name;

    }
    public String  reset(){
          flag= true;
          name=null;
         return "reset";

    }
   public boolean isFlag() {
         return flag;
   }

   public void setFlag(boolean flag) {
         this.flag = flag;
   }
}
  1. 让我们创建一个基于ApacheMyFaces标签的 JSP 文件。它期望一个强制的<f:view>标签。按照惯例,创建一个与其 bean 名称相同的 JSP 文件。它有一个表单,接受名称,并在单击按钮时显示“你好”

User.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="f"  uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h"  uri="http://java.sun.com/jsf/html"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>User</title>
</head>
<body>
<f:view>
  <h:form>
    <h:panelGrid columns="2">
      <h:outputLabel value="Name"></h:outputLabel>
      <h:inputText  value="#{user.name}"></h:inputText>
    </h:panelGrid>
    <h:commandButton action="#{user.sayHello}" value="sayHello"></h:commandButton>
    <h:commandButton action="#{user.reset}" value="Reset"></h:commandButton>
     <h:messages layout="table"></h:messages>
  </h:form>

  <h:panelGroup rendered="#{user.flag!=true}">
  <h3> Result </h3>
  <h:outputLabel value="Welcome "></h:outputLabel>
  <h:outputLabel value="#{user.name}"></h:outputLabel>
  </h:panelGroup>
</f:view>
</body>
</html>
  1. 使用托管 Bean 更新faces-config.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
    version="1.2">
    <application>

         <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>   
           <!-- 
           <variable-resolver>org.springframework.web.jsf.SpringBeanVariableResolver</variable-resolver>
           -->
   </application>
   <managed-bean>
          <managed-bean-name>user</managed-bean-name>
          <managed-bean-class>com.packt.jsf.bean.User</managed-bean-class>
          <managed-bean-scope>session</managed-bean-scope>
   </managed-bean>

</faces-config>
  1. Spring-security.xml文件保持不变,但我使用了最新的 jar- 3.1.4 安全 jar:
<beans:beans    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-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">

    </global-method-security>
    <http auto-config="true" use-expressions="true" >
          <intercept-url pattern="/faces/User.jsp" access="hasRole('ROLE_DIRECTOR')"/>
          <http-basic />
    </http>
    <authentication-manager>
      <authentication-provider>
        <user-service>
          <user name="packt" password="123456" authorities="ROLE_DIRECTOR" />
        </user-service>
      </authentication-provider>
    </authentication-manager>
</beans:beans>
  1. web.xml文件应更新 Spring 过滤器和监听器。它还具有 MyFaces 的配置:

Spring-security.xml

<?xml version="1.0" encoding="UTF-8"?><web-app    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>JSf_Spring_Security_Chapter_3_Recipe1</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
          /WEB-INF/spring-security.xml

          </param-value>
  </context-param>
 <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.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>
  <context-param>
    <param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
    <param-value>resources.application</param-value>
  </context-param>
  <context-param>
    <description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>
  <context-param>
    <description>
   This parameter tells MyFaces if javascript code should be allowed in
   the rendered HTML output.
   If javascript is allowed, command_link anchors will have javascript code
   that submits the corresponding form.
   If javascript is not allowed, the state saving info and nested parameters
   will be added as url parameters.
   Default is 'true'</description>
    <param-name>org.apache.myfaces.ALLOW_JAVASCRIPT</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <description>
   If true, rendered HTML code will be formatted, so that it is 'human-readable'
   i.e. additional line separators and whitespace will be written, that do not
   influence the HTML code.
   Default is 'true'</description>
    <param-name>org.apache.myfaces.PRETTY_HTML</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.DETECT_JAVASCRIPT</param-name>
    <param-value>false</param-value>
  </context-param>
  <context-param>
    <description>
   If true, a javascript function will be rendered that is able to restore the
   former vertical scroll on every request. Convenient feature if you have pages
   with long lists and you do not want the browser page to always jump to the top
   if you trigger a link or button action that stays on the same page.
   Default is 'false'
</description>
    <param-name>org.apache.myfaces.AUTO_SCROLL</param-name>
    <param-value>true</param-value>
  </context-param>
  <listener>
    <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
  </listener>
</web-app>:beans>

它是如何工作的...

当用户尝试访问受保护的user.jsp页面时,Spring Security 会拦截 URL 并将用户重定向到登录页面。成功身份验证后,用户将被重定向到spring-security.xml文件中提到的成功url。以下屏幕截图显示了使用 JSF 和 Spring Security 实现基本身份验证的工作流程。

现在访问以下 URL:http://localhost:8086/JSf_Spring_Security_Chapter_3_Recipe1/faces/User.jsp

您应该看到一个基本的身份验证对话框,要求您如下登录:

它是如何工作的...

以下屏幕截图是 JSF 的安全页面,可以在成功身份验证后访问:

它是如何工作的...

另请参阅

  • 使用基于表单的 Spring Security 的 JSF

  • 使用 Spring Security 显示已登录用户的JSF 和基于表单的身份验证食谱

  • 使用基于摘要/哈希的 Spring Security 的使用 JSF食谱

  • 使用 Spring Security 注销 JSF 的Logging out with JSF using Spring Security食谱

  • 使用 Spring Security 和 JSF 进行数据库身份验证的身份验证数据库食谱

  • 使用 JSF 和 Spring Security 进行 ApacheDS 身份验证的ApacheDS 身份验证食谱

  • 使用 JSF 和 Spring Security 的身份验证错误消息食谱

使用基于表单的 Spring Security 的 JSF

在本节中,我们将使用 JSF 和 Spring Security 实现基于表单的身份验证。将 Apache MyFaces 与 Spring Security 集成并不像 Struts 2 集成那样简单。

它需要一个解决方法。ApacheMyfaces 无法理解/j_spring_security方法。解决方法是在我们的 Managed Bean 类中创建一个自定义登录方法。我们将使用 JSF 外部上下文类将认证请求传递给 Spring Security 框架。

准备工作

  • 在 Eclipse IDE 中创建一个新项目:JSF_Spring_Security_Chapter_3_Recipe2

  • 按照以下屏幕截图中显示的配置进行配置

  • 创建一个包:com.packt.jsf.beans

如何做...

执行以下步骤将 JSF 与 Spring Security 集成以实现基于表单的身份验证:

  1. 在 Eclipse 中创建一个 Web 项目:如何做...

  2. 创建一个 Credential Manager Bean:

此 bean 具有基于表单的身份验证 bean 的所有属性和自定义登录方法();

将设置j_usernamej_password值,并在安全页面中显示用户。

doSpringSecurityLogin() bean:就像我们访问ServletContext并将其与请求分派器绑定一样,我们可以使用ExternalContext和请求分派器来执行/j_spring_security_check

phaseListener实现旨在捕获身份验证异常。

CredentialManager.java

public class CredentialManager implements PhaseListener{
   private String j_username;
   private String j_password;

    public String getJ_password() {
         return j_password;
   }
   public void setJ_password(String j_password) {
         this.j_password = j_password;
   }
   public String doSpringSecurityLogin() throws IOException, ServletException
       {
           ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
           RequestDispatcher dispatcher = ((ServletRequest) context.getRequest()).getRequestDispatcher("/j_spring_security_check");
           dispatcher.forward((ServletRequest) context.getRequest(),(ServletResponse) context.getResponse());
           FacesContext.getCurrentInstance().responseComplete();
           return null;
       }
   public String getJ_username() {
         return j_username;
   }
   public void setJ_username(String j_username) {
         this.j_username = j_username;
   }
   @Override
   public void afterPhase(PhaseEvent arg0) {
         // TODO Auto-generated method stub

   }
   @Override
   public void beforePhase(PhaseEvent event) {
         Exception e = (Exception) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(
          WebAttributes.AUTHENTICATION_EXCEPTION);

          if (e instanceof BadCredentialsException) {
              System.out.println("error block"+e);
               FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
                   WebAttributes.AUTHENTICATION_EXCEPTION, null);
               FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Username or password not valid.", "Username or password not valid"));
           }
   }

   @Override
   public PhaseId getPhaseId() {
          return PhaseId.RENDER_RESPONSE;
   }
}
  1. 让我们更新Spring-security.xml文件。login-processing-url映射到j_security_check
<beans:beans    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">

    </global-method-security>
   <http auto-config="true" use-expressions="true" >

          <intercept-url pattern="/faces/Supplier.jsp" access="hasRole('ROLE_USER')"/>  

         <form-login login-processing-url="/j_spring_security_check" login-page="/faces/login.jsp" default-target-url="/faces/Supplier.jsp" authentication-failure-url="/faces/login.jsp" />
         <logout/>
   </http>

   <authentication-manager>
     <authentication-provider>
       <user-service>
         <user name="anjana" password="anju123456" authorities="ROLE_USER"/>
       </user-service>
     </authentication-provider>
   </authentication-manager>
</beans: beans>
  1. 将 Managed Bean 添加到faces-config.xml文件中:
<?xml version="1.0" encoding="UTF-8"?>

<faces-config

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
    version="1.2">
    <lifecycle>
         <phase-listener>com.packt.jsf.beans.CredentialManager</phase-listener>
   </lifecycle>
    <application>

          <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>	
          <!-- 
          <variable-resolver>org.springframework.web.jsf.SpringBeanVariableResolver</variable-resolver>
           -->
   </application>

         <managed-bean>
         <managed-bean-name>credentialmanager</managed-bean-name>
         <managed-bean-class>com.packt.jsf.beans.CredentialManager</managed-bean-class>
         <managed-bean-scope>session</managed-bean-scope>
   </managed-bean>

</faces-config>
  1. 现在是 Apache MyFaces 的login.jsp文件。

login.jsp文件应该包含以下内容:

prependID=false

它应该提交到ManagedBean中定义的自定义登录方法

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring Security Login</title>
</head>
<body>
<f:view>
<h:form prependId="false">
<h:panelGrid columns="2">
<h:outputLabel value="j_username"></h:outputLabel>
<h:inputText   id="j_username" required="true" value="#{credentialmanager.j_username}"></h:inputText>
<h:outputLabel value="j_password"></h:outputLabel>
<h:inputSecret  id ="j_password" required="true" value="#{credentialmanager.j_password}"></h:inputSecret>
</h:panelGrid>
<h:commandButton action="#{credentialmanager.doSpringSecurityLogin}" value="SpringSecurityLogin"/>
 </h:form>
</f:view>
</body>
</html>

它是如何工作的...

访问以下 URL:localhost:8086/JSF_Spring_Security_Chapter_3_Recipe2/faces/Supplier.jsp

当用户访问 URL 时,他们将被重定向到登录页面。然后用户输入其凭据并单击提交。使用FacesContext对象使用PhaseListener实现来实例化ExternalContext对象。将context对象传递给请求对象,其中包含'j_spring_security_check' URL。Spring Security 将进行身份验证和授权。身份验证失败时,将抛出异常。

另请参阅

  • 使用 Spring Security 显示已登录用户的JSF 和基于表单的身份验证食谱

  • 使用基于摘要/哈希的 Spring Security 的使用 JSF食谱

  • 使用 Spring Security 注销 JSF 的Logging out with JSF using Spring Security食谱

  • 使用 Spring Security 进行数据库身份验证的身份验证数据库食谱

  • 使用 JSF 和 Spring Security 进行 ApacheDS 身份验证的ApacheDS 身份验证食谱

  • 使用 JSF 和 Spring Security 进行身份验证错误消息配方

使用 Spring Security 和 JSF 进行基于表单的认证以显示已登录用户

在上一个配方中,我们演示了使用 Spring Security 和 JSF phaseListener实现基于表单的认证。在本节中,我们将显示已登录的用户。

准备工作

您必须在Supplier.jsp文件中进行一些小的更改。

如何做...

执行以下步骤在浏览器上显示已登录用户的详细信息:

  1. 要显示已登录的用户,请访问受保护页面中的托管 bean 对象。

  2. Supplier.jsp文件中,编辑以下内容:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<f:view>
<h:panelGroup>
  <h3> Result </h3>
  <h:outputLabel value="Welcome "></h:outputLabel>
  <h:outputLabel value="#{credentialmanager.j_username}"></h:outputLabel>
  </h:panelGroup>
</f:view>
</body>
</html>

它是如何工作的...

当用户被重定向到登录页面时,faces 上下文对象将用户信息提交给 Spring Security。成功后,用户 POJO 的 getter 和 setter 设置用户信息,用于在 JSP 页面上显示用户信息。

以下截图显示了使用 JSF 和 Spring Security 进行基于表单的认证,在浏览器中显示用户信息的工作流程:

它是如何工作的...

成功认证后,用户将被引导到以下页面:

它是如何工作的...

另请参阅

  • 使用基于摘要/哈希的 Spring Security 的 JSF配方

  • 使用 JSF 和 Spring Security 进行注销配方

  • 使用 Spring Security 和 JSF 进行身份验证数据库配方

  • 使用 JSF 和 Spring Security 进行 ApacheDS 认证配方

  • 使用 JSF 和 Spring Security 进行身份验证错误消息配方

使用基于摘要/哈希的 Spring Security 进行 JSF

在本节中,我们将使用 JSF 和 Spring Security 实现摘要认证。用户的密码使用其中一种加密算法进行哈希处理,并在.xml文件中进行配置。用于哈希密码的算法也在配置文件中提到。

准备工作

Spring 摘要认证在 JSF 中也可以正常工作。我们需要使用jacksum.jar对密码进行哈希处理。在配置文件中提供哈希密码。还在配置文件中提到用于哈希处理的算法。

如何做...

执行以下步骤来实现 JSF 和 Spring Security 的摘要认证机制:

  1. 让我们加密密码:packt123456

  2. 我们需要使用一个外部的 jar 包,Jacksum,这意味着 Java 校验和。

  3. 它支持 MD5 和 SHA1 加密。

  4. 下载jacksum.zip文件并解压缩 ZIP 文件夹。

packt>java -jar jacksum.jar -a sha -q"txt:packt123456"

如何做...

  1. 让我们创建一个新项目来演示这一点,我们将使用基本认证。在 Eclipse 中创建一个动态 Web 项目,并将其命名为JSF_Spring_Security_DIGEST_Recipe3

  2. web.xmlface-config.xml和 JSP 设置与JSF_Spring_Security_Chapter3_Recipe1相同。我们需要更新Spring-security.xml文件以使用 SHA 加密和解密进行认证:

Spring-security.xml

<beans:beans    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">

    </global-method-security>
   <http auto-config="true" use-expressions="true" >
         <intercept-url pattern="/faces/User.jsp" access="hasRole('ROLE_DIRECTOR')"/>
         <http-basic />
   </http>

   <authentication-manager>
      <authentication-provider>
 <password-encoder hash="sha" />
 <user-service>
 <user name="anjana" password="bde892ed4e131546a2f9997cc94d31e2c8f18b2a" 
 authorities="ROLE_DIRECTOR" />
 </user-service>
 </authentication-provider>
 </authentication-manager>
</beans:beans>

它是如何工作的...

当您运行应用程序时,将提示您输入对话框。

输入用户名和密码后,Spring 框架将解密密码并将其与用户输入的详细信息进行比较。当它们匹配时,它会标记一个认证成功的消息,这将使上下文对象将用户重定向到成功的 URL。

以下截图显示了 JSF 和 Spring 进行摘要认证的工作流程。

这是一个基本的表单,但认证机制是摘要的。

Spring 通过解密密码对用户进行了身份验证:

它是如何工作的...它是如何工作的...

另请参阅

  • 使用 JSF 和 Spring Security 进行注销配方

  • 使用 Spring Security 和 JSF 进行身份验证数据库配方

  • 使用 JSF 和 Spring Security 进行 ApacheDS 认证配方

  • 使用 JSF 和 Spring Security 进行身份验证错误消息配方

使用 JSF 和 Spring Security 进行注销

在本节中,我们将使用 Spring Security 在 JSF 应用程序中实现注销场景。

准备工作

  • 实现PhaseListener

  • 在 JSF 页面上添加一个commandButton

如何做...

执行以下步骤来实现 JSF 应用程序中的 Spring Security 注销:

  1. 在 Eclipse 中创建一个新的动态 Web 项目如何做...

  2. 我们将再次创建一个CredentialManager bean。它将有另一个自定义的注销方法。 Login.jsp与上一个示例相同。不要忘记将其复制到新项目中。我们将在这里使用基于表单的身份验证:

package com.packt.jsf.beans;

import java.io.IOException;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import org.springframework.security.authentication.BadCredentialsException;
import javax.faces.application.FacesMessage;

import org.springframework.security.web.WebAttributes;

public class CredentialManager implements PhaseListener{
   /**
    * 
    */
   private static final long serialVersionUID = 1L;
   private String j_username;
   private String j_password;

    public String getJ_password() {
         return j_password;
   }
   public void setJ_password(String j_password) {
         this.j_password = j_password;
   }
   public String doSpringSecurityLogin() throws IOException, ServletException
       {
           ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
           RequestDispatcher dispatcher = ((ServletRequest) context.getRequest()).getRequestDispatcher("/j_spring_security_check");
           dispatcher.forward((ServletRequest) context.getRequest(),(ServletResponse) context.getResponse());
           FacesContext.getCurrentInstance().responseComplete();
           return null;
       }
   public String doSpringSecurityLogout() throws IOException, ServletException
 {
 ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
 RequestDispatcher dispatcher = ((ServletRequest) context.getRequest()).getRequestDispatcher("/j_spring_security_logout");
 dispatcher.forward((ServletRequest) context.getRequest(),(ServletResponse) context.getResponse());
 FacesContext.getCurrentInstance().responseComplete();
 return null;
 }
   public String getJ_username() {
         return j_username;
   }
   public void setJ_username(String j_username) {
         this.j_username = j_username;
   }
   public void afterPhase(PhaseEvent arg0) {
         // TODO Auto-generated method stub

   }
   public void beforePhase(PhaseEvent arg0) {
         Exception e = (Exception) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(
            WebAttributes.AUTHENTICATION_EXCEPTION);

          if (e instanceof BadCredentialsException) {
              System.out.println("error block"+e);
               FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
                   WebAttributes.AUTHENTICATION_EXCEPTION, null);
               FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Username or password not valid.", "Username or password not valid"));
           }
   }
   public PhaseId getPhaseId() {
          return PhaseId.RENDER_RESPONSE;
   }

}
  1. 让我们在我们的安全页面上提供一个注销按钮:

Supplier.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="f"  uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h"  uri="http://java.sun.com/jsf/html"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<f:view>
  <h:form prependId="false">
<h:panelGroup>
  <h:outputLabel value="Welcome "></h:outputLabel>
  <h:outputLabel value="#{credentialmanager.j_username}"></h:outputLabel>
  </h:panelGroup>

 <h:commandButton action="#{credentialmanager.doSpringSecurityLogout}" value="SpringSecurityLogout" />
  </h:form>
</f:view>
</body>
</html>
  1. 更新Spring-security.xml文件:
<beans:beans    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
 http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">

    </global-method-security>
   <http auto-config="true" use-expressions="true" >

          <intercept-url pattern="/faces/Supplier.jsp" access="hasRole('ROLE_USER')"/>  
         <form-login login-processing-url="/j_spring_security_check" login-page="/faces/login.jsp" default-target-url="/faces/Supplier.jsp" authentication-failure-url="/faces/login.jsp" />
         <logout  logout-success-url="/faces/login.jsp" />
   </http>

   <authentication-manager>
     <authentication-provider>
       <user-service>
         <user name="anjana" password="123456" authorities="ROLE_USER"/>
       </user-service>
     </authentication-provider>
   </authentication-manager>
</beans:beans>

它是如何工作的...

CredentialManager类实现了phaseListener接口。doSpringSecurityLogout方法通过使用ExternalContext创建一个上下文对象来处理 Spring 注销。然后,上下文提交注销请求,即"/j_spring_security_logout"到 Spring Security 框架,该框架注销用户。

它是如何工作的...

单击注销后,用户将被重定向到登录页面。

另请参阅

  • 使用 Spring Security 和 JSF 进行数据库认证食谱

  • 使用 JSF 和 Spring Security 进行 ApacheDS 身份验证食谱

  • 使用 JSF 和 Spring Security 进行身份验证错误消息食谱

使用 Spring Security 和 JSF 进行数据库认证

在本节中,我们将使用数据库来验证 JSF 应用程序中的用户身份验证。我们已经参考了注销示例,并且已经使用数据库进行了身份验证。

准备工作

  • 在 Eclipse 中创建一个动态 Web 项目:JSF_Spring_DBAuthentication_Recipe6

  • 所有文件和文件夹与注销应用程序相同

  • 更新security.xml文件和web.xml文件

  • 将以下 JAR 文件添加到lib文件夹中,或者如果您使用 Maven,则更新您的 POM 文件:

  • spring-jdbc-3.1.4RELEASE

  • mysql-connector-java-5.1.17-bin

  • commons-dbcp

  • commons-pool-1.5.4

如何做...

以下步骤将帮助我们通过从数据库中检索数据来验证用户信息:

  1. 更新Spring-security.xml文件以读取数据库配置:

applicationContext-security.xml

<beans: beans    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">

    </global-method-security>
   <http auto-config="true" use-expressions="true" >

           <intercept-url pattern="/faces/Supplier.jsp" access="hasRole('ROLE_USER')"/>  
         <form-login login-processing-url="/j_spring_security_check" login-page="/faces/login.jsp" default-target-url="/faces/Supplier.jsp" authentication-failure-url="/faces/login.jsp" />
         <logout  logout-success-url="/faces/login.jsp" />

   </http>

   <authentication-manager> 
      <authentication-provider> 
          <jdbc-user-service data-source-ref="MySqlDS" 
            users-by-username-query=" 
              select username,password, enabled   
              from users1 where username=?"  

            authorities-by-username-query=" 
               select u.username, ur.role from users1 u, user_roles ur  
         where u.user_id = ur.user_id and u.username =?  " /> 
      </authentication-provider>
         </authentication-manager> 
</beans: beans>

它是如何工作的...

数据源引用在Sping-security.xml文件中给出。当用户点击登录时,Spring Security 过滤器将调用与数据库身份验证相关的类,这些类将读取db-beans.xml文件以建立连接。<jdbc-user-service>标签通过执行查询并根据用户在浏览器中提交的参数从数据库中检索用户信息来实现数据库身份验证。

另请参阅

  • 使用 JSF 和 Spring Security 进行 ApacheDS 身份验证食谱

  • 使用 JSF 和 Spring Security 进行身份验证错误消息食谱

使用 JSF 和 Spring Security 进行 ApacheDS 身份验证

在本节中,我们将使用 ApacheDS 和 Spring Security 在 JSF 应用程序中对用户进行身份验证。

准备工作

ApacheDS 身份验证类似于 Struts 2 ApacheDS 身份验证:

  • 在 Eclipse 中创建一个动态 Web 项目:JSF_Spring_ApacheDSAuthentication_Recipe7

  • 所有文件和文件夹与注销应用程序相同

  • 更新security.xml文件

  • spring-security-ldap.jar添加到您的web-inf/lib文件夹

如何做...

执行以下步骤来配置 Spring 和 JSF 应用程序的 LDAP:

  1. 更新Spring-security.xml文件以读取 LDAP 配置:
<beans:beans    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.1.xsd">

 <global-method-security pre-post-annotations="enabled">

    </global-method-security>
   <http auto-config="true" use-expressions="true" >

           <intercept-url pattern="/faces/Supplier.jsp" access="hasRole('ROLE_USER')"/>  
         <form-login login-processing-url="/j_spring_security_check" login-page="/faces/login.jsp" default-target-url="/faces/Supplier.jsp" authentication-failure-url="/faces/login.jsp" />
         <logout  logout-success-url="/faces/login.jsp" />
               </http>
         <authentication-manager>
           <ldap-authentication-provider 
                            user-search-filter="(mail={0})" 
                            user-search-base="ou=people"
                            group-search-filter="(uniqueMember={0})"
                      group-search-base="ou=groups"
                      group-role-attribute="cn"
                      role-prefix="ROLE_">
           </ldap-authentication-provider>
   </authentication-manager>

   <ldap-server url="ldap://localhost:389/o=example" manager-dn="uid=admin,ou=system" manager-password="secret" /></beans:beans>

它是如何工作的...

JSF 过滤器用于委托。Spring 过滤器用于身份验证。我们使用 ldap-authentication-provider 来设置 LDAP 参数到 Spring Security 引擎。当应用程序收到身份验证和授权请求时,spring-security-ldap 提供程序设置 LDAP 参数并使用 ldap-server-url 参数连接到 LDAP。然后检索用户详细信息并将其提供给 Spring 身份验证管理器和过滤器来处理身份验证的响应。

另请参阅

  • JSF 和 Spring Security 的身份验证错误消息配方

JSF 和 Spring Security 的身份验证错误消息

在本节中,我们将看到如何捕获身份验证错误消息并在浏览器上向用户显示。如前面的示例中所示的credentialmanager bean 将捕获身份验证失败的异常。我们将看到如何在 JSP 中捕获它。

准备工作

credentialmanager bean 已捕获了错误凭据异常。

我们需要将其显示给用户。这可以通过在我们的 JSP 文件中使用<h: messages>标签来实现。这应该放在 grid 标记内。在托管 bean 中实现phaselistener的目的是捕获消息并将其显示给用户。这是更新后的login.jsp

如何做...

执行以下步骤来捕获 JSP 中的身份验证失败消息:

  • 编辑login.jsp文件:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="f"  uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h"  uri="http://java.sun.com/jsf/html"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<f:view>
<h:form prependId="false">
                <h:panelGrid columns="2">

                 <h:outputLabel value="j_username"></h:outputLabel>
            <h:inputText    id="j_username" required="true" value="#{credentialmanager.j_username}"></h:inputText>
               <h:outputLabel value="j_password"></h:outputLabel>
            <h:inputSecret    id ="j_password" required="true" value="#{credentialmanager.j_password}"></h:inputSecret>
             <h:outputLabel value="_spring_security_remember_me"></h:outputLabel>
               <h:selectBooleanCheckbox
                      id="_spring_security_remember_me" />

              </h:panelGrid>
              <h:commandButton action="#{credentialmanager.doSpringSecurityLogin}" value="SpringSecurityLogin" />
 <h:messages />

         </h:form>
         </f:view>
</body>
</html>

它是如何工作的...

credentialmanager中的beforePhase()方法捕获了身份验证异常消息。异常被添加到FacesMessage,在 JSP 文件中捕获。

FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Username or password not valid.", "Username or password not valid"));

以下截图显示了实现:

它是如何工作的...

以下截图显示了身份验证失败时的屏幕:

它是如何工作的...

以下截图显示了当在用户名和密码字段中输入空凭据时的屏幕:

它是如何工作的...

另请参阅

  • 第四章, 与 Grails 一起使用 Spring Security

第四章:Spring Security with Grails

在本章中,我们将涵盖:

  • 使用 Groovy Grails 设置 Spring Security 身份验证

  • 使用 Grails 保护 Grails 控制器的 Spring Security

  • 使用 Groovy Grails 注销场景的 Spring Security

  • 使用 Groovy Grails 基本身份验证的 Spring Security

  • 使用 Groovy Grails 摘要身份验证的 Spring Security

  • Spring Security with Groovy Grails 多重身份验证

  • 使用 Groovy Grails LDAP 身份验证的 Spring Security

介绍

Grails 是一个基于插件的框架,它只需要在命令提示符上输入一些直观的命令即可工作。

在本章中,我们将看到如何轻松地将 Spring Security 与 Groovy on Grails 集成,编码量较少。

使用 Groovy Grails 设置 Spring Security 身份验证

在这个食谱中,我们首先将设置 Groovy 和 Grails。然后我们将展示如何将 Spring Security 与 Grails 集成。

准备工作

如何做...

以下步骤用于将 Spring Security 与 Groovy Grails 集成:

  1. 创建一个目录:Grails 项目
cd Grails_Project
grails create-app myfirstapp
cd myfirstapp
grails create-controller MyFirstController

这将创建一个控制器,该控制器将在控制器包内可用。

  1. 您可以打开生成的控制器文件并查看它。它将具有 Grails 自动生成的包名称myfirstapp
package myfirstapp
class MyFirstController {
    def index() { }
}
  1. 更新生成的控制器文件。
package myfirstapp
class MyFirstController {
  def index() { 
    render "Hello PACKT"
  }
}
  1. 通过访问此 URLhttp://localhost:8080/myfirstapp/来测试 Grails 设置。
cd myfirstapp

  1. 为 Grails 下载安全 jar 文件。
grails install-plugin spring-security-core
grails  s2-quickstart org.packt SecuredUser SecuredRole

如果安装程序不支持您的 Grails 版本,您可以向BuildConfig.groovy文件添加依赖项:

plugins {

    compile ':spring-security-core:2.0-RC2'

}
  1. 更新Bootstrap.groovy文件:
import org.packt.SecuredUser;
import org.packt.SecuredRole;
import org.packt.SecuredUserSecuredRole
class BootStrap {

  def springSecurityService

    def init = { servletContext ->

    if(!SecuredUser.count()){
      /*The default password is 'password'*/
      def password = 'password'
      def user = new SecuredUser(username : 'anjana', password:'anjana123',enabled:true,accountExpired : false , accountLocked : false ,passwordExpired : false).save(flush: true, insert: true)
      def role = new SecuredUser(authority : 'ROLE_USER').save(flush: true, insert: true)
      /*create the first user role map*/
      SecuredUserSecuredRole.create user , role , true
    }

    }
    def destroy = {
    }
}

在前面的文件中,我们已经用用户名anjana和密码anjana123填充了用户。

只需这样做,我们就可以验证用户。

您可以看到我们没有更新任何 XML 文件。我们只是安装了插件并修改了文件。

工作原理...

让我们看看运行 Grails 时会得到什么样的输出:grails run-app

工作原理...

更新i18n文件夹中的Messages.properties文件:

springSecurity.login.header=Spring Security login
springSecurity.login.username.label=UserName
springSecurity.login.password.label=Password
springSecurity.login.remember.me.label=remember me
springSecurity.login.button=Login
springSecurity.errors.login.fail=Authentication failed

单击http://localhost:8080/myfirstapp/login/auth上的LoginController链接。

您应该能够看到登录屏幕,该屏幕是在安装安全插件时由 Grails 框架生成的。页面位于视图文件夹中。现在您可以使用用户名和密码anjanaanjana123登录。您将被重定向到 Grails 主页。身份验证失败时,您将收到身份验证失败消息。

当您单击LogoutController链接时,您将注销。当您再次单击控制器时,将要求您重新登录。

以下是应用程序的工作流程:

这是 Grails 登录屏幕——单击登录按钮,在输入用户名和密码后,将提交凭据到 Spring Security 框架:

工作原理...

在身份验证失败时,用户将被重定向到登录屏幕,并显示身份验证失败消息。

工作原理...

另请参阅

  • Spring Security with Grails 保护 Grails 控制器食谱

  • Spring Security with Groovy Grails 注销场景食谱

  • Spring Security with Groovy Grails 基本身份验证食谱

  • Spring Security with Groovy Grails 摘要身份验证食谱

  • Spring Security with Groovy Grails 多级身份验证食谱

  • Spring Security with Groovy Grails LDAP 身份验证食谱

使用 Grails 的 Spring Security 来保护 Grails 控制器

让我们将 Spring Security 应用于 Grails 控制器。场景是用户将访问 Grails 应用程序,并将提供一个登录链接。成功验证后,用户将提供可以访问的链接。这些链接只对已登录用户可见。

准备工作

为了演示目的,我们将创建以下内容:

  • 一个简单的 Grails 控制器:myfirstapp

  • 一个将使用 Spring Security 保护的MyFirstController控制器

  • 修改index.gsp

操作步骤...

以下步骤用于将 Spring Security 与 Grails 集成以保护 Grails 控制器:

  1. 转到myfirstapp\grails-app\views

  2. 您将看到index.gsp文件,将其重命名为index.gsp_backup。我已经从index.gsp_backup中复制了样式。

  3. 创建一个新的index.gsp文件,编辑文件如下:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <h1>Welcome to Grails</h1>
    <sec:ifLoggedIn>Access the <g:link controller='myFirst' action=''>Secured Controller</g:link><br/>
        <g:link controller='logout' action=''>Spring Logout</g:link>
    </sec:ifLoggedIn>

    <sec:ifNotLoggedIn>
    <h2>You are seeing a common page.You can click on login.After login success you will be provided with the links which you can access.</h2>
    <g:link controller='login' action='auth'>Spring Login</g:link>
    </sec:ifNotLoggedIn>

  </body>
</html>

工作原理...

访问 URL:http://localhost:8080/myfirstapp/

工作原理...

现在单击Spring 登录链接,您将被重定向到登录页面。Spring Security 处理身份验证机制,在成功登录后,用户将提供一个链接以访问受保护的控制器。

工作原理...

链接在index.gsp页面中提供,根据登录或注销状态显示和隐藏链接。这是使用index.gsp页面中的安全标签提供的。

单击受保护的控制器链接。您应该能够在浏览器上看到受保护控制器的输出消息。

工作原理...

另请参阅

  • 使用 Groovy Grails 注销场景的 Spring Security配方

  • 使用 Groovy Grails 基本身份验证的 Spring Security配方

  • 使用 Groovy Grails 摘要身份验证的 Spring Security配方

  • 使用 Groovy Grails 多级身份验证的 Spring Security配方

  • 使用 Groovy Grails LDAP 身份验证的 Spring Security配方

Groovy Grails Spring Security 身份验证注销场景

在这个配方中,让我们看看在 Grails 应用程序中使用 Spring Security 的注销实现。

准备工作

当我们在 Grails 中安装 Spring Security 插件时,Login ControllerLogout Controller类将自动创建。Login Controller将处理身份验证。Logout Controller将处理注销过程,它将重定向用户到公共页面。

操作步骤...

以下步骤用于在 Groovy on Grails 应用程序中实现注销操作:

  1. index.jsp文件中,我们添加以下内容:
<g:link controller='logout' action=''>Spring Logout</g:link>
  1. Logout Controller类将请求重定向到j_spring_security
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

class LogoutController {

  /**
   * Index action. Redirects to the Spring security logout uri.
   */
  def index = {
    // TODO put any pre-logout code here
    redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j_spring_security_logout'
  }
}

工作原理...

单击注销链接。用户将被重定向到主页。SpringSecurityUtils.securityConfig.logout.filterProcessesUrl默认设置为/j_spring_security_logout。因此,当用户单击注销时,他们将被重定向到/j_spring_security_logout操作。这将注销用户并且用户必须再次登录到 Grails 应用程序。

另请参阅

  • 使用 Groovy Grails 基本身份验证的 Spring Security配方

  • 使用 Groovy Grails 摘要身份验证的 Spring Security配方

  • 使用 Groovy Grails 多级身份验证的 Spring Security配方

  • 使用 Groovy Grails LDAP 身份验证的 Spring Security配方

使用 Groovy Grails 基本身份验证的 Spring Security

在这个配方中,我们将演示使用基本身份验证机制在 Grails 上使用 Groovy 的安全性。

准备工作

  • 我们需要创建一个 Grails 应用程序:grailsbasicauthexample

  • 将 Spring Security 插件安装到新应用程序中

  • 创建UserRole

  • 编辑Config.groovy文件

  • 编辑BootStrap.groovy文件

  • 创建一个控制器:GreetingsController

操作步骤...

以下步骤用于演示在 Groovy on Grails 中使用 Spring Security 进行基本身份验证:

  1. 在命令提示符中运行以下命令:
  • Grails create-app grailsbasicauthexample

  • cd grailsbasicauthexample

  • grails install-plugin spring-security-core

  • grails s2-quickstart com.packt SecuredUser SecuredRole

  1. 编辑 config.groovy 文件并设置以下值:
grails.plugins.springsecurity.useBasicAuth = true
grails.plugins.springsecurity.basic.realmName = "HTTP Basic Auth Demo"
  1. 编辑 Bootstrap.groovy 文件:
import com.packt.*;
class BootStrap {
  def init = { servletContext ->
    def userRole = SecuredRole.findByAuthority("ROLE_USER") ?: new SecuredRole(authority: "ROLE_USER").save(flush: true)
    def user = SecuredUser.findByUsername("anjana") ?: new SecuredUser(username: "anjana", password: "anjana123", enabled: true).save(flush: true)
    SecuredUserSecuredRole.create(user, userRole, true)
  }
  def destroy = {
  }
}
  1. 运行命令 $grails create-controller Greetings 并添加注解:
package grailsbasicauthexample
import grails.plugins.springsecurity.Secured
class GreetingsController {
  @Secured(['ROLE_USER'])
  def index() { 
    render "Hello PACKT"
  }
}

它是如何工作的...

访问 URL:http://localhost:8080/grailsbasicauthexample/

点击 Greetings Controller 链接。这是一个受 Spring Security 限制的安全链接。当用户点击链接时,基本认证机制会触发一个登录对话框。用户必须输入用户名/密码:anjana/anjana123,然后进行身份验证,用户将被重定向到一个授权页面,也就是,您将会看到 Greetings Controller 链接。

它是如何工作的...

成功认证后,用户将获得对问候控制器的访问权限。

另请参阅

  • Spring Security with Groovy Grails Digest authentication 食谱

  • Spring Security with Groovy Grails multilevel authentication 食谱

  • Spring Security with Groovy Grails LDAP authentication 食谱

Spring Security with Groovy Grails Digest authentication

在这个食谱中,让我们看看摘要认证机制,其中密码将被哈希。让我们将其与 Grails 应用程序集成,并查看它如何进行身份验证和授权。

准备工作

  • 我们需要创建一个 Grails 应用程序:grailsdigestauthexample

  • 将 Spring Security 插件安装到新应用程序中

  • 创建 UserRole

  • 编辑 Config.groovy 文件

  • 编辑 BootStrap.groovy 文件

  • 创建一个控制器:SecuredPackt

如何做…

以下步骤用于演示使用 Spring Security 在 Grails 上进行摘要认证:

  1. 在命令提示符中运行以下命令:
$grails create-app grailsdigestauthexample
$cd grailsdigestauthexample
$grails install-plug-in spring-security-core
$grails s2-quickstart com.packt SecuredUser SecuredRole
$grails create-controller SecuredPackt

  1. 将以下内容添加到 config.groovy 文件并编辑 Bootstrap.groovy 文件:
import com.packt.*;
class BootStrap {
  def init = { servletContext ->
    def userRole = SecuredRole.findByAuthority("ROLE_USER") ?: new SecuredRole(authority: "ROLE_USER").save(flush: true)
    def user = SecuredUser.findByUsername("anjana") ?: new SecuredUser(username: "anjana", password: "anjana123", enabled: true).save(flush: true)
    SecuredUserSecuredRole.create(user, userRole, true)
  }
  def destroy = {
  }
}
  1. 编辑 SecuredPacktController 文件并添加注解:
package grailsdigestauthexample
import grails.plugins.springsecurity.Secured
class SecuredPacktController {
  @Secured(['ROLE_USER'])
  def index() { 
  render "Hello PACKT"
  }
}

Grails 与 Spring Security 插件需要传递用户名作为盐值。

我们需要对生成的 SecuredUser.groovy 文件进行一些调整。

  1. 更新 SecuredUser.groovy 文件,如下所示:
package com.packt
class SecuredUser {
 transient passwordEncoder

  String username
  String password
  boolean enabled
  boolean accountExpired
  boolean accountLocked
  boolean passwordExpired

  static constraints = {
    username blank: false, unique: true
    password blank: false
  }

  static mapping = {
    password column: '`password`'
  }

  Set<SecuredRole> getAuthorities() {
    SecuredUserSecuredRole.findAllBySecuredUser(this).collect { it.securedRole } as Set
  }

  def beforeInsert() {
    encodePassword()
  }

  def beforeUpdate() {
    if (isDirty('password')) {
      encodePassword()
    }
  }

  protected void encodePassword() {
    password = passwordEncoder.encodePassword(password,       username)
  }
}

显示已登录用户:

<!DOCTYPE html>
<html>
  <head>
    <meta name="layout" content="main"/>
    <title>Welcome to Grails</title>

  </head>
  <body>

    <div id="page-body" role="main">
      <h1>Welcome to Grails</h1>

        <sec:ifLoggedIn>
        Hello <sec:username/>
        Access the 
        <g:link controller='securedPackt' action=''>Secured Controller</g:link><br/>
        <g:link controller='logout' action=''>Spring Logout</g:link>
        </sec:ifLoggedIn>

        <sec:ifNotLoggedIn>
          <h2>You are seeing a common page.You can click on login. After login success you will be provided with the links which you can access.</h2>
        <g:link controller='securedPackt' action=''>Secured Controller</g:link><br/>

        </sec:ifNotLoggedIn>
    </div>
    </div>
  </body>
</html>

它是如何工作的...

当用户访问 URL http://localhost:8080/grailsdigestauthexample/ 时,Spring Security 将提示用户一个登录对话框,要求输入用户名和密码。当用户输入用户名和密码时,Spring Security 对其进行身份验证,并将用户重定向到受保护的页面。

应用程序的工作流程如下:

http://localhost:8080/grailsdigestauthexample/

以下截图描述了尝试访问受保护资源时弹出的登录对话框:

它是如何工作的...

它的工作方式类似于基本认证。

成功登录后,您将获得一个注销链接。用户现在已经可以访问受保护的控制器:

它是如何工作的...

显示已登录用户:

它是如何工作的...

另请参阅

  • Spring Security with Groovy Grails multilevel authentication 食谱

  • Spring Security with Groovy Grails LDAP authentication 食谱

Spring Security with Groovy Grails multiple authentication

到目前为止,我们已经看到了单角色认证。让我们看看多角色的演示。该食谱使用了另一个名为 spring-security-ui 的插件。

它有许多控制器,为用户提供用户管理屏幕。

这样可以节省开发人员编写这些屏幕的时间。它还提供了带自动完成的搜索选项。

spring-security-ui 插件还需要安装其他插件,将在控制台提示。还有一种安装插件的替代方法,即可以直接在 BuildConfig.groovy 文件中给出依赖项。

grails.project.dependency.resolution = {
  ...
  plugins {
    compile ":spring-security-ui:0.2""
  }
}

准备工作

我们需要执行以下操作以实现多级身份验证:

  • 创建一个 Grails 应用

  • 安装spring-security-core插件

  • 安装spring-security-ui插件

  • 使用quickstart命令创建RoleUser领域类

  • 创建Sample控制器

  • 编辑BootStrap.groovy文件

  • 编辑SampleController类以添加角色

  • 更新.gsp文件

如何做…

实施多重身份验证的以下步骤使用 Groovy on Grails 和 Spring Security:

  1. 转到 Grails 工作区并运行以下命令:
  • grails create-app multilevelroledemo

  • cd multilevelroledemo

  • grails install-plugin spring-security-core

  • 使用grails install-plugin spring-security-ui命令安装插件

  • 使用grails s2-quickstart com.packt.security SecuredUser SecuredRole命令

  • grails create-controller Sample

  1. 编辑SampleController文件:
package multilevelroledemo
import grails.plugins.springsecurity.Secured
class SampleController {

  def index = {}

  @Secured(['ROLE_USER'])
  def user = {
    render 'Secured for ROLE_USER'
  }

  @Secured(['ROLE_ADMIN'])
  def admin = {
    render 'Secured for ROLE_ADMIN'
  }

  @Secured(['ROLE_SUPERADMIN'])
  def superadmin = {
    render 'Secured for ROLE_SUPERADMIN'
  }
}
  1. 编辑BootStrap.groovy文件。我已添加了多个角色。这些角色和用户将从生成的领域 groovy 文件中创建:
import com.packt.security.SecuredRole
import com.packt.security.SecuredUser
import com.packt.security.SecuredUserSecuredRole
class BootStrap {
  def init = { servletContext ->
    def userRole = SecuredRole.findByAuthority("ROLE_USER") ?: new SecuredRole(authority: "ROLE_USER").save(flush: true)
    def user = SecuredUser.findByUsername("anjana") ?: new SecuredUser(username: "anjana", password: "anjana123", enabled: true).save(flush: true)
    SecuredUserSecuredRole.create(user, userRole, true)

    def userRole_admin = SecuredRole.findByAuthority("ROLE_ADMIN") ?: new SecuredRole(authority: "ROLE_ADMIN").save(flush: true)
    def user_admin = SecuredUser.findByUsername("raghu") ?: new SecuredUser(username: "raghu", password: "raghu123", enabled: true).save(flush: true)
    SecuredUserSecuredRole.create(user_admin, userRole_admin, true)

    def userRole_superadmin = SecuredRole.findByAuthority("ROLE_SUPERADMIN") ?: new SecuredRole(authority: "ROLE_SUPERADMIN").save(flush: true)
    def user_superadmin = SecuredUser.findByUsername("packt") ?: new SecuredUser(username: "packt", password: "packt123", enabled: true).save(flush: true)
    SecuredUserSecuredRole.create(user_superadmin, userRole_superadmin, true)
  }
  def destroy = {
  }
}
  1. 修改.gsp文件。在views/sample中添加一个index.gsp文件:
<head>
  <meta name='layout' content='main' />
  <title>Multi level  Roles in Grails</title>
</head>

<body>
  <div class='nav'>
    <span class='menuButton'><a class='home' href='${createLinkTo(dir:'')}'>Home</a></span>
  </div>
  <div class='body'>
    <g:link action='user'> ROLE_USER</g:link><br/>
    <g:link action='admin'>ROLE_ADMIN</g:link><br/>
    <g: link action='superadmin'> ROLE_SUPERADMIN</g:link><br/>
  </div>
</body>
  1. config文件夹中添加SecurityConfig.groovy文件:
security {
  active = true
  loginUserDomainClass = 'com.packt.security.SecuredUser'
  authorityDomainClass = 'com.packt.security.SecuredPackt'
  useRequestMapDomainClass = false
  useControllerAnnotations = true
}

它是如何工作的…

让我们看看它是如何工作的。我们还将看到spring-security-ui提供的控制器及其功能。

我们在这里有三个具有不同角色的用户。它们是在Bootstrap.groovy文件中使用领域类创建的:

  • anjana/anjana123 作为 ROLE_USER

  • raghu/raghu123 作为 ROLE_ADMIN

  • packt/packt123 作为 ROLE_SUPERADMIN

访问 URL:http://localhost:8080/multilevelroledemo/

您将看到 Grails 主页以及控制器列表。

单击spring.security.ui.usercontroller链接。该控制器属于spring-security-ui插件。该控制器提供了用户管理屏幕。该控制器为用户提供了搜索功能。这是一个很棒的 UI,它甚至具有带有搜索过滤器的自动完成选项。您可以转到以下链接:

http://localhost:8080/multilevelroledemo/user/search

下面的截图显示了 Spring 用户管理控制台,您可以在其中看到搜索用户的选项:

它是如何工作的...

让我们看一下搜索结果,如下截图所示:

它是如何工作的...

现在让我们检查spring-security-ui插件中提供的角色控制器。该控制器提供了搜索角色的选项,并提供了角色与用户的映射。它还提供了更新角色的选项:

http://localhost:8080/multilevelroledemo/role/roleSearch

您还可以创建用户,该选项可在菜单中找到。访问以下链接创建用户:

http://localhost:8080/multilevelroledemo/user/create

让我们看看我们为应用程序创建的示例控制器:

它是如何工作的...

以下 URL 显示了具有各种角色的示例控制器映射。这也是spring-security-ui插件提供的:

http://localhost:8080/multilevelroledemo/securityInfo/mappings

它是如何工作的...

让我们访问http://localhost:8080/multilevelroledemo/sample/index的示例控制器。

它显示了三个角色。单击链接,您将被提示登录。

使用适当的用户名和密码登录,您的角色信息将被显示。

spring-security-ui插件本身提供了登录和注销的选项,适用于整个应用程序。

我们只能使用注解,即@Secured注解来对用户进行身份验证和授权以执行某些操作。

我们还可以省略在Bootstrap.groovy中创建用户。

另请参阅

  • Groovy Grails LDAP 身份验证的 Spring 安全配方

Groovy Grails LDAP 身份验证的 Spring 安全

让我们进一步探索使用 LDAP 身份验证的 Groovy on Grails 上的spring-security插件。在这个示例中,我在我的机器上安装了Apache DS和 Apache Studio。我将使用这个进行身份验证。

Burt Beckwith 在此方面写了非常好的博客。您可以在以下网址查看:burtbeckwith.com/blog/

准备工作

  • 创建一个 Grails 应用程序:grailssecurityldapexamplex

  • 让我们创建一个控制器:SampleController

  • 安装以下插件:

  • spring-security-core

  • spring-security-ldap

  • 编辑Config.groovy文件。

  • 我们将在成功验证后显示角色和用户详细信息。在这个示例中,我们将根据电子邮件地址和密码对用户进行身份验证。

  • 我们需要在Config.groovy中提供 Apache DS 详细信息和端口号。

  • 我正在使用一个单独的分区sevenseas。您可以通过添加一个单独的jdmpartition来创建自己的域。

  • 有两种角色:用户和管理员。角色与 Apache DS 中的用户映射。我在 Apache DS 中创建了两个“组织单位”:

  • people:这将有用户

  • groups:这将具有映射到它的用户的角色

  • 我从 Apache DS 获取usernameroleemail

如何做…

采取以下步骤来使用 Grails 实现spring-security与 LDAP 进行身份验证:

  1. 安装以下命令以安装插件:
  • create-app grailssecurityldapexample

  • cd grailssecurityldapexample

  • grails install-plugin spring-security-core

  • grails install-plugin spring-security-ldap

  • grails create-controller Sample

  1. 让我们首先更新message.properties文件以获得清晰的 UI:
springSecurity.login.header=Spring Security login
springSecurity.login.username.label=UserName
springSecurity.login.password.label=Password
springSecurity.login.remember.me.label=remember me
springSecurity.login.button=Login
springSecurity.errors.login.fail=Authentication failed

然后在Config.groovy文件中配置 Apache DS 属性。

  1. 此配置将告诉 Grails 根据其电子邮件 ID 对用户进行身份验证:
grails.plugins.springsecurity.ldap.search.filter = '(mail={0})'
grails.plugins.springsecurity.ldap.context.server = 'ldap://localhost:10389/o=sevenSeas'
grails.plugins.springsecurity.ldap.context.managerDn = 'uid=admin,ou=system'
grails.plugins.springsecurity.ldap.context.managerPassword = 'secret'
grails.plugins.springsecurity.ldap.authorities.groupSearchBase ='ou=groups'
grails.plugins.springsecurity.ldap.authorities.groupSearchFilter = '(uniqueMember={0})'
grails.plugins.springsecurity.ldap.authorities.retrieveDatabaseRoles = false
grails.plugins.springsecurity.ldap.authorities.ignorePartialResultException= true
grails.plugins.springsecurity.ldap.search.base = 'ou=people'
grails.plugins.springsecurity.ldap.search.filter = '(mail={0})'
grails.plugins.springsecurity.ldap.search.attributesToReturn = ['cn', 'sn','mail']
grails.plugins.springsecurity.ldap.authenticator.attributesToReturn = ['cn', 'sn','mail']
  1. 编辑控制器:
package grailssecurityldapexample
class SampleController {
  def index() { 
    render "Hello PACKT"
    }
}
  1. 编辑resource.groovy文件以进行 Bean 映射。
beans = { 
ldapUserDetailsMapper(MyUserDetailsContextMapper) { 
}
}
  1. 用以下代码替换index.gsp的现有body标记:
<body>
  <a href="#page-body" class="skip"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>

  <div id="page-body" role="main">
      <h1>Welcome to Grails</h1>
      <sec:ifLoggedIn>
Your Details<br/>
      Name:<sec:loggedInUserInfo field="fullname"/> <br/>
      Email:<sec:loggedInUserInfo field="email"/> <br/>
      Role:<sec:loggedInUserInfo field="title"/> <br/>
      <g:link controller='sample' action=''>Sample Controller</g:link><br/>
      (<g:link controller="logout">Logout</g:link>)
     </sec:ifLoggedIn> 
     <sec:ifNotLoggedIn>
      <h2>You are seeing a common page. You can click on login. After login success you will be provided with the links which you can access.</h2>
      <g:link controller='login' action='auth'>Spring Login</g:link>
      </sec:ifNotLoggedIn>

    </div>
  </body>
  1. src/groovy下创建MyUserDetails.groovy
import org.springframework.security.core.GrantedAuthority 
import org.springframework.security.core.userdetails.User

class MyUserDetails extends User {   
 String fullname 
 String email 
 String title 

MyUserDetails(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities, String fullname,
String email, String title) {  
  super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities)
this.fullname = fullname 
this.email = email 
this.title = title 
}
}
  1. 让我们为 LDAP 创建一个ContextMapper

我们在这里获取 LDAP 属性:

import org.springframework.ldap.core.DirContextAdapter
import org.springframework.ldap.core.DirContextOperations
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper
class MyUserDetailsContextMapper implements UserDetailsContextMapper {
    UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection authorities) {
      String fullname = ctx.originalAttrs.attrs['cn'].values[0]
      String email = ctx.originalAttrs.attrs['mail'].values[0].toString().toLowerCase() 
      def title = ctx.originalAttrs.attrs['sn']
      def userDetails = new MyUserDetails(username, '', true, true, true, true,authorities, fullname,email,  title == null ? '' : title.values[0])
      return userDetails
    }
    void mapUserToContext(UserDetails user,
		DirContextAdapter ctx) {
			throw new IllegalStateException("Only retrieving
				data from LDAP is currently supported")
    }

}

执行以下命令以启动应用程序:

grails run-app

它是如何工作的…

当用户访问 URL:http://localhost:8080/grailssecurityldapexample/时,他们将看到一个带有链接的常规页面。在登录表单中输入用户名和密码。单击提交,Grails 将 URL 提交给 Spring Security。Spring Security 连接提供的 LDAP 详细信息并查询 LDAP 以获取用户名。成功后,用户将被重定向到成功的 URL。

访问 URL:http://localhost:8080/grailssecurityldapexample/

它是如何工作的...

单击Spring 登录链接,输入用户名:admin@test.com和密码:123456

它是如何工作的...

单击注销

单击Spring 登录链接,输入用户名:test@test.com和密码:pass。Grails 应用程序将凭据提交给 Spring Security 框架,后者查询 LDAP 并检索用户详细信息,并在安全页面上显示它:

它是如何工作的...

另请参阅

  • 第六章,使用 Vaadin 的 Spring 安全性

  • 第五章,使用 GWT 的 Spring 安全性

第五章:使用 GWT 的 Spring Security

在本章中,我们将涵盖:

  • 使用 Spring Security Beans 进行 GWT 身份验证的 Spring Security

  • 使用 GWT 和 Spring Security 进行基于表单的身份验证

  • 使用 GWT 和 Spring Security 进行基本身份验证

  • 使用 GWT 和 Spring Security 进行摘要身份验证

  • 使用 GWT 和 Spring Security 进行数据库身份验证

  • 使用 GWT 和 Spring Security 进行 LDAP 身份验证

介绍

Google Web 开发工具包GWT)提供了一个用于开发 Java Web 应用程序的标准框架。GWT 旨在创建丰富的互联网应用程序,并且如果您想要实现跨浏览器兼容性,它将是一个很好的选择。现代浏览器,例如 Mozilla 和 Chrome,提供了可以安装在所有浏览器上的 GWT 插件。不同的 IDE 包括 Eclipse、NetBeans 和许多其他 IDE 都提供了各种插件。这些插件提高了开发速度。Eclipse 的 GWT 插件带有一个内部 Jetty 服务器,应用程序会自动部署在上面。GWT 还减少了对 javascript 开发人员的依赖,因为 GWT 代码通过 GWT 编译器转换为所有浏览器兼容的 javascript 和 HTML。

在本章中,我们将演示使用各种方法集成 GWT 的 Spring Security。首先,让我们进行基本设置。这就是下载插件并创建一个示例 GWT 项目。

使用 Spring Security Beans 进行 GWT 身份验证的 Spring Security

到目前为止,在我们之前的所有演示中,我们一直在applicationContext.xml文件中提供配置。在下面的示例中,我们将采用不同的方法。在这种方法中,我们将看到如何使用 Spring Security API 中可用的身份验证提供程序接口和身份验证接口来进行身份验证。

默认情况下,GWT 插件将创建一个问候应用程序,该应用程序将通过接受用户名来向用户问候。我们的目标是在此基础上应用安全性。我们希望在启动时提示用户输入 Spring Security 登录页面,然后将用户带入应用程序。

准备就绪

  • dl.google.com/eclipse/plugin/3.7下载 Eclipse Indigo。

  • 如果您使用不同的插件,请访问:developers.google.com/eclipse/docs/download

  • 在 Eclipse 中创建一个 GWT Web 项目-这将生成一个默认的 GWT 应用程序,用于向用户问候。

  • 在任何 GWT 应用程序中,您可以看到以下模块:

  • 配置模块:这将有gwt.xml文件

  • 客户端:这将有两个接口-异步接口和另一个接口,它扩展了RemoteService接口

  • 服务器:将具有实现客户端接口并扩展远程服务 Servlet 的Implementation

  • 共享:这将有用于数据验证的类

  • 测试:您可以在这里添加您的 junit 测试用例

  • War:这将有web-inf文件夹

  • 在内部服务器上运行应用程序。您将获得一个 URL。

  • 在 Mozilla Firefox 浏览器中打开 URL;您将收到一个提示,要下载 GWT 插件并安装它。

  • 您将被提示输入用户名,输入后,您将收到一个对话框,其中将显示用户详细信息。

  • 我们的目标是在应用程序启动时应用安全性,也就是说,我们希望识别访问 GWT 应用程序的用户。

  • 创建一个applicationContext.xml文件。必须将其命名为applicationContext,否则我们将在控制台中收到错误消息。

  • 使用 spring 监听器编辑web.xml文件。

  • 确保war/web-inf/lib文件夹中有以下 JAR 文件:

  • gwt-servlet

  • spring-security-config-3.1.4.Release

  • spring-security-core-3.1.4.Release

  • spring-security-web-3.1.4.Release

  • org.spring-framework.core-3.1.4.Release

  • org.spring-framework.context.support-3.1.4.Release

  • org.springframework.context-3.1.4.Release

  • org.springframework.expression-3.1.4.Release

  • org.springframework.aop-3.1.4.Release

  • org.springframework.aspects-3.1.4.Release

  • org.springframework.asm-3.1.4.Release

  • org.springframework.web-3.1.4.Release

  • org.springframework.web.servelet-3.1.4.Release

  • org.springframework.instrument-3.1.4.Release

  • org.springframework.instrument-tomcat-3.1.4.Release

如何做...

  1. 使用 Spring 监听器和 Spring 过滤器更新Web.xml文件:
<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.web.context.ContextLoaderListener
  </listener-class>
</listener>

您可以观察到我们没有像在以前的应用程序中那样配置<context-param>。Spring 将自动寻找applicationContext.xml文件。

  1. 编辑applicationContext.xml文件:
<http auto-config="true">
  <intercept-url pattern="/xyz/**" access="ROLE_AUTHOR"/>
  <intercept-url pattern="/xyz/**" access="ROLE_AUTHOR"/>
  <intercept-url pattern="/**/*.html" access="ROLE_AUTHOR"/>
  <intercept-url pattern="/**" 
    access="IS_AUTHENTICATED_ANONYMOUSLY" />
</http>
<beans:bean id="packtAuthenticationListener" 
  class="com.demo.xyz.server.PacktAuthenticationListener"/>
<beans:bean id="packtGWTAuthenticator" 
  class="com.demo.xyz.server.PacktGWTAuthenticator" />    
<authentication-manager alias="authenticationManager">
  <authentication-provider ref="packtGWTAuthenticator"/>
</authentication-manager>
</beans:beans>

这个配置也会给出下一步的提示。您可以观察到我们没有配置任何<login-page>或其 URL。我们只给出了需要安全的 URL。<authentication-provider>与自定义类映射。

我们还配置了两个 Bean,即监听器和认证者。

Spring 的上下文 API 允许我们创建监听器来跟踪应用程序中的事件。如果您回忆一下,我们还在我们的 JSF 应用程序中使用了监听器阶段监听器来跟踪与安全相关的事件和错误。

PacktGWTAuthenticator实现了认证提供程序接口。

  1. 使用 Spring 认证提供程序创建一个认证者:
Package com.demo.xyz.server
public class PacktGWTAuthenticator implements AuthenticationProvider{
  static Users users=new Users();
  private static Map<String, String> usersMap =users.loadUsers();

  @Override
  public Authentication authenticate
    (Authentication authentication) 
  throws AuthenticationException {

    String mylogin_name = (String) authentication.getPrincipal();
    String mypassword = (String)authentication.getCredentials();
    //check username
    if (usersMap.get(mylogin_name)==null)
    throw new UsernameNotFoundException
      (mylogin_name+"credential not found in the UsersMap");
//get password
    String password = usersMap.get(mylogin_name);

    if (!password.equals(mypassword))
      throw new BadCredentialsException("Incorrect password-
        or credential not found in the UsersMap");

      Authentication packtauthenticator =  new 
        PacktGWTAuthentication("ROLE_AUTHOR", authentication);
      packtauthenticator .setAuthenticated(true);

      return packtauthenticator;

    }

    @Override
    public boolean supports(Class<? extends Object>
       authentication) {
    return UsernamePasswordAuthenticationToken.class
      .isAssignableFrom(authentication);
  }
}

在这里,authenticate()supports()是认证提供程序接口方法。用户类将加载用户。

  1. 创建一个User类来加载用户:
package com.demo.xyz.server;
import java.util.HashMap;
import java.util.Map;
public class Users {
  public Map<String, String> getUsersMap() {
    return usersMap;
  }

  public void setUsersMap(Map<String, String> usersMap) {

    this.usersMap = usersMap;
  }

  private Map<String, String> usersMap = new HashMap
    <String, String>();

  public Map<String, String> loadUsers(){
    usersMap.put("rashmi", "rashmi123");
    usersMap.put("shami", "shami123");
    usersMap.put("ravi", "ravi123");
    usersMap.put("ratty", "ratty123");
    return usersMap;
  }

}

上述类有一些 getter 和 setter。还有一个加载用户的方法。

  1. 实现 Spring 认证类以获取用户信息:
public class PacktGWTAuthentication implements Authentication{

  private static final long serialVersionUID = -3091441742758356129L;

  private boolean authenticated;

  private GrantedAuthority grantedAuthority;
  private Authentication authentication;

  public PacktGWTAuthentication(String role, Authentication authentication) {
    this.grantedAuthority = new GrantedAuthorityImpl(role);
    this.authentication = authentication;
  }

  @Override
  public Object getCredentials() {
    return authentication.getCredentials();
  }

  @Override
  public Object getDetails() {
    return authentication.getDetails();
  }

  @Override
  public Object getPrincipal() {
    return authentication.getPrincipal();
  }

  @Override
  public boolean isAuthenticated() {
    return authenticated;
  }

  @Override
  public void setAuthenticated(boolean authenticated)throws IllegalArgumentException {
    this.authenticated = authenticated;
  }

  @Override
  public String getName() {
    return this.getClass().getSimpleName();
  }
  @Override
  public Collection<GrantedAuthority> getAuthorities() {
    Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    authorities.add(granted Authority);
    return authorities;
  }

}

认证接口处理用户详细信息、主体和凭据。认证提供程序使用此类传递角色信息。

  1. 实现在 GWT 客户端包中声明的接口:
package com.demo.xyz.server;
public class PacktAuthenticatorServiceImpl extends RemoteServiceServlet  implements PacktAuthenticatorService {

  @Override
  public String authenticateServer() {
  Authentication authentication =SecurityContextHolder.getContext().getAuthentication();
  if (authentication==null){
    System.out.println("looks like you have not logged in.");
    return null;
  }
  else {
    System.out.println(authentication.getPrincipal().toString());
    System.out.println(authentication.getName().toString());
    System.out.println(authentication.getDetails().toString());
    return (String) authentication.getPrincipal();
    }

  }

}

在这个类中找到authenticate Server方法的实现。这将打印调试语句以检查用户是否已登录。如果已登录,那么我们将必须获取主体和用户详细信息。

  1. 使用 Spring 监听器跟踪事件:
package com.demo.xyz.server;
public class PacktAuthenticationListener implements 
  ApplicationListener<AbstractAuthenticationEvent>{
  @Override
  public void onApplicationEvent
    (AbstractAuthenticationEvent event) {

    final StringBuilder mybuilder = new StringBuilder();
    mybuilder.append("AN AUHTHENTICATION EVENT ");
    mybuilder.append(event.getClass().getSimpleName());
    mybuilder.append("*** ");
    mybuilder.append(event.getAuthentication().getName());
    mybuilder.append("$$$DETAILS OF THE EVENT: ");
    mybuilder.append(event.getAuthentication().getDetails());

    if (event instanceof 
      AbstractAuthenticationFailureEvent) {
      mybuilder.append("$$$ EXCEPTION HAS OCCURED: ");
      mybuilder.append(((AbstractAuthenticationFailureEvent)
       event).getException().getMessage());
    }
    System.out.println(mybuilder.toString());
  }
}

该类实现了 Springs 应用程序监听器,类型为AbstractAuthenticationEvent。我们捕获认证事件并在控制台中打印出来;您也可以使用记录器来跟踪此类事件。

  1. ModuleLoad()上更新 GWT 类:
package com.demo.xyz.client;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Xyz implements EntryPoint {
/**
 * The message displayed to the user when the server cannot be reached or
 * returns an error.
 */
private static final String SERVER_ERROR = 
  "An error occurred while "+ "attempting to contact
   the server. Please check your network "
  + "connection and try again.";

/**
 * Create a remote service proxy to talk to the server-side Greeting service.
 */
private final GreetingServiceAsync greetingService = 
  GWT.create(GreetingService.class);
private final PacktAuthenticatorServiceAsync 
  packtAuthenticatorService = 
  GWT.create(PacktAuthenticatorService.class);
/**
 * This is the entry point method.
 */
public void onModuleLoad() {
  final Button sendButton = new Button("Send");
  final TextBox nameField = new TextBox();
  nameField.setText("GWT User");
  final Label errorLabel = new Label();
  sendButton.addStyleName("sendButton");
  RootPanel.get("nameFieldContainer").add(nameField);
  RootPanel.get("sendButtonContainer").add(sendButton);
  RootPanel.get("errorLabelContainer").add(errorLabel);

// Focus the cursor on the name field when the app loads
  nameField.setFocus(true);
  nameField.selectAll();

  // Create the popup dialog box
  final DialogBox dialogBox = new DialogBox();
  dialogBox.setText("Remote Procedure Call");
  dialogBox.setAnimationEnabled(true);
  final Button closeButton = new Button("Close");
// We can set the id of a widget by accessing its Element
  closeButton.getElement().setId("closeButton");
  final Label textToServerLabel = new Label();
  final HTML serverResponseLabel = new HTML();
  VerticalPanel dialogVPanel = new VerticalPanel();
  dialogVPanel.addStyleName("dialogVPanel");
  dialogVPanel.add(new HTML
    ("<b>Sending name to the server:</b>"));
  dialogVPanel.add(textToServerLabel);
  dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
  dialogVPanel.add(serverResponseLabel);
  dialogVPanel.setHorizontalAlignment
    (VerticalPanel.ALIGN_RIGHT);
dialogVPanel.add(closeButton);
dialogBox.setWidget(dialogVPanel);

  // Add a handler to close the DialogBox
  closeButton.addClickHandler(new ClickHandler() {
    public void onClick(ClickEvent event) {
      dialogBox.hide();
      sendButton.setEnabled(true);
      sendButton.setFocus(true);
    }
  });

  // Create a handler for the sendButton and nameField
  class MyHandler implements ClickHandler, KeyUpHandler {

  public void onClick(ClickEvent event) {
    sendNameToServer();
  }

  public void onKeyUp(KeyUpEvent event) {
    if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
      sendNameToServer();
    }
  }

  /**
   * Send the name from the nameField to the server and wait for a response.
   */
  private void sendNameToServer() {
  // First, we validate the input.
  errorLabel.setText("");
  String textToServer = nameField.getText();
  if (!FieldVerifier.isValidName(textToServer)) {
    errorLabel.setText("Please enter at least four 
      characters");
    return;
    }

// Then, we send the input to the server.
    sendButton.setEnabled(false);
    textToServerLabel.setText(textToServer);
    serverResponseLabel.setText("");
    greetingService.greetServer(textToServer,
    new AsyncCallback<String>() {
      public void onFailure(Throwable caught) {
        // Show the RPC error message to the user dialogBox
        setText("Remote Procedure Call - Failure");
        serverResponseLabel.addStyleName
          ("serverResponseLabelError");
        serverResponseLabel.setHTML(SERVER_ERROR);
        dialogBox.center();
        closeButton.setFocus(true);
      }

      public void onSuccess(String result) {
        dialogBox.setText("Remote Procedure Call");
        serverResponseLabel.removeStyleName
          ("serverResponseLabelError");
        serverResponseLabel.setHTML(result);
        dialogBox.center();
        closeButton.setFocus(true);
      }
    });
  }
}

// Add a handler to send the name to the server
MyHandler handler = new MyHandler();
sendButton.addClickHandler(handler);
nameField.addKeyUpHandler(handler);
packtAuthenticatorService.authenticateServer(new AsyncCallback<String>() {
  public void onFailure(Throwable caught) {
    dialogBox.setText("Remote Procedure Call - Failure");
  }
  public void onSuccess(String result) {
    nameField.setText(result);
  }
}
);
}
}

onModuleLoad方法的末尾添加此代码。这类似于在加载时注册我们的服务。

  1. 编辑PacktAuthenticationService类:
package com.demo.xyz.client;

/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class Xyz implements EntryPoint {
  /**
   * The message displayed to the user when the server cannot be reached or
   * returns an error.
   */
  private static final String SERVER_ERROR = 
    "An error occurred while "+ "attempting to contact
     the server. Please check your network "
    + "connection and try again.";

  /**
   * Create a remote service proxy to talk to the server-side Greeting service.
   */
  private final GreetingServiceAsync greetingService
     = GWT.create(GreetingService.class);
  private final PacktAuthenticatorServiceAsync 
    packtAuthenticatorService = 
    GWT.create(PacktAuthenticatorService.class);
  /**
   * This is the entry point method.
   */
  public void onModuleLoad() {
    final Button sendButton = new Button("Send");
    final TextBox nameField = new TextBox();
    nameField.setText("GWT User");
    final Label errorLabel = new Label();

    // We can add style names to widgets
    sendButton.addStyleName("sendButton");

    // Add the nameField and sendButton to the RootPanel
    // Use RootPanel.get() to get the entire body element
    RootPanel.get("nameFieldContainer").add(nameField);
    RootPanel.get("sendButtonContainer").add(sendButton);
    RootPanel.get("errorLabelContainer").add(errorLabel);

    // Focus the cursor on the name field when the app loads nameField.setFocus(true);
    nameField.selectAll();

    // Create the popup dialog box
    final DialogBox dialogBox = new DialogBox();
    dialogBox.setText("Remote Procedure Call");
    dialogBox.setAnimationEnabled(true);
    final Button closeButton = new Button("Close");
    //We can set the id of a widget by accessing its Element
    closeButton.getElement().setId("closeButton");
    final Label textToServerLabel = new Label();
    final HTML serverResponseLabel = new HTML();
    VerticalPanel dialogVPanel = new VerticalPanel();
    dialogVPanel.addStyleName("dialogVPanel");
    dialogVPanel.add(new HTML
      ("<b>Sending name to the server:</b>"));
    dialogVPanel.add(textToServerLabel);
    dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
    dialogVPanel.add(serverResponseLabel);
    dialogVPanel.setHorizontalAlignment
      (VerticalPanel.ALIGN_RIGHT);
    dialogVPanel.add(closeButton);
    dialogBox.setWidget(dialogVPanel);

    // Add a handler to close the DialogBox
    closeButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        dialogBox.hide();
        sendButton.setEnabled(true);
        sendButton.setFocus(true);
      }
    });

    // Create a handler for the sendButton and nameField
    class MyHandler implements ClickHandler, KeyUpHandler {
      /**
       * Fired when the user clicks on the sendButton.
       */
      public void onClick(ClickEvent event) {
        sendNameToServer();
      }

      /**
       * Fired when the user types in the nameField.
       */
      public void onKeyUp(KeyUpEvent event) {
        if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
          sendNameToServer();
        }
      }

        /**
         * Send the name from the nameField to the server and wait for a response.
         */
        private void sendNameToServer() {
        // First, we validate the input.
        errorLabel.setText("");
        String textToServer = nameField.getText();
        if (!FieldVerifier.isValidName(textToServer)) {
          errorLabel.setText("Please enter at least
             four characters");
          return;
        }

        // Then, we send the input to the server.
        sendButton.setEnabled(false);
        textToServerLabel.setText(textToServer);
        serverResponseLabel.setText("");
        greetingService.greetServer(textToServer,
        new AsyncCallback<String>() {
          public void onFailure(Throwable caught) {
            // Show the RPC error message to the user
          dialogBox.setText("Remote Procedure Call
             - Failure");
          serverResponseLabel.addStyleName
            ("serverResponseLabelError");
          serverResponseLabel.setHTML(SERVER_ERROR);
          dialogBox.center();
          closeButton.setFocus(true);
        }

        public void onSuccess(String result) {
        dialogBox.setText("Remote Procedure Call");
        serverResponseLabel.removeStyleName
          ("serverResponseLabelError");
        serverResponseLabel.setHTML(result);
        dialogBox.center();
        closeButton.setFocus(true);
      }
    });
  }
}

// Add a handler to send the name to the server
MyHandler handler = new MyHandler();
sendButton.addClickHandler(handler);
nameField.addKeyUpHandler(handler);
packtAuthenticatorService.authenticateServer(new AsyncCallback<String>() {
  public void onFailure(Throwable caught) {
  dialogBox.setText("Remote Procedure Call - Failure");
}
public void onSuccess(String result) {
  nameField.setText(result);
}
}
);
}
}

它是如何工作的...

现在访问以下 URL:

http://127.0.0.1:8888/Xyz.html?gwt.codesvr=127.0.0.1:9997

用户将被重定向到 Spring Security 内部登录页面。当用户输入用户密码并点击提交时,PacktGWTAuthenticator类从Users类中加载用户,并比较输入。如果映射具有与用户提供的相同的凭据,授权将被启动,并且成功后,用户将被引导到 GWT 应用程序。该示例已经显式使用了 Spring Security 的Authentication ProviderAuthenticator Bean类,通过实现接口和application-context.xml调用PacktGWTAuthenticatorPacktGWTAuthentication implementation类来进行认证和授权。

它是如何工作的...它是如何工作的...

成功登录时将看到先前的图像。

Eclipse 控制台中生成的监听器输出:

PacktGWTAuthentication
org.springframework.security.web.authentication.WebAuthenticationDetails@fffdaa08: RemoteIpAddress: 127.0.0.1; SessionId: 1cdb5kk395o29

登录失败时显示以下图像:

它是如何工作的...

另请参阅

  • 使用 GWT 和 Spring Security 进行基于表单的身份验证食谱

  • 使用 GWT 和 Spring Security 进行基本身份验证食谱

  • 使用 GWT 和 Spring Security 进行摘要身份验证食谱

  • 使用 GWT 和 Spring Security 进行数据库身份验证食谱

  • 使用 GWT 和 Spring Security 进行 LDAP 身份验证食谱

使用 GWT 和 Spring Security 进行基于表单的身份验证

我们将演示 GWT 中的基于表单的身份验证。这与我们在以前的配方中所做的身份验证非常相似。我们将编辑applicationContext.xml

准备工作

  • 创建一个样本 GWT 项目。

  • 在构建路径中添加与 spring 相关的 JAR 包。

  • 添加与 Spring Security 相关的 JAR 包。

  • 添加applicationContext.xml文件。

  • 按照上一节所示编辑web.xml文件。

  • 还要在web-inf lib文件夹中添加与 spring 相关的 JAR 包。

如何做...

编辑applicationContext.xml文件:

<http auto-config="true" >
  <intercept-url pattern="/basicgwtauth/**"
     access="ROLE_AUTHOR"/>
        <intercept-url pattern="/basicgwtauth/**" access="ROLE_AUTHOR"/>
        <intercept-url pattern="/**/*.html" access="ROLE_AUTHOR"/>
        <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />

</http>
<authentication-manager>
  <authentication-provider>
    <user-service>
      <user name="anjana" password="123456" 
      authorities="ROLE_AUTHOR" />
    </user-service>
  </authentication-provider>
</authentication-manager>

此配置调用内部 Spring Security 登录表单。其想法是展示另一种情景,在这种情况下我们不指定身份验证机制,而是 spring 默认使用其登录表单页面来对用户进行身份验证。

工作原理...

现在访问以下 URL:

http://127.0.0.1:8888/Basicgwtauth.html?gwt.codesvr=127.0.0.1:9997

工作原理...

输入登录用户名和密码;您将被带到 GWT 页面。这也是一种机制,用于调用 spring 的内部登录 jsp 页面,如果开发人员不想创建自己定制的 jsp。它仍然读取提供的身份验证提供程序详细信息以对用户进行身份验证和授权。

以类似的方式,您也可以通过编辑身份验证管理器配置来使用数据库和 LDAP 进行身份验证。

另请参阅

  • 使用 GWT 和 Spring Security 进行基本身份验证的配方

  • 使用 GWT 和 Spring Security 进行摘要身份验证的配方

  • 使用 GWT 和 Spring Security 进行数据库身份验证的配方

  • 使用 GWT 和 Spring Security 进行 LDAP 身份验证的配方

使用 GWT 和 Spring Security 进行基本身份验证

我们将演示 GWT 中的基本身份验证。这与我们稍后将要做的基本身份验证非常相似。我们将编辑applicationContext.xml

准备工作

  • 创建一个样本 GWT 项目

  • 在构建路径中添加与 spring 相关的 JAR 包

  • 添加与 Spring Security 相关的 JAR 包

  • 添加applicationContext.xml文件

  • 按照上一节所示编辑web.xml文件

  • 还要在web-inf lib文件夹中添加与 spring 相关的 JAR 包

如何做...

编辑applicationContext.xml文件:

<http auto-config="true" >
  <intercept-url pattern="/basicgwtauth/**"
     access="ROLE_AUTHOR"/>
  <intercept-url pattern="/basicgwtauth/**"
     access="ROLE_AUTHOR"/>
  <intercept-url pattern="/**/*.html" access="ROLE_AUTHOR"/>
  <intercept-url pattern="/**"
     access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <http-basic />
</http>
<authentication-manager>
  <authentication-provider>
    <user-service>
      <user name="anjana" password="123456" 
        authorities="ROLE_AUTHOR" />
    </user-service>
  </authentication-provider>
</authentication-manager>

在这里,我们将指定基本的身份验证机制。

工作原理..

现在访问 URL:

http://127.0.0.1:8888/Basicgwtauth.html?gwt.codesvr=127.0.0.1:9997

Spring Security 将阻止用户访问 GWT 应用程序。安全机制将从application-context.xml文件中读取。对于此应用程序,安全机制是基本的。Spring Security 将弹出一个对话框,要求输入用户名和密码。用户输入的登录用户名和密码将被验证和授权,用户将被带到 GWT 页面。

工作原理..

输入登录用户名和密码,您将被带到 GWT 页面。

另请参阅

  • 使用 GWT 和 Spring Security 进行摘要身份验证的配方

  • 使用 GWT 和 Spring Security 进行数据库身份验证的配方

  • 使用 GWT 和 Spring Security 进行 LDAP 身份验证的配方

使用 GWT 和 Spring Security 进行摘要身份验证

我们现在将演示 GWT 中的摘要身份验证。这与我们在以前的配方中所做的基本身份验证非常相似。我们将编辑applicationContext.xml。我们将对密码进行哈希处理。设置保持不变,唯一的变化是applicationcontext.xml

准备工作

  • 创建一个样本 GWT 项目

  • 在构建路径中添加与 spring 相关的 JAR 包

  • 添加与 Spring Security 相关的 JAR 包

  • 添加applicationContext.xml文件

  • 按照上一节所示编辑web.xml文件

  • 还要在web-inf lib文件夹中添加与 spring 相关的 JAR 包

如何做...

编辑applicationContext.xml文件:

<http auto-config="true" >
  <intercept-url pattern="/basicgwtauth/**" access="
     ROLE_EDITOR "/>
  <intercept-url pattern="/basicgwtauth/**" access="
     ROLE_EDITOR "/>
  <intercept-url pattern="/**/*.html" access=
    " ROLE_EDITOR "/>
  <intercept-url pattern="/**" access
    ="IS_AUTHENTICATED_ANONYMOUSLY" />
  <http-basic />
</http>
<authentication-manager>
  <authentication-provider>
    <password-encoder hash="sha" />
    <user-service>
      <user name="anjana" 
        password="bde892ed4e131546a2f9997cc94d31e2c8f18b2a" 
      authorities="ROLE_EDITOR" />
    </user-service>
  </authentication-provider>
</authentication-manager>

在这里,我们指定身份验证机制为基本,并在此处给出了哈希密码。要对密码进行哈希处理,请使用jacksum jar。这已经在第二章中进行了演示,“Spring Security with Sturts2”。

它是如何工作的...

现在访问以下 URL:

http://127.0.0.1:8888/Basicgwtauth.html?gwt.codesvr=127.0.0.1:9997

用户应通过访问此 URL 重定向到 GWT 应用程序。但是 Spring 框架会中断此操作,以检查用户是否有权查看应用程序。它会弹出一个登录屏幕。输入登录用户名和密码,您将进入 GWT 页面。

根据配置文件中提到的算法对密码进行解码以进行身份验证。这里提到的算法是Sha。因此,密码将使用Sha 算法进行加密和解密。

它是如何工作的...

输入登录用户名和密码,您将进入 GWT 页面。根据配置文件中提到的算法,将对密码进行解码以进行身份验证。

另请参阅

  • 使用 GWT 和 Spring Security 进行数据库身份验证配方

  • LDAP 身份验证与 GWT 和 Spring Security配方

GWT 和 Spring Security 的数据库身份验证

我们将演示 GWT 中的数据库身份验证。设置保持不变。在我们以前的所有示例中,我们都使用了applicationContext.xml,这是 Spring 框架很容易识别的,因为它具有默认文件名。在当前示例中,我们将为此提供一个新的文件名,并查看应用程序的响应。此外,我们需要添加spring-jdbc.xml

准备就绪

  • 创建一个示例 GWT 项目

  • 在构建路径中添加与 spring 相关的 JAR 包

  • 添加与 Spring Security 相关的 JAR 包

  • 添加spring-security.xml文件

  • 添加与 spring-jdbc 相关的 JAR 包

  • 根据前一部分的示例编辑web.xml文件

  • 还要在web-inf lib文件夹中添加与 spring 相关的 JAR 包

如何做...

编辑spring-security.xml文件:

<http auto-config="true" >
  <intercept-url pattern="/springgwtdbsecurity/**"
     access="ROLE_AUTHOR"/>
  <intercept-url pattern="/springgwtdbsecurity/**"
     access="ROLE_AUTHOR"/>
  <intercept-url pattern="/**/*.html" access="ROLE_AUTHOR"/>
  <intercept-url pattern="/**"
     access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <http-basic />
</http>
<authentication-manager alias="authenticationManager">
  <authentication-provider>
  <jdbc-user-service data-source-ref="dataSource"
  users-by-username-query="
  select username,password, enabled 
  from users where username=?" 

  authorities-by-username-query="
  select u.username, ur.authority from users u,
     user_roles ur 
        where u.user_id = ur.user_id and u.username =?"/>
  </authentication-provider>
</authentication-manager>

xml文件的 beans 标记中添加上述内容。在这里,我们指定身份验证机制为基本,并且用户信息存储在数据库中。

编辑spring-jdbc.xml文件:

<beans 

  xsi:schemaLocation="http://www.springframework.org/
    schema/beans
  http://www.springframework.org/schema/beans/
    spring-beans-3.0.xsd">

  <bean id="MySqlDatasource" class="org.springframework.
    jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value=
      "com.mysql.jdbc.Driver" />
    <property name="url" value=
      "jdbc:mysql://localhost:3306/packtdb" />
    <property name="username" value="root" />
  <property name="password" value="packt123" />
  </bean>
</beans>

我们正在提供数据库信息。

编辑web.xml文件:

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
    /WEB-INF/spring-security.xml,
    /WEB-INF/spring-jdbc.xml
  </param-value>
</context-param>

<listener>
  <listener-class>
    org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>

我们必须配置springsecurityFilterchain,如前面的示例所示,在其中添加上述部分。

它是如何工作的...

现在访问以下 URL:

http://127.0.0.1:8888/springgwtdbsecurity.html?gwt.codesvr=127.0.0.1:9997

输入登录用户名和密码,您将进入 GWT 页面。将创建数据库连接并执行查询。用户输入的值将与检索到的值进行身份验证。通过这种方式,我们可以看到 GWT 与 Spring Security 无缝集成。

另请参阅

  • LDAP 身份验证与 GWT 和 Spring Security配方

GWT 和 Spring Security 的 LDAP 身份验证

我们将演示 GWT 中的 LDAP 身份验证。设置保持不变:用户必须创建组和用户。

准备就绪

  • 创建一个示例 GWT 项目

  • 在构建路径中添加与 spring 相关的 JAR 包

  • 添加与 Spring Security 相关的 JAR 包

  • 添加spring-security.xml文件

  • 添加与 spring-LDAP 相关的 JAR 包

  • 根据前一部分显示的内容编辑web.xml文件

  • 还要在web-inf lib文件夹中添加与 spring 相关的 JAR 包

如何做...

编辑spring-security.xml文件:

<http auto-config="true" >
  <intercept-url pattern="/springgwtldapsecurity/**"
     access="ROLE_AUTHOR"/>
  <intercept-url pattern="/springgwtldapsecurity/**"
     access="ROLE_AUTHOR"/>
  <intercept-url pattern="/**/*.html" access="ROLE_AUTHOR"/>
  <intercept-url pattern="/**"
     access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <http-basic />
</http>
<authentication-manager>
  <ldap-authentication-provider 
    user-search-filter="(mail={0})" 
    user-search-base="ou=people"
    group-search-filter="(uniqueMember={0})"
    group-search-base="ou=groups"
    group-role-attribute="cn"
    role-prefix="ROLE_">
    </ldap-authentication-provider>
  </authentication-manager>

<ldap-server url="ldap://localhost:389/o=example"
   manager-dn="uid=admin,ou=system"
   manager-password="secret" />

将此代码添加到 xml 的beans标记中。在这里,我们指定身份验证机制为基本,并且用户信息存储在 LDAP 服务器中。

编辑web.xml文件:

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
    /WEB-INF/spring-security.xml
  </param-value>
</context-param>

<listener>
  <listener-class>
    org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>

我们必须像前面的示例中那样配置springsecurityFilterchain

它是如何工作的...

现在访问以下 URL:

27.0.0.1:8888/springgwtldapsecurity.html?gwt.codesvr=127.0.0.1:9997

输入登录用户名和密码,您将被带到 GWT 页面。Spring 将使用<ldap-server>标签中提供的详细信息来访问开放 LDAP。Spring Security LDAP 将与开放 LDAP 通信,并将用户输入的值与检索到的值进行身份验证。成功后,用户将被重定向到应用程序。通过这一点,我们可以看到 GWT 与 Spring Security 无缝集成。

还有更多...

谷歌上有一个活跃的项目code-gwtsecurity包,旨在将 Spring Security 与 GWT 应用程序集成。它通过 GWT 弹出窗口进行登录。在身份验证失败时,它会在 GWT 窗口上向用户显示错误消息。文件Spring4GWT jar通过拦截 RPC 中的错误消息来工作。

让我们看看在下一章中 Spring 如何与 Vaadin 集成。

第六章:使用 Vaadin 的 Spring Security

在本章中,我们将涵盖:

  • 使用 Vaadin 的 Spring Security - 基本身份验证

  • 使用 Vaadin 的 Spring Security - Spring 基于表单的身份验证

  • 使用 Vaadin 的 Spring Security - 自定义 JSP 基于表单的身份验证

  • 使用 Vaadin 的 Spring Security - 使用 Vaadin 表单

介绍

Vaadin 已成为当前项目中流行的框架。它提供了类似 GWT 的 RIA。它没有 RPC 调用和异步服务类。它的工作方式类似于 GWT 小部件。Vaadin 还很容易与 portlet 集成。在 GWT 中,我们必须安装与浏览器兼容的 GWT 插件,但在 Vaadin 中我们不需要这样做。在 Vaadin 开发的应用程序在所有现代浏览器上都兼容。Vaadin 可以编写为服务器端和客户端应用程序。Vaadin UI 组件实际上是一个 JavaServlet 组件,可以轻松运行在诸如 Tomcat 之类的 Web 服务器上,也可以运行在 JBOSS 和 Glassfish 等应用服务器上。在当前演示中,我正在使用 Tomcat 和 Eclipse Indigo。

在本章中,我们将演示使用各种方法集成 Spring Security 与 Vaadin。让我们首先进行基本设置。这就是下载插件并创建一个示例 Vaadin 项目。

使用 Vaadin 的 Spring Security - 基本身份验证

我们的目标是在 Vaadin 应用程序上进行简单的基本身份验证。当我们访问 Vaadin 应用程序的 URL 时,我希望出现一个登录对话框。我创建了一个简单的产品目录应用程序,它看起来与地址簿非常相似。

准备工作

  • 在 Eclipse 上设置 Vaadin 应用程序:

  • 下载 Vaadin vaadin.com/eclipse 适用于 Eclipse Indigo。

在本章中,我们将演示 Spring Security 与 Vaadin 两个版本(Vaadin 6 和 Vaadin 7)的集成。

  • 在 Eclipse 中创建一个 Vaadin 7 的 Vaadin Web 项目 - 这将生成一个带有点击按钮的默认应用程序,我们将对其进行修改。

  • 在 Tomcat 服务器上运行应用程序。

  • 创建一个applicationContext.xml文件。必须将其命名为applicationContext,否则我们将在控制台中收到错误消息。

  • 编辑web.xml文件,添加 spring 监听器。

  • 将所有 jar 包添加到类路径中。

如何做...

以下步骤是为了将 Spring Security 与 Vaadin 集成以演示基本身份验证:

  1. 使用 spring 监听器和 spring 过滤器更新web.xml文件,使用 Vaadin servlet:
<display-name>Vaadin_Project1</display-name>
<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.web.context.ContextLoaderListener
  </listener-class>
</listener>

  <context-param>
    <description>
    Vaadin production mode</description>
    <param-name>productionMode</param-name>
    <param-value>false</param-value>
  </context-param>

  <servlet>
    <servlet-name>Vaadin_Project1</servlet-name>
    <servlet-class>com.vaadin.server.VaadinServlet
      </servlet-class>
  <init-param>
    <description>
      Vaadin UI class to use</description>
    <param-name>UI</param-name>
    <param-value>com.example.vaadin_project1
      .Vaadin_project1UI</param-value>
  </init-param>
  <init-param>
    <description>
    Legacy mode to return the value of
       the property as a string from 
      AbstractProperty.toString()</description>
    <param-name>legacyPropertyToString</param-name>
    <param-value>false</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>Vaadin_Project1</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>
  1. 您可以观察到我们没有像在以前的应用程序中那样配置<context-param>。Spring 将自动查找applicationContext.xml文件。为了设置 Vaadin,我们需要使用两个参数PropertyToString和一个名为com.example.vaadin_project1的 UI 类来配置 Vaadin servlet 类。使用以下代码编辑applicationContext.xml文件:
<http auto-config="true">
 <intercept-url pattern="/Vaadin_Project1/**"access="ROLE_EDITOR"/> 
 <intercept-url pattern="/Vaadin_Project1/*.*"access="ROLE_EDITOR"/> 
 <intercept-url pattern="/**" access="ROLE_EDITOR" />
 <http-basic /> 
</http>

<authentication-manager>
  <authentication-provider>
    <user-service>
      <user name="anjana" password="123456"authorities="ROLE_EDITOR" />
    </user-service>
  </authentication-provider>
</authentication-manager>
</beans:beans>

这是一个简单的基本身份验证配置。使用此配置,我们期望在显示 Vaadin 应用程序之前出现登录对话框。我创建了一个新的编辑器角色。

在这里,我们创建了一个ProductList组件来显示产品列表。

它是如何工作的...

在这个例子中,我们演示了 Vaadin 应用程序的基本身份验证机制。有时我们不需要为用户显示 jsp 页面或 Vaadin 登录表单,在这种情况下,我们选择基本身份验证,其中会弹出一个对话框要求用户输入他们的凭据。成功后,用户将获得对 Vaadin 应用程序的访问权限。应用程序的工作流程如下所示:

现在访问以下 URL:

http://localhost:8086/Vaadin_Project1/

您应该看到以下截图中显示的页面:

它是如何工作的...它是如何工作的...

另请参阅

  • 使用 Vaadin 的 Spring Security - Spring 基于表单的身份验证配方

  • 使用 Vaadin 的 Spring Security - 自定义 JSP 基于表单的身份验证配方

  • 使用 Vaadin 的 Spring Security - 使用 Vaadin 表单配方

Spring Security with Vaadin – Spring 表单认证

我们将演示 Vaadin 中的基于表单的认证。这与我们在之前的配方中使用的认证非常相似。我们将编辑 applicationContext.xml 文件。我们不会创建任何自定义登录表单,我们希望使用 spring 内部登录表单。

准备工作

您必须在 application-Context.xml 文件中注释掉 <http-basic/> 标记。

如何做...

按照以下代码编辑 applicationContext.xml 文件:

<http auto-config="true">
  <intercept-url pattern="/Vaadin_Project1/**"
     access="ROLE_EDITOR"/> 
  <intercept-url pattern="/Vaadin_Project1/*.*"
     access="ROLE_EDITOR"/> 
  <intercept-url pattern="/**" access="ROLE_EDITOR" />
</http>
<authentication-manager>
  <authentication-provider>
    <user-service>
       <user name="anjana" password="123456"
       authorities="ROLE_EDITOR" />
    </user-service>
  </authentication-provider>
</authentication-manager>

工作原理...

在这个例子中,调用了 spring 的内部登录表单来对 Vaadin 应用程序进行认证。这个配置是在 applicationConext.xml 文件中完成的。Spring 框架弹出了自己的内部 jsp 文件供用户使用。当用户输入凭据并点击 提交 时,他们将被重定向到 Vaadin 应用程序。运行 Tomcat 服务器。

现在访问以下 URL:

http://localhost:8086/Vaadin_Project1/

工作原理...

这是 Spring 提供的内置登录表单。

输入登录用户名和密码,您将进入 Vaadin 产品列表。

同样,您可以通过编辑 authentication-manager 配置来使用数据库和 LDAP 进行认证。

另请参阅

  • Spring Security with Vaadin – 自定义 JSP 表单认证 配方

  • Spring Security with Vaadin – 使用 Vaadin 表单 配方

Spring Security with Vaadin – 自定义 JSP 表单认证

到目前为止,我们已经演示了使用 Spring Security API 登录表单和登录弹出对话框的 Vaadin 7 应用程序。我们所做的一切都是在应用程序上下文文件中创建用户。

这次我们将为应用程序上下文赋予不同的名称,并提供一个自定义的登录表单,并使用 Vaadin 6 项目。

准备工作

  • 创建一个示例 Vaadin 6 项目

  • 在构建路径中添加与 Spring 相关的 jar 包

  • 添加与 Spring Security 相关的 jar 包

  • 添加 vaadin-spring-security.xml 文件

  • 添加 mybeans.xml 文件

  • 按照上一节中的示例编辑 web.xml 文件

  • 还要在 web-inf lib 文件夹中添加与 Spring 相关的 jar 包

如何做...

以下步骤是使用自定义 JSP 实现基于表单的认证,使用 Vaadin 应用程序。

由于 Vaadin 6 应用程序的入口点是 AbstractApplicationServlet,我们将创建一个扩展 AbstractApplicationServlet 的类。这将给我们一个选择来重写类的方法。

我们还将创建一个扩展 Application 类的类。在这个类中,我们将创建一个窗口。例如,我们将在登录后添加一些文本。

我们还将在 web.xml 文件中添加 jsp 文件映射。

我们需要将 MyAbstractApplicationServlet 类映射为 web.xml 文件中的 Servlet。

我们还需要配置 Spring 上下文监听器和 Spring 过滤器。

  1. 编辑 web.xml 文件:
<display-name>Vaadin_Project3</display-name>
  <context-param>
    <description>Vaadin production mode</description>
    <param-name>productionMode</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      /WEB-INF/vaadin-spring-security.xml
      /WEB-INF/mybeans.xml
    </param-value>

  </context-param>

  <servlet>
    <servlet-name>login</servlet-name>
    <jsp-file>/jsp/login.jsp</jsp-file>
  </servlet>

  <servlet>
    <servlet-name>login_error</servlet-name>
    <jsp-file>/jsp/login_error.jsp</jsp-file>
  </servlet>

  <servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/jsp/login</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>login_error</servlet-name>
    <url-pattern>/jsp/login_error</url-pattern>
  </servlet-mapping>

 <servlet>
 <servlet-name>Vaadin Application Servlet</servlet-name>
 <servlet-class>packt.vaadin.MyAbstractApplicationServlet</servlet-class>
 </servlet>

  <servlet-mapping>
    <servlet-name>Vaadin Application Servlet</servlet-name>
    <url-pattern>/*</url-pattern>

  </servlet-mapping>
  1. 编辑 vaadin-spring-security.xml 文件:
<global-method-security pre-post-annotations="enabled" />

<http auto-config='true'>
  <intercept-url pattern="/jsp/login*"access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <intercept-url pattern="/jsp/login_error*"access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <intercept-url pattern="/**" access="ROLE_USER" />
  <form-login login-page='/jsp/login'authentication-failure-url="/jsp/login_error" />
</http>

<authentication-manager>
  <authentication-provider>
    <user-service>
      <user name="raghu" password="anju"authorities="ROLE_USER,ROLE_ADMIN" />
      <user name="onju" password="bonju"authorities="ROLE_USER" />
    </user-service>
  </authentication-provider>
</authentication-manager>
  1. 子类化并重写 AbstractApplicationServlet 方法。

AbstractApplicationServlet 类是一个抽象类,扩展了 HttpServlet 并实现了一个名为 Constants 的接口。 Service()init() 方法是由 servlet 容器使用的 servlet 方法。我们创建了一个 appContext 对象,并在 init() 方法中对其进行了初始化。已重写 getNewApplication() 方法以获取扩展应用程序的类。已重写 getApplication() 方法。

如何做...

  1. 实现如下:

MyAbstractApplicationServlet

public class MyAbstractApplicationServlet extends AbstractApplicationServlet
{
  private WebApplicationContext appContext;
  private Class<? extends Application> applicationClass;

  @Override
  protected Application getNewApplication(HttpServletRequest httpServletRequest) throws ServletException {
      MainApplication mainApplication = (MainApplication)appContext.getBean("applicationBean");
      mainApplication.setWebApplicationContext(appContext);
      return  mainApplication;
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
      super.service(request, response); 
    }

    @Override
    public void init(ServletConfig servletConfig)throws ServletException {
      super.init(servletConfig);   
      appContext = WebApplicationContextUtils.getWebApplicationContext(servletConfig.getServletContext());
    }

    @Override
    protected Class<? extends Application>getApplicationClass() throws ClassNotFoundException {
    return MainApplication.class;
  }
}
  1. 子类化并重写 ApplicationClass 方法。

ApplicationClass 是一个抽象类,实现了一些接口。我们已经重写了抽象类的 init() 方法。您需要创建 HeaderHorizontalLayout 类并将它们作为组件添加到窗口中。

如何做...

MainApplication

@Component("applicationBean")
@Scope("prototype")

public class MainApplication extends Application {

  public WebApplicationContext webappContext;

  @Override
  public void init() {
    Window window;
    window = new Window("My Vaadin Application");
    window.addComponent(new HeaderHorizontalLayout(this));
    window.addComponent(new BodyHorizontalLayout(this));
    window.addComponent(new FooterHorizontalLayout(this));
    setMainWindow(window);
  }

  public void setWebApplicationContext(WebApplicationContext appContext){
  this.webappContext = webappContext;
  }

}

工作原理...

在此示例中,我们使用定制的 jsp 页面来处理对 Vaadin 应用程序的访问。当用户尝试访问 Vaadin 应用程序时,定制的 jsp 会显示给用户。用户输入用户名和密码,然后由 Spring 框架进行验证。验证成功后,Vaadin 页面将显示。

工作流程如下所示:

现在访问 URL:

http://localhost:8086/Vaadin_Project3/

它是如何工作的...

输入登录用户名和密码,您将被带到 Vaadin 页面。

它是如何工作的...

另请参阅

  • 使用 Vaadin 表单的 Spring Security - 使用 Vaadin 表单配方

Spring Security 与 Vaadin - 使用 Vaadin 表单

到目前为止,我们已经使用了定制的 JSP 页面或 Spring 提供的登录弹出框或 JSP 文件。我们还演示了 Spring Security 与 Vaadin 6 和 Vaadin 7 的集成。因此,我很想提供一个完整的 Vaadin 与 Spring Security 实现。让我们创建一个 Vaadin 表单,并将其与 Spring Security 集成。

准备工作

  • 在 Eclipse IDE 中创建一个 Vaadin 7 项目

  • 创建一个扩展面板的MyLoginView

  • 创建一个扩展面板的SecuredView

  • 创建一个扩展VaadinServletMyVaadinServlet

  • 创建一个VaadinRequestHolder

  • 配置web.xml文件

  • 编辑applicationContext.xml文件

  • 为面板类实现View接口

如何做...

以下给出的步骤是为了创建一个 Vaadin 登录表单,并将其用于使用 Spring Security 对用户进行认证:

  1. MyLoginView将在应用程序启动时加载登录表单。
public class MyLoginView extends Panel implements View {
  private Layout mainLayout;
  Navigator navigator;
  protected static final String CountView = "SecuredView";
  public MyLoginView() {
    final FormLayout loginlayout=new FormLayout();
    final TextField nameField=new TextField("name");
    final PasswordField passwordField=new PasswordField("password");
    loginlayout.addComponent(nameField);
    loginlayout.addComponent(passwordField);
    Button loginButton = new Button("Login");
    loginlayout.addComponent(loginButton);
    mainLayout = new VerticalLayout();
    mainLayout.addComponent(loginlayout);
    setContent(mainLayout);

    loginButton.addClickListener(new Button.ClickListener() {
      public void buttonClick(ClickEvent event) {
        try{
          ServletContext servletContext = VaadinRequestHolder.getRequest().getSession().getServletContext();
          UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(nameField.getValue(),passwordField.getValue());
            token.setDetails( new WebAuthenticationDetails(VaadinRequestHolder.getRequest()));
            WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
            AuthenticationManager authManager = wac.getBean(AuthenticationManager.class);
            Authentication authentication = authManager.authenticate(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            if(authentication.isAuthenticated()){
              Notification.show("You are authenticated");
            navigator = new Navigator(getUI().getCurrent(), mainLayout);
            navigator.addView(CountView, new SecuredView());
            navigator.navigateTo(CountView);
          }

    } catch (BadCredentialsException e) {

      Notification.show("Bad credentials");
    }
  }
});

}
@Override
public void enter(ViewChangeEvent event) {
}

我们使用了表单布局,并添加了用户名和密码字段。我们添加了一个按钮。点击按钮时,我们进行认证。

我们在requestHolder.UserNamePasswords中捕获VaadinRequest对象。认证令牌接收来自用户名和密码字段的输入。然后将令牌传递给AuthenticationManger以验证字段。如果认证成功,它将导航到受保护的页面。它还会向用户发出通知。

  1. 在认证后使用Secured View并提供注销功能。
public class SecuredView extends Panel implements View {
  public static final String NAME = "count";
  private Layout mainLayout;
  Navigator navigator;
  protected static final String MainView = "LoginView";
  public SecuredView() {
    mainLayout = new VerticalLayout();
    mainLayout.addComponent(new Label("You are seeing a secured page"));
    Button logoutButton = new Button("Logout");
    mainLayout.addComponent(logoutButton);
    setContent(mainLayout);
    logoutButton.addClickListener(new Button.ClickListener() {
    public void buttonClick(ClickEvent event) {
    try{
      ServletContext servletContext = VaadinRequestHolder.getRequest().getSession().getServletContext();
      WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
      LogoutHandler logoutHandler = wac.getBean(LogoutHandler.class);
      Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
      logoutHandler.logout(VaadinRequestHolder.getRequest(), null, authentication);

 Notification.show("You are logged out");
 navigator = new Navigator(getUI().getCurrent(), mainLayout);
 navigator.addView(MainView, new MyLoginView());
 navigator.navigateTo(MainView);
    } catch (BadCredentialsException e) {

    Notification.show("Bad credentials");
    }
  }
});
}

public void enter(ViewChangeEvent event) {

}

}

受保护的视图有一个标签和一个注销按钮。注销按钮点击事件处理springlogout。注销时,用户将被重定向到登录页面。LogoutHandler类有一个logout()方法来处理认证。我使用了导航器类。您可以使用 UI 类getUI.Current创建导航器的实例,它会给出一个 UI 对象。

这种方法可以在您的面板类中使用。我还将布局对象传递给构造函数。

navigator = new Navigator(getUI().getCurrent(),mainLayout);
navigator.addView(MainView, new MyLoginView());
navigator.navigateTo(MainView);

以下是两个类的图示表示:

如何做...

  1. 扩展 Vaadin servlet 以捕获请求对象。

MyVaadinServlet

public class MyVaadinServlet extends VaadinServlet {
  @Override
  protected void service(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException {
  SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
  VaadinRequestHolder.setRequest(request);
  super.service(request, response);
  VaadinRequestHolder.clean();
  SecurityContextHolder.clearContext();
  }
}

Vaadin servlet 在web.xml文件中进行配置。它接受 UI 类作为参数。在前面的代码中,我们扩展了 Vaadin servlet 并重写了service()方法,在其中我们将请求传递给VaadinRequestHolder类。通过这样做,我们将上下文对象传递给SecurityContextHolder以开始认证。

如何做...

  1. 在 UI 类中注册视图。

Vaadin_project5UI

@SuppressWarnings("serial")
@Theme("vaadin_project5")
public class Vaadin_project5UI extends UI{
  private Layout mainLayout;
  Navigator navigator;
  protected static final String CountView = "main";
  @Override
  protected void init(VaadinRequest request) {
    getPage().setTitle("Navigation Example");
    // Create a navigator to control the views
    navigator = new Navigator(this, this);
    // Create and register the views
    navigator.addView("", new MyLoginView());
    navigator.addView(CountView, new SecuredView());
  }
}

在此代码中,我们注册了LoginViewSecuredView,默认登录视图将被调用。

  1. 配置web.xml文件:
<display-name>Vaadin_Project5</display-name>
<context-param>
  <description>
  Vaadin production mode</description>
  <param-name>productionMode</param-name>
  <param-value>false</param-value>
</context-param>
<servlet>
 <servlet-name>Vaadin_project5 Application</servlet-name>
 <servlet-class>com.example.vaadin_project5.MyVaadinServlet</servlet-class>
 <init-param>
 <description>
 Vaadin UI class to use</description>
 <param-name>UI</param-name>
 <param-value>com.example.vaadin_project5.Vaadin_project5UI</param-value>
 </init-param>
 <init-param>
 <description>
 Legacy mode to return the value of the propertyas a string from AbstractProperty.toString()</description>
 <param-name>legacyPropertyToString</param-name>
 <param-value>false</param-value>
 </init-param>
</servlet>
<servlet-mapping>
 <servlet-name>Vaadin_project5 Application</servlet-name>
 <url-pattern>/*</url-pattern>
</servlet-mapping>
<listener>
 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

我们在web.xml中配置了MyVaadinServlet

  1. 编辑application-Context.xml文件。
<global-method-security pre-post-annotations="enabled" />
<authentication-manager>
  <authentication-provider>
    <user-service>
    <user name="anjana" password="123456"authorities="ROLE_EDITOR" />
    </user-service>
  </authentication-provider>
</authentication-manager>
<beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
  <beans:property name="invalidateHttpSession"value="false" />
</beans:bean>
</beans:beans>

它是如何工作的...

在这个例子中,我们创建了一个 Vaadin 登录表单。如果开发人员不想使用外部 jsp,这是使用 Vaadin 框架类创建登录表单的另一个选项。这将使它成为一个纯 Vaadin 应用程序,其中包含一个 Spring Security 应用程序。在显示实际的产品目录页面之前,用户会通过 Spring Security 进行身份验证和授权。Vaadin 表单将用户的凭据提交给 Spring Security 框架,进行身份验证和授权。MyVaadinServlet类与 Spring Security 上下文通信,以在 Vaadin 应用程序中设置安全上下文。

Spring Security 与 Vaadin 的工作流程如下所示:

  • 运行 Tomcat 服务器。

  • 现在访问 URL:

http://localhost:8086/Vaadin_Project5/

以下截图显示了 Vaadin 登录表单:

工作原理...

它还会显示有关错误凭据的消息:

工作原理...

身份验证后,您将被导航到受保护的页面:

工作原理...

单击注销,您将被带回登录视图。以下截图显示了信息:

工作原理...

第七章:使用 Wicket 的 Spring Security

在本章中,我们将涵盖:

  • Spring Security 与 Wicket - 基本数据库身份验证

  • Spring Security 与 Wicket - Spring 基于表单的数据库身份验证

  • Spring Security 与 Wicket - 自定义 JSP 基于表单的数据库身份验证

  • 使用 Wicket 授权的 Spring 身份验证

  • 使用 Wicket 和 Spring Security 的多租户

介绍

在启动 Wicket 之前,我们正在检查可用版本。最新版本是 6.9。在 Apache Wicket 网站上明确指出,最新项目应该使用版本 6.9 作为基础。我们在下载了 NetBeans 7.1 后,发现 NetBeans Wicket 插件支持 Wicket 的 1.5 版本。

我们更喜欢使用最新的稳定版本;它将有许多错误修复和升级,并且将更容易开发。

Wicket 还使用Wicket 过滤器来分派请求和响应。就像 GWT 和 Vaadin 应用程序一样,它们有 servlet,期望一些参数,如 UI 类来初始化,我们需要提供一个扩展Web Application类的类名作为过滤器的参数。然后有一些类,它们扩展了WebPage类。创建一个与扩展WebPage类相同名称的 HTML 页面是一个很好的惯例和实践。

Wicket 使用多级继承方法。我们必须扩展Wicket类以实现各种场景。它还具有内置的身份验证和授权 API。

设置数据库

以下代码将设置数据库:

CREATE TABLE `users1` (
  `USER_ID` INT(10) UNSIGNED NOT NULL,
  `USERNAME` VARCHAR(45) NOT NULL,
  `PASSWORD` VARCHAR(45) NOT NULL,
  `ENABLED` tinyint(1) NOT NULL,
  PRIMARY KEY (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_roles` (
  `USER_ROLE_ID` INT(10) UNSIGNED NOT NULL,
  `USER_ID` INT(10) UNSIGNED NOT NULL,
  `AUTHORITY` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`USER_ROLE_ID`),
  KEY `FK_user_roles` (`USER_ID`),
  CONSTRAINT `FK_user_roles` FOREIGN KEY (`USER_ID`) REFERENCES `users` (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

设置 Wicket 应用程序

以下语句是需要执行的 Maven 命令。您应该在您的机器上安装 Maven,并且应该有一个本地存储库。默认情况下,它在.m2\repository中。运行命令后,您应该获得构建成功的信号,这将让我们开始 Wicket 实现:

mvn archetype:generate -DarchetypeGroupId=org.apache.wicket -DarchetypeArtifactId=wicket-archetype-quickstart -DarchetypeVersion=6.9.1 -DgroupId=com.packt -DartifactId=spring-security-wicket -DarchetypeRepository=https://repository.apache.org/ -DinteractiveMode=false

在命令提示符上可见以下输出:

[INFO] Parameter: groupId, Value: com.packt
[INFO] Parameter: artifactId, Value: spring-security-wicket
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.packt
[INFO] Parameter: packageInPathFormat, Value: com/packt
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.packt
[INFO] Parameter: groupId, Value: com.packt
[INFO] Parameter: artifactId, Value: spring-security-wicket
[INFO] project created from Archetype in dir: E:\spring-security-wicket
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1:22.610s
[INFO] Finished at: Mon Jul 15 21:17:24 IST 2013
[INFO] Final Memory: 7M/13M
[INFO] ------------------------------------------------------------------------

以下命令将完成 Wicket 的完整设置。它们还将下载 Wicket 框架源文件到存储库中。

Spring-security-wicket>mvn clean compile install
Spring-security-wicket>mvn tomcat:run
Spring-security-wicket>mvn eclipse: eclipse

访问以下 URL:

http://localhost:8080/spring-security-wicket/

该 URL 将显示 Wicket 应用程序的欢迎页面。Wicket 应用程序设置已准备就绪。

Wicket 还配备了自己的身份验证和授权 API。让我们看看如何使用它。

Spring Security 与 Wicket - 基本数据库身份验证

我们的目标是在 Wicket 应用程序上进行简单的基本身份验证。当我们访问 Wicket 应用程序的 URL 时,我希望出现登录对话框。成功后,它应该重定向到主页。我们需要向pom.xml文件添加 Spring Security 依赖项并重新构建 Wicket 应用程序。下一步将是在web.xml文件中配置 spring 监听器。我们还需要添加applicationContext.xml文件。

准备工作

  • 使用 Spring 依赖项更新pom.xml文件。

  • 创建一个applicationContext.xml文件。必须将其命名为applicationContext,否则我们将在控制台中收到错误消息。

  • 使用 Spring 监听器编辑web.xml

  • 创建一个database-details.xml文件并添加数据库详细信息。

  • db-details.xml文件添加为context-param到 spring 监听器。

如何做...

以下是使用 Wicket 实现 Spring Security 以演示基本身份验证的步骤,其中凭据存储在数据库中:

  1. POM.xml文件添加依赖项:
<!-- Spring dependecncies -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <!-- Spring Security -->
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <!-- WICKET DEPENDENCIES -->
  <dependency>
    <groupId>org.apache.wicket</groupId>
    <artifactId>wicket-core</artifactId>
    <version>${wicket.version}</version>
  </dependency>
  <!-- WICKET Authentication-DEPENDENCIES -->
  <dependency>
    <groupId>org.apache.wicket</groupId>
    <artifactId>wicket-auth-roles</artifactId>
    <version>6.9.1</version>
  </dependency>
  1. 使用 Spring 监听器和 Spring 过滤器更新Web.xml文件与 Wicket 过滤器:
<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.web.context.ContextLoaderListener
    </listener-class>
</listener>

<filter>
  <filter-name>wicket.spring-security-wicket</filter-name>
<filter-class>
  org.apache.wicket.protocol.http.WicketFilter</filter-class>
  <init-param>
    <param-name>applicationClassName</param-name>
    <param-value>com.packt.WicketApplication</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>wicket.spring-security-wicket</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
  1. 编辑applicationContext.xml文件:
<global-method-security pre-post-annotations="enabled" />

<http auto-config="true">
  <intercept-url pattern="/spring-security-wicket/**" 
    access="ROLE_SELLER"/> 
  <intercept-url pattern="/spring-security-wicket/*.*" 
    access="ROLE_SELLER"/> 
  <intercept-url pattern="/**"access="ROLE_SELLER" />
  <http-basic />
</http>

<authentication-manager>
  <authentication-provider>
    <jdbc-user-service data-source-ref="MySqlDS" 
      users-by-username-query=" 
      select username,password, enabled   
      from users1 where username=?"  
      authorities-by-username-query=" 
      select u.username, ur.role from users1 u,user_roles ur  
    where u.user_id = ur.user_id and u.username =?  " />
  </authentication-provider>
</authentication-manager>

这是一个简单的基本身份验证配置。通过此配置,我们期望在显示 Wicket 应用程序之前出现登录对话框。我创建了一个新角色,卖家。

它是如何工作的...

现在访问以下 URL:

http://localhost:8080/spring-security-wicket/

这是将 Spring Security 与 Wicket 集成的初始设置示例。我们已经演示了基本的身份验证机制。通过登录表单,Spring Security 中断对 Wicket 应用程序的访问。成功认证后,用户将获得对 Wicket 应用程序的访问权限。

显示的页面如下截图所示:

它是如何工作的...它是如何工作的...

另请参阅

  • 使用 Wicket 的 Spring Security-Spring 基于表单的身份验证

  • 使用 Wicket 的 Spring Security-定制的 JSP 基于表单的身份验证

  • 使用 Wicket 授权的 Spring 身份验证

  • 使用 Wicket 和 Spring Security 的多租户

使用 Wicket 的 Spring Security-Spring 基于表单的数据库身份验证

在我们之前的示例中,我们发现 Wicket 6.9 与 Spring Security 非常兼容,并且很容易集成。我们所做的就是添加 spring 依赖项并配置applicationContext.xml文件。

在本节中,我们将使用 Spring 表单进行身份验证。我们期望 Spring 表单出现在对话框的位置,并为我们进行身份验证。

准备工作

  • 创建一个 Maven Wicket 项目:spring-security-wicket_springform

  • 使用 Spring 依赖项更新pom.xml文件。

  • 创建一个applicationContext.xml文件。必须将其命名为applicationContext,否则我们将在控制台中收到错误消息。

  • 编辑web.xml,使用 Spring 监听器。

  • 创建一个数据库details.xml文件,并添加数据库详细信息。

  • 将文件添加为 Spring 监听器的上下文参数。

如何做...

使用以下代码编辑applicationContext.xml文件:

<global-method-security pre-post-annotations="enabled" />

<http auto-config="true">
  <intercept-url pattern="/spring-security-wicket/**" 
    access="ROLE_SELLER"/> 
  <intercept-url pattern="/spring-security-wicket/*.*" 
    access="ROLE_SELLER"/> 
  <intercept-url pattern="/**" access="ROLE_SELLER" />
</http>

<authentication-manager> 
  <authentication-provider> 
    <jdbc-user-service data-source-ref="MySqlDS" 
    users-by-username-query=" 
    select username,password, enabled   
    from users1 where username=?"  

    authorities-by-username-query=" 
    select u.username, ur.role from users1 u, user_roles ur  
    where u.user_id = ur.user_id and u.username =?  " /> 
  </authentication-provider>
</authentication-manager>

这是一个简单的表单身份验证配置。使用此配置,我们期望在显示 Wicket 应用程序之前有一个登录页面。唯一的变化是我们已经删除了先前应用程序的<http-basic>标签。还要注意 URL,它将具有会话 ID。

它是如何工作的...

现在访问以下 URL:

http://localhost:8080/spring-security-wicket_springform/

在这个示例中,我们展示了如何在 Wicket 应用程序中调用 Spring 的内部登录表单。当我们访问 Wicket 应用程序时,我们将被重定向到 Spring 自己的登录页面。用户输入他们的用户名和密码,这将由 Spring 的身份验证提供者进行验证和授权。成功后,用户将获得对 Wicket 应用程序的访问权限。

当您访问上述 URL 时,您应该看到以下屏幕:

它是如何工作的...

另请参阅

  • 使用 Wicket 的 Spring Security-定制的 JSP 基于表单的身份验证

  • 使用 Wicket 授权的 Spring 身份验证

  • 使用 Wicket 和 Spring Security 进行多租户

使用 Wicket 的 Spring Security-定制的 JSP 基于表单的数据库身份验证

前两个示例是为了测试 Wicket 与 Spring Security 的兼容性。它还演示了将 Spring 与 Wicket 集成的简单性。我们从我们的两个 Wicket 示例中学到,我们可以很容易地使用基于 Spring 和基于表单的身份验证与数据库,并且同样可以扩展到 LDAP。

在这个示例中,我们将添加一个定制的 JSP 表单。我们期望 Wicket 应用程序调用我们的 JSP 表单进行登录。如果开发人员不想创建一个 Wicket 表单,他们可以使用这种方法。这种方法也适用于 GWT 和 Vaadin。

您还需要为登录页面提供匿名访问权限。

准备工作

  • 创建一个 Maven Wicket 项目:spring-security-wicket_customized_jsp

  • 使用 Spring 依赖项更新pom.xml文件。

  • 创建一个applicationContext.xml文件。必须将其命名为applicationContext,否则我们将在控制台中收到错误消息。

  • 编辑web.xml,使用 Spring 监听器。

  • 还要将login.jsp 配置添加为web.xml中的 servlet。

  • 创建一个数据库,details.xml文件,并添加数据库详细信息。

  • 将文件添加为 Spring 监听器的上下文参数。

  • 此外,您需要添加一个login.jsp;您可以使用上一章中使用的login.jsp文件。

操作步骤...

以下步骤是为了将 Spring Security 与 Wicket 框架集成,以演示使用自定义 JSP 的基于表单的身份验证:

  1. 编辑applicationContext.xml文件:
<global-method-security pre-post-annotations="enabled" />

<http auto-config='true'>
  <intercept-url pattern="/jsp/login*" 
    access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <intercept-url pattern="/jsp/login_error*" 
    access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <intercept-url pattern="/**" access="ROLE_SELLER" />
  <form-login login-page='/jsp/login' 
    authentication-failure-url="/jsp/login_error" />
</http> 
<authentication-manager> 
  <authentication-provider> 
    <jdbc-user-service data-source-ref="MySqlDS" 
    users-by-username-query=" 
    select username,password, enabled   
    from users1 where username=?"  

    authorities-by-username-query=" 
    select u.username, ur.role from users1 u, user_roles ur  
    where u.user_id = ur.user_id and u.username =?  " /> 
  </authentication-provider>
</authentication-manager>

login.jsp已在applicationContext.xml文件中配置为匿名用户。

  1. 编辑web.xml文件:
<servlet>
  <servlet-name>login</servlet-name>
  <jsp-file>/jsp/login.jsp</jsp-file>
</servlet>

<servlet>
  <servlet-name>login_error</servlet-name>
  <jsp-file>/jsp/login_error.jsp</jsp-file>
</servlet>

<servlet-mapping>
  <servlet-name>login</servlet-name>
  <url-pattern>/jsp/login</url-pattern>
</servlet-mapping>

<servlet-mapping>
  <servlet-name>login_error</servlet-name>
  <url-pattern>/jsp/login_error</url-pattern>
</servlet-mapping>

login.jsp已配置为一个 servlet。

工作原理...

现在访问以下 URL:

http://localhost:8080/spring-security-wicket_springform/

在这个示例中,我们将 Wicket 应用与我们自己的login.jsp文件集成,以进行身份验证和授权。当用户尝试访问 Wicket 应用时,Spring Security 会阻止用户访问提供在applicationContext.xml中创建和配置的 jsp 页面的应用。提交后,将触发 Spring Security 身份验证操作,进行身份验证和授权。成功后,用户将获得访问 Wicket 应用的权限。

访问此 URL 时,您应该看到以下屏幕截图:

工作原理...

另请参阅

  • 使用 Wicket 授权的 Spring 身份验证示例

  • 使用 Wicket 和 Spring Security 进行多租户示例

使用 Wicket 授权的 Spring 身份验证

到目前为止,我们已经看到了在 Wicket 应用之外使用 Spring Security 的各种选项。现在我们将看到如何在 wicket 框架中创建安全表单,并在 Spring 框架中使用它来实现两种不同的角色。该示例还演示了如何在 Wicket 应用中使用 Spring bean。

准备工作完成。

  • 创建一个 Maven Wicket 项目:spring-security-wicket

  • 使用 Spring 依赖项更新pom.xml文件。

  • 创建applicationContext.xml文件。必须将其命名为applicationContext,否则将在控制台中收到错误消息。

  • 添加一个spring-wicket-security依赖项。

  • 使用 Spring 监听器编辑web.xml

  • 分别创建EditorPage.htmlAuthorPage.html以及相应的EditorPage.javaAuthorPage.java。作者页面和编辑页面是相似的页面,但根据角色调用。

  • 创建HomePage.javaHomePage.html

  • 创建SignInPage.htmlSignInPage.java

  • 子类化AuthenticatedWebSession类,并覆盖超类中的方法。默认情况下,它使用 Wicket 身份验证,因此覆盖它以使用 Spring 身份验证。

操作步骤...

  1. 下一步是使用 Spring 安全进行身份验证和使用 spring Wicket 进行授权,编辑application-Context.xml
<!-- Enable annotation scanning -->
<context:component-scan base-package="com.packt.wicket" />

</beans>
  1. 编辑spring-wicket-security.xml文件:
<security:authentication-manager alias="springauthenticationManager">
  <security:authentication-provider>
<!--  TODO change this to reference a real production environment user service -->
    <security:user-service>
      <security:user name="jimmy" password="jimmy" authorities="ROLE_EDITOR, ROLE_AUTHOR"/>
      <security:user name="tommy" password="tommy" authorities="ROLE_EDITOR"/>
    </security:user-service>
  </security:authentication-provider>
</security:authentication-manager>

<security:global-method-security secured-annotations="enabled" />
  1. 编辑AuthorPage.java文件:
@AuthorizeInstantiation("ROLE_AUTHOR")
public class AuthorPage extends WebPage {

  @SpringBean
  private SomeInterfaceImpl someInterfaceImpl;

  public AuthorPage(final PageParameters parameters) {
    super(parameters);
    add(new Label("msg", someInterfaceImpl.method1()));
    add(new Link("Editor"){
      @Override
      public void onClick() {
        Page next = new EditorPage();
        setResponsePage(next);
      }
    });
    add(new Link("Logout"){
      @Override
      public void onClick() {
        getSession().invalidate();
        Page next = new HomePage(parameters);
        setResponsePage(next);
      }
    });
  }
}
  1. 编辑SigInPage.java文件:
public final class SignInPage extends WebPage
{
  /**
  * Constructor
  */
  public SignInPage()
  {
    final SignInForm form = new SignInForm("signinForm");
    add(form);
  }

  /**
  * Sign in form
  */
  public final class SignInForm extends Form<Void>
  {
    private String username;
    private String password;

    public SignInForm(final String id)
    {
      super(id);
      setModel(new CompoundPropertyModel(this));
      add(new RequiredTextField("username"));
      add(new PasswordTextField("password"));
      add(new FeedbackPanel("feedback"));
    }

    @Override
    public final void onSubmit()
    {
      MyWebSession session = getMySession();
      if (session.signIn(username,password))
      {

        setResponsePage(getApplication().getHomePage());

      }
      else
      {
        String errmsg = getString("loginError", null,
           "Unable to sign you in");

      }
    }
    private MyWebSession getMySession()
    {
      return (MyWebSession)getSession();
    }
  }
}
  1. 编辑HomePage.java文件:
public class HomePage extends WebPage {
  private static final long serialVersionUID = 1L;
  @SpringBean
  private SomeInterfaceImpl someInterfaceImpl;
  public HomePage(final PageParameters parameters) {
    super(parameters);
    add(new Label("version", getApplication()
      .getFrameworkSettings().getVersion()));
    add(new Label("msg", someInterfaceImpl.method1()));
    add(new Link("click if you are Editor"){
      @Override
      public void onClick() {
        Page next = new EditorPage();
        setResponsePage(next);
      }
    });

    add(new Link("Click if You are Author"){
      @Override
      public void onClick() {
        Page next = new AuthorPage(parameters);
        setResponsePage(next);
      }
    });

  }

}
  1. 编辑MyWebSession.java文件:
public class HomePage extends WebPage {
  private static final long serialVersionUID = 1L;
  @SpringBean
  private SomeInterfaceImpl someInterfaceImpl;
  public HomePage(final PageParameters parameters) {
    super(parameters);
    add(new Label("version", getApplication()
      .getFrameworkSettings().getVersion()));
    add(new Label("msg", someInterfaceImpl.method1()));
    add(new Link("click if you are Editor"){
      @Override
      public void onClick() {
        Page next = new EditorPage();
        setResponsePage(next);
      }
    });

    add(new Link("Click if You are Author"){
      @Override
      public void onClick() {
        Page next = new AuthorPage(parameters);
        setResponsePage(next);
      }
    });

  }

}

工作原理...

实现非常简单;我们需要做的就是拥有一个 Wicket 登录表单。单击提交后,我们需要获得经过身份验证的会话,这种方法将为我们提供一个选项,将 Spring 安全集成到我们使用 Wicket 应用创建的登录表单中。成功后,Spring 将验证用户凭据,并与 Wicket 框架通信以显示相应的授权页面。

Wicket 应用与 Spring 安全集成的工作流程如下所述。

当用户单击 URL:http://localhost:8080/spring-security-wicket/时,允许用户访问主页。主页显示两个链接,表示两个不同的角色和用户。成功验证后,用户将被授权使用基于角色的相应页面。这些页面显示在以下屏幕截图中:

工作原理...

应用启动时的主页

工作原理...

登录页面

工作原理...

作者页面

工作原理...

另请参阅

  • 使用 Wicket 和 Spring Security 实现多租户

使用 Wicket 和 Spring Security 实现多租户

多租户已成为云中的流行词。在多租户设置中,每个租户将有一个单独的数据源。我们需要为数据源创建两个不同的数据源和查找。让我们使用一个简单的 Wicket 应用程序和一个自定义的 JSP,其中将有一个租户下拉菜单。用户从下拉菜单中选择一个租户,将设置与租户对应的数据源。

我正在使用 NetBeans IDE,它可以轻松识别 Maven 项目。NetBeans 还带有 glassfish 应用服务器和 derby 数据库。

准备工作

  • 更新login.jsp文件

  • 使用 derby 数据库依赖更新pom.xml文件

  • 编辑applicationContext.xml

  • 编辑spring-security.xml

  • 编辑web.xml文件

  • 创建一个过滤器来捕获租户 ID

  • 还在 derby 中创建两个数据库

  • 在两个数据库中创建两个表USERSUSER_ROLES

  • USERSUSER_IDUSERNAMEPASSWORD)中添加列

  • USER_ROLESUSER_IDUSER_ROLE_IDAUTHORITY)中添加列

如何做...

以下步骤用于在 Wicket 应用程序中使用 Spring Security API 实现多租户:

  1. application-Context.xml文件中编辑两个数据源:
<!-- Enable annotation scanning -->
<context:component-scan base-package="com.packt.wicket" />

  <bean id="derbydataSource" class="com.packt.wicket.TenantRoutingDataSource ">
    <property name="targetDataSources">
      <map>
        <entry key="Tenant1" value-ref="tenant1DataSource"/>
        <entry key="Tenant2" value-ref="tenant2DataSource"/>
      </map>
    </property>
  </bean>
 <bean id="tenant1DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
 <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
 <property name="url" value="jdbc:derby://localhost:1527/client1" />
 <property name="username" value="client1" />
 <property name="password" value="client1" />

 </bean>
<bean id="tenant2DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
 <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
 <property name="url" value="jdbc:derby://localhost:1527/client2" />
 <property name="username" value="client2" />
 <property name="password" value="client2" />

</bean>

  1. 编辑spring-wicket-security.xml文件,并添加ExceptionMappingAuthenticationFailureHandler bean 来捕获 SQL 异常:
<bean id="authenticationFailureHandler"
  class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
  <property name="exceptionMappings">
    <props>
      <prop key="org.springframework.security.authentication.BadCredentialsException">/jsp/login?error='badCredentials'</prop>
      <prop key="org.springframework.security.authentication.CredentialsExpiredException">/jsp/login?error='credentialsExpired'</prop>
      <prop key="org.springframework.security.authentication.LockedException">/jsp/login?error='accountLocked'</prop>
      <prop key="org.springframework.security.authentication.DisabledException">/jsp/login?error='accountDisabled'</prop>
      </props>
    </property>
  </bean>
  <security:http auto-config='true'>
    <security:intercept-url pattern="/jsp/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    <security:intercept-url pattern="/jsp/login_error*"access="IS_AUTHENTICATED_ANONYMOUSLY" />
    <security:intercept-url pattern="/**" access="ROLE_SELLER" />
    <security:form-login login-page='/jsp/login' authentication-failure-handler-ref="authenticationFailureHandler" />
  </security:http>
  <security:authentication-manager>
    <security:authentication-provider>
      <security:jdbc-user-service data-source-ref="derbydataSource"
          users-by-username-query=" select username,password,'true'as enabled from users where username=?"  

          authorities-by-username-query=" 
          select u.username as username, ur.authority as authority from users u, user_roles ur  
          where u.user_id = ur.user_id and u.username =?"
      /> 
    </security:authentication-provider>  
  </security:authentication-manager>

<security:global-method-security secured-annotations="enabled" />
  1. 编辑login.jsp文件:
Login here--customized---login page
<form action="/ /Multitenant-spring-security-
  wicket//j_spring_security_check" method="post">
  <table>
    <tr>
      <td>
        User
      </td>
      <td>
        <input name="j_username">
      </td>
    </tr>
    <tr>
      <td>
        Password
      </td>
      <td>
        <input type="password" name="j_password"/>
      </td>
    </tr>

    <tr><td><label>Tenant:&nbsp;</label></td><td> 
      <select style="width:146px" id="tenant" name="tenant">
      <option value="">Choose Tenant</option>
      <option value="Tenant1">Tenant 1</option>
      <option value="Tenant2">Tenant 2</option></select></td>
    </tr>
    <tr>
      <td>
        <input type="submit" value="login">
      </td>
    </tr>
  </table>
</form>
</div>
  1. 编辑TenantRoutingDataSource.java文件以将租户路由到不同的数据源。该类是 spring 的AbstractRoutingDataSource的子类。它用于设置数据源。

URL:docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSource.html

public class TenantRoutingDataSource extends AbstractRoutingDataSource {
  protected final Log logger = LogFactory.getLog(this.getClass());

  protected Object determineCurrentLookupKey() {

    String lookupKey = (String)ThreadLocalContextUtil.getTenantId();
    System.out.println(lookupKey+"------lookupKey");

    return lookupKey;
  }
}
  1. 编辑MultitenantFilter以捕获租户类型并设置数据源:
public void doFilter(ServletRequest request,
   ServletResponse response,FilterChain chain)
   throws IOException, ServletException {
  if (null == filterConfig) {
    return;
  }
  HttpServletRequest httpRequest = (HttpServletRequest)
     request;

  ThreadLocalContextUtil.clearTenant();
  if (httpRequest.getRequestURI()
    .endsWith(SPRING_SECURITY_LOGOUT_MAPPING)) {
    httpRequest.getSession()
      .removeAttribute(TENANT_HTTP_KEY);
  }

  String tenantID = null;
  if (httpRequest.getRequestURI()
    .endsWith(SPRING_SECURITY_CHECK_MAPPING)) {
    tenantID = request.getParameter(TENANT_HTTP_KEY);
    httpRequest.getSession().setAttribute
      (TENANT_HTTP_KEY, tenantID);
  } else {
    tenantID = (String) httpRequest.getSession()
      .getAttribute(TENANT_HTTP_KEY);
  }

  if (null != tenantID) {
    ThreadLocalContextUtil.setTenantId(tenantID);
    if (logger.isInfoEnabled()) logger.info
      ("Tenant context set with Tenant ID: " + tenantID);
    }

  chain.doFilter(request, response);
}

工作原理...

当用户尝试访问应用程序时,他们将被重定向到登录表单,在该表单中用户输入他们的用户名和密码并选择租户。这也可以是根据业务需求的公司名称或位置。根据所选的租户,Spring 设置认证提供程序。MultitenantFilterTenantRoutingDataSource类在threadLocalUtil中设置租户信息。用户使用租户数据源进行身份验证,并进入主页。

应用程序启动时的登录页面将如下截图所示:

工作原理...

登录页面

工作原理...

如果租户不存在,则出现异常

工作原理...

显示选择的错误凭证异常

第八章:使用 ORM 和 NoSQL DB 的 Spring 安全

在本章中,我们将涵盖:

  • Spring Security 与 Hibernate 一起使用@preAuthorize 注释

  • Spring Security 与 Hibernate 一起使用身份验证提供程序和@preAuthorize 注释

  • Spring Security 与 Hibernate 一起使用用户详细信息服务和 Derby 数据库

  • Spring Security 与 MongoDB

介绍

Spring 框架已经设计成可以轻松集成类似于 Mybatis、Hibernate 等 ORM 框架。Hibernate 教程非常详细,并且可以在 JBoss 网站上找到。Hibernate 为我们提供了数据持久性。

在本章中,我们将看到如何将 Spring Security 与 ORM 框架集成。我们还将将 Spring Security 与最新的 MongoDB 集成。

我们将首先进行一些与 Hibernate 和 Spring 相关的基本设置。由于本章涉及数据库相关内容,我们需要为本章中使用的所有食谱创建一个数据库。我正在使用带有 maven 的 NetBeans IDE。我觉得 NetBeans IDE 与其他 IDE 相比非常先进。

设置 Spring Hibernate 应用程序

我们将创建一个简单的恐怖电影应用程序,该应用程序将在 UI 中显示一系列恐怖电影,并具有一些CRUD创建、读取、更新和删除)功能。设置Spring Hibernate应用程序涉及以下步骤:

  1. 在 Derby 中创建一个horrormoviedb数据库。您可以使用 NetBeans。

  2. 单击服务选项卡,您将看到数据库

  3. 右键单击JavaDB以查看创建数据库...选项。选择创建数据库...选项。设置 Spring Hibernate 应用程序

  4. 在数据库horrormovie中创建一个表。设置 Spring Hibernate 应用程序

  5. 在表中创建列,并将列命名为horrormovie_idhorrormovie_namehorrormovie_director

  6. 创建一个 maven 项目,更新 POM 文件以包含 Spring、Hibernate、Derby 和 Spring Security 依赖项,并在 NetBeans IDE 中打开它。

  7. 使用@table@column注释创建实体类。

  8. 创建DAODAOImpl类来处理 Hibernate 操作。

  9. 创建ServiceServiceImpl类,以在DAO和 UI 之间充当中间管理器。

  10. 创建一个控制器来处理 UI 部分。

Spring Security 与 Hibernate 一起使用@preAuthorize 注释

在当前演示中,我们使用了两个不同的数据库。身份验证管理器配置为tenant1DataSource,它连接到一个 Derby 数据库,其中保存了用户和角色信息。使用此数据源,我们将进行身份验证和授权。

为显示horrormovie列表,我们在 Derby 中创建了另一个数据源,该数据源与 Hibernate 配置文件一起使用。

DAOImpl类的方法中,我们使用了@preAuthorize注释。

让我们使用 GlassFish 应用服务器来运行应用程序。

准备工作

  • 编辑application-security.xml

  • 编辑horrormovie-servlet.xml

  • DAOImpl中使用@preAuthorize注释。Spring Security 在调用方法时授权用户。

如何做...

以下步骤将使用 Hibernate 应用程序进行身份验证和授权:

  1. 使用数据源详细信息和 Bean 信息编辑application-security.xml文件。
<global-method-security pre-post-annotations="enabled" />

  <http auto-config="false"  use-expressions="true">
    <intercept-url pattern="/login" access="permitAll" />
    <intercept-url pattern="/logout" access="permitAll" />
    <intercept-url pattern="/accessdenied" access="permitAll" />
    <intercept-url pattern="/**"access="hasRole('ROLE_EDITOR')" />
    <form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied" />
    <logout logout-success-url="/logout" />
  </http>

  <authentication-manager alias="authenticationManager">
    <authentication-provider>
      <jdbc-user-service data-source-ref="tenant1DataSource"
        users-by-username-query=" select username,password ,'true' as enabled from users where username=?"  
        authorities-by-username-query=" 
        select u.username as username, ur.authority as authority from users u, user_roles ur  
        where u.user_id = ur.user_id and u.username =?"
        /> 
    </authentication-provider>
  </authentication-manager>

  <beans:bean id="horrorMovieDAO" class="com.packt.springsecurity.dao.HorrorMovieDaoImpl" />
  <beans:bean id="horrorMovieManager" class="com.packt.springsecurity.service.HorrorMovieManagerImpl" />
  <beans:bean id="tenant1DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <beans:property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
  <beans:property name="url" value="jdbc:derby://localhost:1527/client1" />
  <beans:property name="username" value="client1" />
  <beans:property name="password" value="client1" />

</beans:bean>
  1. 使用控制器信息编辑horrormovie-servlet.xml文件。
<global-method-security pre-post-annotations="enabled" />

  <http auto-config="true">
    <intercept-url pattern="/spring-security-wicket/**" access="ROLE_SELLER"/>
    <intercept-url pattern="/spring-security-wicket/*.*" access="ROLE_SELLER"/> 
    <intercept-url pattern="/**" access="ROLE_SELLER" />
  <http-basic />
</http>
<authentication-manager> 
  <authentication-provider> 
    <jdbc-user-service data-source-ref="MySqlDS" 
      users-by-username-query=" 
      select username,password, enabled   
      from users1 where username=?"  
      authorities-by-username-query=" 
      select u.username, ur.role from users1 u, user_roles ur  
      where u.user_id = ur.user_id and u.username =?  " /> 
  </authentication-provider>
</authentication-manager>

它使用 JDBC 进行身份验证服务。

  1. 在执行addHorrorMovie方法时使用注释,Spring 会检查安全上下文对象的凭据,并进行身份验证和授权;以下是代码:
@Repository
public class HorrorMovieDaoImpl implements HorrorMovieDAO  {

  @Autowired
  private SessionFactory sessionFactory;

  @PreAuthorize("hasRole('ROLE_AUTHOR')")
  @Override
  public void addHorrorMovie(HorrorMovieEntity horrormovie) {
    this.sessionFactory.getCurrentSession().save(horrormovie);
  }

  @SuppressWarnings("unchecked")
  @Override
  public List<HorrorMovieEntity> getAllHorrorMovies() {
    return this.sessionFactory.getCurrentSession().createQuery("from HORRORMOVIE").list();
  }

  @Override
  public void deleteHorrorMovie(Integer horrorMovieId) {
    HorrorMovieEntity horrorMovie = (HorrorMovieEntity)sessionFactory.getCurrentSession().load(HorrorMovieEntity.class, horrorMovieId);
    if (null != horrorMovie) {
      this.sessionFactory.getCurrentSession().delete(horrorMovie);
    }
  }
}
  1. 以下是一些 SQL 命令:
create table HORRORMOVIE
 (HORRORMOVIE_ID int generated by default as identity 
 (START WITH 2, INCREMENT BY 1),
 HORRORMOVIE_NAME char(50),HORRORMOVIE_DIRECTOR char(50));

insert into HORRORMOVIE values 
 (1, 'EVILDEAD','Fede Alvarez');
insert into HORRORMOVIE values 
 (DEFAULT, 'EVILDEAD2','Fede Alvarez');

它是如何工作的...

在这个例子中,我们创建了一个 Hibernate 应用程序,并使用了 JDBC 服务进行身份验证。Spring 框架中断了访问应用程序的请求,并要求用户输入凭据。使用application-security.xml文件中提供的 JDBC 详细信息对凭据进行验证。

成功后,用户将被重定向到显示电影列表的应用程序。

现在访问以下网址:

http://localhost:8080/login

使用 JDBC 服务进行身份验证和授权以及在方法上应用 Spring Security 的截图如下:

示例的工作流程显示在以下截图中:

它是如何工作的...它是如何工作的...

另请参阅

  • 使用身份验证提供程序的 Spring Security 与 Hibernate配方

  • 使用 Derby 数据库的用户详细信息服务的 Spring Security 与 Hibernate配方

  • 使用 MongoDB 的 Spring Security配方

使用身份验证提供程序和@preAuthorize 注释的 Spring Security 与 Hibernate

我们正在使用示例horrormovie应用程序来演示使用自定义身份验证提供程序和@preAuthorize注释的 Spring Security 与 Hibernate。

在这个配方中,我们将创建自己的自定义身份验证提供程序并实现接口身份验证提供程序。我们将在controller方法上应用注释,而不是在hibernate方法上。

准备工作

  • 创建一个实现AuthenticationProvider接口的新类,并将 Bean 定义添加到application-security.xml文件中

  • 编辑application-security.xml文件

  • 在控制器中使用@preAuthorize注释

如何做...

使用AuthenticationProvider接口实现 Spring Security 的以下步骤:

  1. 编辑application-security.xml文件,添加数据源详细信息和 Bean 信息。
<global-method-security pre-post-annotations="enabled" />

<http auto-config="false"  use-expressions="true">
  <intercept-url pattern="/login" access="permitAll" />
  <intercept-url pattern="/logout" access="permitAll" />
  <intercept-url pattern="/accessdenied" access="permitAll"/>
  <intercept-url pattern="/list" access="hasRole('ROLE_EDITOR')" />
  <intercept-url pattern="/add" access="hasRole('ROLE_EDITOR')" />
  <form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied" />
  <logout logout-success-url="/logout" />
</http>

  <authentication-manager alias="authenticationManager">
 <authentication-provider ref="MyCustomAuthenticationProvider" />
 </authentication-manager>

  <beans:bean id="horrorMovieDAO" class="com.packt.springsecurity.dao.HorrorMovieDaoImpl" />
  <beans:bean id="horrorMovieManager" class="com.packt.springsecurity.service.HorrorMovieManagerImpl"/>

 <beans:bean id="MyCustomAuthenticationProvider" class="com.packt.springsecurity.controller" />
</beans:beans>
  1. 编辑MyCustomAuthenticationProvider文件。
public class MyCustomAuthenticationProvider implements AuthenticationProvider {
  @Override
  public boolean supports(Class<? extends Object>authentication)
{
    return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

 private static Map<String, String> APP_USERS= new HashMap<String, String>(2);
 private static List<GrantedAuthority> APP_ROLES= new ArrayList<GrantedAuthority>();
 static
 {
 APP_USERS.put("ravi", "ravi123");
 APP_USERS.put("chitra", "chitra123");
 APP_ROLES.add(new SimpleGrantedAuthority("ROLE_EDITOR"));
 }

  @Override
  public Authentication authenticate(Authentication auth)
  {
 if (APP_USERS.containsKey(auth.getPrincipal())
 && APP_ROLES.get(auth.getPrincipal()).equals(auth.getCredentials()))
 {
 return new UsernamePasswordAuthenticationToken(auth.getName(), auth.getCredentials(),
 AUTHORITIES);
 }
 throw new BadCredentialsException("Username/Password does not match for "
      + auth.getPrincipal());
    }
  }
}
  1. 在控制器中使用注释。
AddHorrorMovieController
@PreAuthorize("hasRole('ROLE_EDITOR')")
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addHorrorMovie(
  @ModelAttribute(value = "horrorMovie") HorrorMovieEntity horrorMovie,
    BindingResult result) {
    horrorMovieManager.addHorrorMovie(horrorMovie);
    return "redirect:/list";
  }

它是如何工作的...

现在访问以下网址:

http://localhost:8080/login

在中断请求后,Spring Security 调用MyCustomAuthenticationProvider,该提供程序具有用于身份验证和用户信息的重写 authenticate 方法。用户凭据在APP_Users映射中进行验证和授权,成功验证和授权后,用户将被重定向到spring-security.xml文件中配置的成功 URL。

使用自定义身份验证提供程序进行身份验证和授权,并在控制器方法上应用 Spring Security 的截图如下:

它是如何工作的...它是如何工作的...

另请参阅

  • 使用@preAuthorize 注释的 Spring Security 与 Hibernate配方

  • 使用自定义身份验证提供程序和@preAuthorize 注释的 Spring Security 与 Hibernate配方

  • 使用 Derby 数据库的用户详细信息服务的 Spring Security 与 Hibernate配方

  • 使用 MongoDB 的 Spring Security配方

使用 Derby 数据库的 UserDetailsService 与 Spring Security 的 Hibernate

到目前为止,我们已经看到了使用各种身份验证提供程序的 Hibernate 和 Spring Security。在本节中,我们将使用 Hibernate 从数据库中检索用户和权限。

为此,我们将实现UserDetailsService接口并在接口中实现一个方法。首先,我们需要为用户和角色创建实体类。

我们还将@preAuthorize注释移到controller类中。

准备工作

  • 创建一个实现UserDetailsService接口的新类,并将 Bean 定义添加到application-security.xml文件中

  • 编辑application-security.xml文件

  • 在控制器中使用@preAuthorize注释

  • 在恐怖数据库中添加USERSUSER_ROLE

  • 插入角色ROLE_EDITOR和名为raviravi123的用户

如何做...

通过实现与 Hibernate 交互的UserDetailsService接口来集成 Spring Security 身份验证的以下步骤:

  1. 创建一个实现UserDetailsService接口的类MyUserDetailsService
public class MyUserDetails implements UserDetailsService {
  @Autowired
  private UsersDAO UsersDAO;
  public UserDetails loadUserByUsername(String userName)
  throws UsernameNotFoundException {

    Users users= UsersDAO.findByUserName(userName);
    boolean enabled = true;
    boolean accountNonExpired = true;
    boolean credentialsNonExpired = true;
    boolean accountNonLocked = true;
    return new User(
      users.getUserName(), 
      users.getUserPassword(), 
      enabled, 
      accountNonExpired, 
      credentialsNonExpired, 
      accountNonLocked,
      getAuthorities(users.getRole().getRoleId().intValue()));
    }

    public Collection<? extends GrantedAuthority>getAuthorities(Integer role) {
    List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(role));
    System.out.println("authList----------->"+authList);
    return authList;
  }

  public List<String> getRoles(Integer role) {

    List<String> roles = new ArrayList<String>();

    if (role.intValue() == 1) {
      roles.add("ROLE_EDITOR");
    } else if (role.intValue() == 2) {
      roles.add("ROLE_AUTHOR");
    }
    return roles;
  }

  public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
  List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
  for (String role : roles) {
    System.out.println("role----------->"+role);
    authorities.add(new SimpleGrantedAuthority(role));
  }
  return authorities;
  }

}
  1. 编辑application-security.xml文件。
<authentication-manager alias="authenticationManager">
  <authentication-provider user-service-ref="MyUserDetails">
    <password-encoder hash="plaintext" />
  </authentication-provider>
</authentication-manager>

<beans:bean id="horrorMovieDAO" class="com.packt.springsecurity.dao.HorrorMovieDaoImpl" />
<beans:bean id="horrorMovieManager" class="com.packt.springsecurity.service.HorrorMovieManagerImpl" />
<beans:bean id="UsersDAO" class="com.packt.springsecurity.dao.UsersDAOImpl" />
<beans:bean id="UsersManager" class="com.packt.springsecurity.service.UsersManagerImpl" />
<beans:bean id="UserRoleDAO" class="com.packt.springsecurity.dao.UserRoleDAOImpl" />
<beans:bean id="UserRoleManager" class="com.packt.springsecurity.service.UserRoleManagerImpl" />

<beans:bean id="MyUserDetails" class="com.packt.springsecurity.service.MyUserDetails" />
</beans:beans>
  1. 在控制器中使用注释。
@PreAuthorize("hasRole('ROLE_EDITOR')")
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addHorrorMovie(
  @ModelAttribute(value = "horrorMovie")HorrorMovieEntity horrorMovie,
  BindingResult result) {
    horrorMovieManager.addHorrorMovie(horrorMovie);
    return "redirect:/list";
  }

它是如何工作的...

现在访问以下 URL:

http://localhost:8080/login

首先使用UserDetailsService和 Hibernate 进行身份验证和授权。 UserDetailsService是 Spring Security 接口,由MyUserDetailsService类实现。该类在application-security.xml文件中进行配置,以便 Spring Security 调用此实现类使用 Hibernate 加载用户详细信息。 UsersDAO.findByUserName(userName)是调用 Hibernate 获取基于传递的用户名的用户信息的方法。

在使用注释将 Spring Security 应用于控制器之后,我们应该能够使用用户名和密码(ravi 和 ravi123)登录。 <password-encoder hash="plaintext" />是 Spring Security 支持的哈希算法。成功验证后,用户将被重定向到授权页面。

应用程序的工作流程在以下屏幕截图中演示:

工作原理...工作原理...

另请参阅

  • 使用@preAuthorize 注释的 Hibernate 的 Spring Security 配方

  • 使用自定义身份验证提供程序和@preAuthorize 注释的 Hibernate 的 Spring Security 配方

  • 使用 Derby 数据库的用户详细信息服务的 Spring Security 配方

  • 使用 MongoDB 的 Spring Security 配方

使用 MongoDB 的 Spring Security

在本节中,让我们看看 Spring Security 如何与 MongoDB 配合使用。 MongoDB 是一种流行的 NOSQL 数据库。它是一个基于文档的数据库。 MongoDB 是用流行的 C++数据库编写的,这使它成为一种面向对象的基于文档的数据库。在 MongoDB 中,查询也是基于文档的,它还提供使用 JSON 样式进行索引以存储和检索数据。最新的 Spring 版本是版本 3.2,已包含在 POC 中。

准备工作

  • 下载 MongoDB 数据库

  • 配置数据文件夹

  • 在命令提示符中启动 MongoDB

  • 在另一个命令提示符中启动 MongoDB

  • 通过向其中插入数据创建horrordb数据库

  • 执行命令use horrordb

  • 将 MongoDB 依赖项添加到 POM(项目对象模型)文件

  • 将 JSON 依赖项添加到 POM 文件

  • 将 Spring 版本升级到 3.2.0,将 Spring Security 升级到 1.4

  • 创建一个MongoUserDetails

  • 编辑horror-movie servlet

  • 编辑Application-security.xml文件

如何做...

以下步骤使用 Mongo 与 Spring Security 来实现UserDetailsService接口对用户进行身份验证和授权:

  1. 在命令提示符中显示数据库操作如下:
db.horrormovie.insert({horrormovie_id:1,horrormovie_name:
 "omen",horrormovie_director:"Richard Donner"})

db.horrormovie.insert({horrormovie_id:2,horrormovie_name:
 "the conjuring",horrormovie_director:"James Wan"})

db.horrormovie.insert({horrormovie_id:3,horrormovie_name:
 "The Lords of Salem",horrormovie_director:"Rob Zombie"})

db.horrormovie.insert({horrormovie_id:4,horrormovie_name:
 "Evil Dead",horrormovie_director: "Fede Alvarez"})

db.users.insert({id:1,username:"anjana",password:
 "123456",role:1})

db.users.insert({id:2,username:"raghu",password:
 "123456",role:2})

db.users.insert({id:3,username:"shami",password:
 "123456",role:3})

  1. 创建一个实现UserDetailsService接口的MongoUserDetailsService类。
@Service
public class MongoUserDetailsService implements UserDetailsService {

  @Autowired
  private UserManager userManager;
  private static final Logger logger = Logger.getLogger(MongoUserDetailsService.class);
  private org.springframework.security.core.userdetails.User userdetails;
  public UserDetails loadUserByUsername(String username)
  throws UsernameNotFoundException {
    boolean enabled = true;
    boolean accountNonExpired = true;
    boolean credentialsNonExpired = true;
    boolean accountNonLocked = true;
    Users users = getUserDetail(username);
    System.out.println(username);
    System.out.println(users.getPassword());
    System.out.println(users.getUsername());
    System.out.println(users.getRole());

    return new User(users.getUsername(), users.getPassword(),enabled,accountNonExpired,credentialsNonExpired,accountNonLocked,getAuthorities(users.getRole()));
  }

  public List<GrantedAuthority> getAuthorities(Integer role) {
    List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
      if (role.intValue() == 1) {
        authList.add(new SimpleGrantedAuthority("ROLE_EDITOR"));

      } else if (role.intValue() == 2) {
        authList.add(new SimpleGrantedAuthority("ROLE_AUTHOR"));
    }
    return authList;
  }

  public Users getUserDetail(String username) {
  Users users = userManager.findByUserName(username);
  System.out.println(users.toString());
  return users;
}
  1. 编辑application-security.xml
<global-method-security pre-post-annotations="enabled" />

<http auto-config="false"  use-expressions="true">
  <intercept-url pattern="/login" access="permitAll" />
  <intercept-url pattern="/logout" access="permitAll" />
  <intercept-url pattern="/accessdenied" access="permitAll" />
  <intercept-url pattern="/list" access="hasRole('ROLE_EDITOR')" />
<!--                <http-basic/>-->
  <form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied" />
  <logout logout-success-url="/logout" />
</http>

<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="mongoUserDetailsService">
<password-encoder hash="plaintext" />
</authentication-provider>
</authentication-manager>
  1. 编辑horrormovie-servlet.xml
<context:annotation-config />
<context:component-scan base-package="com.packt.springsecurity.mongodb.controller" />
<context:component-scan base-package="com.packt.springsecurity.mongodb.manager" />
<context:component-scan base-package="com.packt.springsecurity.mongodb.dao" />
<context:component-scan base-package="com.packt.springsecurity.mongodb.documententity" />

<bean id="jspViewResolver"
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="viewClass"
  value="org.springframework.web.servlet.view.JstlView" />
  <property name="prefix" value="/WEB-INF/view/" />
  <property name="suffix" value=".jsp" />
</bean>
<mongo:mongo host="127.0.0.1" port="27017" />
<mongo:db-factory dbname="horrordb" />

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>

<bean id="horrorMovieDAO" class="com.packt.springsecurity.mongodb.dao.HorrorMovieDaoImpl" />
<bean id="horrorMovieManager" class="com.packt.springsecurity.mongodb.manager.HorrorMovieManagerImpl" />
<bean id="UsersDAO" class="com.packt.springsecurity.mongodb.dao.UsersDAOImpl" />
<bean id="userManager" class="com.packt.springsecurity.mongodb.manager.UserManagerImpl" />
<bean id="mongoUserDetailsService" class="com.packt.springsecurity.mongodb.controller.MongoUserDetailsService" />

<bean id="HorroMovieController" class="com.packt.springsecurity.mongodb.controller.HorrorMovieController" />
  1. 在控制器中使用注释。
@PreAuthorize("hasRole('ROLE_EDITOR')")
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addHorrorMovie(
@ModelAttribute(value = "horrorMovie")HorrorMovieEntity horrorMovie,
  BindingResult result) {
  horrorMovieManager.addHorrorMovie(horrorMovie);
  return "redirect:/list";
}

工作原理...

首先使用MongoDetailsService和 Spring 数据进行身份验证和授权。 MongoDetailsServiceUserDetailsService的实现,getUserDetail(string username)调用springdata类从 Mongo 数据库中获取基于传递的用户名的用户凭据。如果根据用户名存在数据,则意味着身份验证成功。然后我们使用注释在控制器方法上应用 Spring Security。

现在我们应该能够使用用户名和密码(ravi 和 123456)登录。

现在访问以下 URL:

http://localhost:8080/login

工作流程在以下屏幕截图中演示:

工作原理...工作原理...

另请参阅

  • 使用@preAuthorize 注释的 Hibernate 的 Spring Security 配方

  • 使用自定义身份验证提供程序和@preAuthorize 注释的 Hibernate 的 Spring Security 配方

  • 使用 Derby 数据库的用户详细信息服务的 Spring Security 配方

  • 使用 MongoDB 的 Spring Security 配方

第九章:Spring Security 与 Spring Social

在本章中,我们将涵盖:

  • Spring Security 与 Spring Social 访问 Facebook

  • Spring Security 与 Spring Social 访问 Twitter

  • Spring Security 与多个身份验证提供程序

  • OAuth 的 Spring Security

介绍

Spring Social 是一个著名的 API。大多数 Web 应用程序希望为用户提供从其应用程序到社交网络站点(如 Facebook 和 Twitter)的发布选项。Spring Social 是为满足此要求而构建的。

在本章中,我们将集成 Spring Security 与 Spring Social 以连接到 Facebook 和 Twitter 帐户。

Spring Security 与 Spring Social 访问 Facebook

Spring Social 在身份验证方面使用spring-security API。我们需要在pom.xml中添加 spring-social 依赖项,以及spring-corespring-security包。在本节中,我们将演示 Spring Social 如何将我们的 Java 应用程序与 Facebook 连接起来。我们可以在我们的 Java 应用程序中登录到 Facebook 应用程序。

一旦与社交网络站点建立连接,用户就可以在其中发布和检索消息。

我们使用了相同的 hibernate 恐怖电影应用程序。我使用了 derby 数据库,并在 glassfish 服务器上部署了应用程序。Spring Social 内部使用 Spring 的jdbctemplate类来检索数据库信息。

准备工作

要使用 Spring Security 与 Spring Social 访问 Facebook,您需要执行以下任务:

  • 注册为 Facebook 开发人员并创建应用程序。您将获得可用于集成的 appID 和秘钥

  • 将请求映射添加到控制器以处理 Facebook 创建的jsp页面以将消息发布到 Facebook

  • 创建UserConnection

  • 将 Jackson 依赖项添加到您的pom.xml文件中。演示项目将随本书提供下载

  • 添加 Spring Social 依赖项,如:

  • Spring-social-core

  • Spring-social-web

  • Spring-social-facebook

  • Spring-social-twitter

  • Spring-social-linkedin

  • Spring-social-github

  • 为用户登录和注销创建.jsp页面

  • spring.properties文件中提供数据库连接属性

  • jdbc.properties文件中提供 Facebook 的应用程序秘钥和 appID

如何做...

以下是实现允许用户使用 Spring Social 和 Spring Security 登录到 Facebook 应用程序的应用程序的步骤:

  1. 创建名为MyController的控制器来处理 Facebook 页面。
  @RequestMapping(value = "/fbprofile", method = RequestMethod.GET)
  public String getfbProfile(ModelMap model,HttpServletRequest request, 
      HttpServletResponse response) {
    model.addAttribute("request.userPrincipal.name", request.getUserPrincipal().getName());
    Facebook facebook = connectionRepository.getPrimaryConnection(Facebook.class).getApi();
    model.addAttribute("profileLink", facebook.userOperations().getUserProfile().getLink());
    model.addAttribute("Gender", facebook.userOperations().getUserProfile().getGender());
    model.addAttribute("profileInfo", facebook.userOperations().getUserProfile());
    model.addAttribute("userpermissions", facebook.userOperations().getUserPermissions());
    List<Reference> friends = facebook.friendOperations().getFriends();
    model.addAttribute("friends", friends);
    model.addAttribute("friendlist", facebook.friendOperations().getFriendLists());
    return "facebookprofile";
  }
  1. Spring-social.xml文件中提供连接工厂:
  <bean id="connectionFactoryLocator" class="org.springframework.social.connect.support.ConnectionFactoryRegistry">
    <property name="connectionFactories">
      <list>
        <bean class="org.springframework.social.facebook.connect.FacebookConnectionFactory">
          <constructor-arg value="${facebook.clientId}" />
          <constructor-arg value="${facebook.clientSecret}" />
        </bean>
      </list>
    </property>
  </bean>

ConnectionFactory定位器创建了 Facebook bean。在这里,您可以添加其他社交网络提供商,如 Digg 和 Flickr。UsersConnectionRepository使用 JDBC 模板执行与各种社交网络提供商的连接查询。

  1. spring-social.xml文件中使用连接工厂:
  <bean id="textEncryptor" class="org.springframework.security.crypto.encrypt.Encryptors" factory-method="noOpText" />
  <bean id="usersConnectionRepository" class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository">
    <constructor-arg ref="mydataSource" />
    <constructor-arg ref="connectionFactoryLocator" />
    <constructor-arg ref="textEncryptor" />
  </bean>
  <bean id="connectionRepository" factory-method="createConnectionRepository" factory-bean="usersConnectionRepository" scope="request">
      <constructor-arg value="#{request.userPrincipal.name}" />
      <aop:scoped-proxy proxy-target-class="false"/>
  </bean>
  1. spring-social文件中配置ConnectController类。ConnectController类在连接到提供程序时起着重要作用。它与(/connect) URL 映射。为了充分利用ConnectController类,为 Facebook 和 Twitter 创建单独的文件夹。
  <bean class="org.springframework.social.connect.web.ConnectController"
    p:applicationUrl="${application.url}"/>
  1. 在您的 derby 数据库中运行 SQL 命令。
create table UserConnection (userId varchar(255) not null,
  providerId varchar(255) not null,
  providerUserId varchar(255),
  rank int not null,
  displayName varchar(255),
  profileUrl varchar(512),
  imageUrl varchar(512),
  accessToken varchar(255) not null,
  secret varchar(255),
  refreshToken varchar(255),
  expireTime bigint,
  primary key (userId, providerId, providerUserId));

create unique index UserConnectionRank on UserConnection(userId, providerId, rank);

它是如何工作的...

Spring Social 使用UserConnection表存储网络站点提供程序信息以及用户信息。Spring Social 使用 Spring Security 以及 appID 和秘钥对用户进行身份验证。

访问 URL:http://localhost:8080/horrormovie/list

您将被重定向到http://localhost:8080/horrormovie/login;jsessionid=581813e14c1752d2260521830d3d

使用用户名和密码登录。您将连接到horromovie数据库,如下截图所示:

它是如何工作的...

单击连接到 Facebook 个人资料链接,用户将被重定向到以下网页:

它是如何工作的...

该页面显示以下字段:

  • 个人资料链接

  • 性别

  • 个人资料信息

  • 发布消息到 Facebook 的文本框

您可以从此应用程序发布消息,然后打开 Facebook 个人资料以查看已发布的消息。该消息将以您创建的 Facebook 应用程序的名称发布。

另请参阅

  • 使用 Spring Social 访问 Twitter 的 Spring 安全示例

  • 使用多个身份验证提供程序的 Spring 安全示例

  • 使用 OAuth 进行 Spring 安全示例

使用 Spring Social 访问 Twitter 的 Spring 安全

我们刚刚连接了 Facebook 并能够发布消息。在本节中,我们将看到如何连接 Twitter。让我们使用与 Facebook 相同的应用程序,该应用程序使用 derby 数据库和 hibernate 身份验证服务。

准备工作

您需要执行以下任务,以使用 Spring Social 和 Spring Security 访问 Twitter:

  • 创建一个 Twitter 应用程序:dev.twitter.com/apps/new

  • 将消费者 ID 和密钥添加到.properties文件中。

  • 更新控制器以处理 Twitter 请求

  • 创建 JSP 文件以访问和显示 Twitter 对象

如何做...

以下是在上一节演示的应用程序中实现 Twitter 登录选项的步骤:

  1. 更新名为HorrorMovie Controller的控制器以处理 Twitter 请求。
< @RequestMapping(value = "/posttofb", method = RequestMethod.GET)
  public String posttofb(String message, ModelMap model) {
    try {
      Facebook facebook = connectionRepository.getPrimaryConnection(Facebook.class).getApi();
      facebook.feedOperations().updateStatus(message);
      model.addAttribute("status", "success");
      model.addAttribute("message", message);
      return "redirect:/list";
    } catch (Exception e) {
      model.addAttribute("status", "failure");
      return "/facebook/fbconnect";
    }
  }
  @RequestMapping(value = "/twprofile", method = RequestMethod.GET)
  public String gettwProfile(ModelMap model) {
    try{
      Twitter twitter = connectionRepository.getPrimaryConnection(Twitter.class).getApi();
      model.addAttribute("twprofileLink", twitter.userOperations().getUserProfile().getUrl());
      model.addAttribute("twprofileInfo", twitter.userOperations().getUserProfile());
      model.addAttribute("twfollowers", twitter.friendOperations().getFollowers());
      model.addAttribute("twfriends", twitter.friendOperations().getFriends());
      return "/twitter/twitterprofile";
    } catch (Exception e) {
      model.addAttribute("status", "failure");
      return "/twitter/twconnect";
    }
  }
  @RequestMapping(value = "/posttotw", method = RequestMethod.GET)
  public String posttotw(String message, ModelMap model) {
    try {
      Twitter twitter = connectionRepository.getPrimaryConnection(Twitter.class).getApi();
      twitter.timelineOperations().updateStatus(message);
      model.addAttribute("status", "success");
      model.addAttribute("message", message);
      return "redirect:/list";
    } catch (Exception e) {
      model.addAttribute("status", "failure");
      return "/twitter/twconnect";
    }
  }

工作原理...

访问 URL:http://localhost:8080/horrormovie/list.

Spring Social 将检查用户是否已连接到 Twitter。如果用户已经连接,用户将被重定向到 Twitter 页面并被要求登录。Spring Social 使用 Twitter 消费者 ID 和密钥与 Spring Security 一起从应用程序登录到 Twitter 帐户。这是大多数手机应用程序允许我们登录到 Twitter 和 Facebook 的基础。

另请参阅

  • 使用 Spring Social 访问 Facebook 的 Spring 安全示例

  • 使用多个身份验证提供程序的 Spring 安全示例

  • 使用 OAuth 进行 Spring 安全示例

具有多个身份验证提供程序的 Spring 安全

在本节中,我们将演示使用 Spring Social 和数据库进行多重身份验证。在我们之前的示例中,我们使用了ConnectController类来处理 Facebook 和 Twitter 的连接。对 Facebook 和 Twitter 的访问受限于 Spring Security URL,即只有ROLE_EDITOR可以访问 Facebook 和 Twitter。用户必须经过身份验证和授权才能使用 Facebook 和 Twitter。在本例中,我们将允许用户使用 Facebook 和 Twitter 或普通用户 ID 登录应用程序。

Craig Walls是 Spring Social API 的负责人,并在 gitHub 上提供了各种示例,其中使用了 Spring Social 和 Spring Security。这是Craig Walls提供的示例之一。

准备工作

您需要执行以下任务:

  1. 创建一个通用页面,以用户身份登录或使用 Twitter、Facebook 或 linked-in 配置文件进行注册。

  2. Spring Social API 具有ConnectController类,该类会自动查找连接文件夹。创建一个连接文件夹,添加${provider}Connect.jsp${provider} Connected.jsp。$provider{twitter,facebook,linked-in,github}

  3. Spring Social 在内部使用spring-security。它有自己的用户详细信息类——SocialUserDetailsService。创建一个实现SocialUserDetailsService并覆盖该方法的类。

  4. social-security.xml文件中配置社交认证提供程序。SocialAuthenticationProvider类接受两个输入,例如:

  • usersConnectionRepository

  • socialuserDetailsService——实现SocialUserDetailsService的类

  1. security-xml中配置多个身份验证提供程序:
  • SocialAuthenticationProvider

  • UserDetailsService,提供用户详细信息服务的 jdbc 接口

  1. 配置SocialAuthenticationFilter过滤器,以处理 Spring Security 过滤器链中的提供程序登录流程。它应该被添加到PRE_AUTH_FILTER位置或之前的位置。

如何做...

以下是使用 Spring Security 实现多个提供程序进行身份验证的步骤:

  1. 使用SocialUsersDetailServiceImpl类来实现SocialUserDetailsService类:
public class SocialUsersDetailServiceImpl implements SocialUserDetailsService {
  private UserDetailsService userDetailsService;
  public SocialUsersDetailServiceImpl(UserDetailsService userDetailsService) {
    this.userDetailsService = userDetailsService;
  }
  @Override
    public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException, DataAccessException {
    UserDetails userDetails = userDetailsService.loadUserByUsername(userId);
    return new SocialUser(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
  }}
  1. Security.xml文件中配置SocialAuthenticationProvider类:
  <bean id="socialAuthenticationProvider" class="org.springframework.social.security.SocialAuthenticationProvider"
    c:_0-ref="usersConnectionRepository"
    c:_1-ref="socialUsersDetailService" />
  <bean id="socialUsersDetailService" class="org.springframework.social.showcase.security.SocialUsersDetailServiceImpl"
    c:_-ref="userDetailsService" />
  1. Security.xml文件中配置多个身份验证提供程序:
  <authentication-manager alias="authenticationManager">
    <authentication-provider user-service-ref="userDetailsService">
      <password-encoder ref="passwordEncoder" />
    </authentication-provider>
    <!-- Spring Social Security authentication provider -->
    <authentication-provider ref="socialAuthenticationProvider" />
 </authentication-manager>
  <jdbc-user-service id="userDetailsService" data-source-ref="dataSource" users-by-username-query="select username, password, true from Account where username = ?"
      authorities-by-username-query="select username, 'ROLE_USER' from Account where username = ?"/>
  <beans:bean id="textEncryptor" class="org.springframework.security.crypto.encrypt.Encryptors"
    factory-method="noOpText" />
  <beans:bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"
    factory-method="getInstance" />
  1. Social-security.xml文件中配置SocialAuthenticationFilter类:
<bean id="socialAuthenticationFilter" class="org.springframework.social.security.SocialAuthenticationFilter"
    c:_0-ref="authenticationManager"
    c:_1-ref="userIdSource"
    c:_2-ref="usersConnectionRepository"
    c:_3-ref="connectionFactoryLocator"
    p:signupUrl="/spring-social-showcase/signup"
    p:rememberMeServices-ref="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices#0" />
  1. security.xml文件中配置SocialAuthenticationFilter类与安全:
<http use-expressions="true">
    <!-- Authentication policy -->
    <form-login login-page="/signin" login-processing-url="/signin/authenticate" authentication-failure-url="/signin?param.error=bad_credentials" />
    <logout logout-url="/signout" delete-cookies="JSESSIONID" />
    <intercept-url pattern="/favicon.ico"access="permitAll" />
    <intercept-url pattern="/resources/**" access="permitAll" />
    <intercept-url pattern="/auth/**" access="permitAll" />
    <intercept-url pattern="/signin/**" access="permitAll" />
    <intercept-url pattern="/signup/**" access="permitAll"/>
    <intercept-url pattern="/disconnect/facebook" access="permitAll" />
    <intercept-url pattern="/**" access="isAuthenticated()"/>
    <remember-me />
    <!--  Spring Social Security authentication filter -->
    <custom-filter ref="socialAuthenticationFilter" before="PRE_AUTH_FILTER" />
  </http>

它是如何工作的...

在这个实现中,用户可以通过数据库中的一些凭据或使用社交网络站点的 ID 和密码登录应用程序。SocialAuthenticationProvider类与SocialAuthenticationFilter处理对社交网络站点的身份验证,UserDetailsService管理数据库身份验证。这两个类在security.xml文件中配置。

以下是实施的工作流程。访问 URL:http://localhost:8080/spring-social-showcase-sec-xml/signin。您将被引导到以下网页:

它是如何工作的...

另请参阅

  • 使用 Spring Security 与 Spring Social 访问 Facebook配方

  • 使用 Spring Security 与 Spring Social 访问 Twitter配方

  • 使用 Spring Security 与 OAuth配方

Spring Security 与 OAuth

OAuth 身份验证已被许多应用程序广泛使用。OAuth 是一种协议,通过该协议,应用程序可以以安全的方式共享数据。例如,考虑一个简单的场景,其中一个照片分享应用程序允许用户上传照片,第二个应用程序集成了所有照片存储应用程序,如 Flickr、Dropbox 和类似的网站。当第二个应用程序想要访问第一个应用程序以打印已上传的照片时,它使用 OAuth 身份验证来从用户那里获得确认以访问照片。理想情况下,它在应用程序之间交换一些安全令牌,即,消费者的私钥和服务器的公钥应匹配以使授权成功。

第一个应用程序充当服务器,第二个应用程序充当想要访问某些经过身份验证的数据的消费者。

客户端和服务器应用程序之间交换的一些参数如下:

  • Oauth_consumerKey:我们可以使用应用程序生成 OAuth 请求

  • Oauth_token:此令牌被编码并传递到 URL

  • Oauth_timestamp:此参数与 nonce 一起添加到每个请求中,以防止服务请求被再次使用,称为重放攻击

  • Oauth_version:这定义了正在使用的 OAuth 协议的版本

  • Oauth_signaturemethod:此参数用于签名和验证请求

  • Oauth_nonce:此参数与时间戳一起使用

  • Size:此参数定义文件的大小

  • File:此参数定义文件的名称

让我们开发一个样本客户端-服务器应用程序来演示 Spring Security 的 OAuth:

  • 服务器应用程序:让我们想象一个电影故事应用程序。该应用程序接受用户的故事。用户可以将他们的故事上传到应用程序。这个应用程序的行为类似于服务提供商。用户写一些恐怖故事并将它们提交给电影制作公司。

  • 客户端应用程序:想象另一个电影制作公司的应用程序,该应用程序接受从服务器应用程序上传的故事。电影制作公司必须从电影故事应用程序获取授权以下载故事。

准备工作

执行以下任务,将 Spring Security 与 OAuth 集成:

  • 创建一个带有ConfirmAccessControllerStoryController类的服务器应用程序

  • 创建一个客户端应用程序以访问服务器数据

  • spring-security-oauth依赖项添加到pom.xml文件

如何做...

以下是将spring-securityspring-oauth集成的步骤:

  1. 为故事创建CreateStoryController类。
@Controller
public class CreateStoryController {
  @RequestMapping(value="/stories", method=RequestMethod.GET)
  @ResponseBody
  public String loadStory() {
    StringBuilder horrorStory = new StringBuilder();
    horrorStory.append("Story Name -- Conjuring: Author").append(getAuthorName()).append(" Story:She and that girl and occasionally another girl went out several times a week, and the rest of the time Connie spent around the house—it was summer vacation—getting in her mother's way and thinking, dreaming about the boys she met. But all the boys fell back and dissolved into a single face that was not even a face but an idea, a feeling, mixed up with the urgent insistent pounding of the music and the humid night air of July. Connie's mother kept dragging her back to the daylight by finding things for her to do or saying suddenly, 'What's this about the Pettinger girl?");
    return horrorStory.toString();
  }
  private String getAuthorName() {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String author;
    if (principal instanceof UserDetails) {
      author = ((UserDetails)principal).getUsername();
    } else {
      author = principal.toString();
    }
    return author;
  }
}
  1. 创建ConfirmAccessController类。
@Controller
public class ConfirmAccessController {
  private ClientAuthenticationCache clientauthenticationCache = new DefaultClientAuthenticationCache();
  private ClientDetailsService clientDetailsService;
  public ClientAuthenticationCache getAuthenticationCache() {
    return clientauthenticationCache;
  }
  @RequestMapping(value="/oauth/confirm_access")
  public ModelAndView accessConfirmation(HttpServletRequest request, HttpServletResponse response) {
    ClientAuthenticationToken clientAuthtoken = getAuthenticationCache().getAuthentication(request, response);
    if (clientAuthtoken == null) {
      throw new IllegalStateException("We did not recive any client authentication to authorize");
    }
    ClientDetails client = getClientDetailsService().loadClientByClientId(clientAuthtoken.getClientId());
    TreeMap<String, Object> model = new TreeMap<String, Object>();
    model.put("auth_request", clientAuthtoken);
    model.put("client", client);
    return new ModelAndView("access_confirmation", model);
  }
  public ClientDetailsService getClientDetailsService() {
    return clientDetailsService;
  }
  @Autowired
  public void setClientDetailsService(
      ClientDetailsService clientDetailsService) {
    this.clientDetailsService = clientDetailsService;
  }
}
  1. 配置 Spring 安全与 OAuth。
<!-- Root Context: defines shared resources visible to all other web components -->
  <http auto-config='true'>
  <intercept-url pattern="/**" access="ROLE_EDITOR" />
  </http>
 <authentication-manager>
 <authentication-provider>
 <user-service>
 <user name="anju" password="anju123" authorities="ROLE_EDITOR" />
 </user-service>
 </authentication-provider>
 </authentication-manager>
 <!--apply the oauth client context -->
 <oauth:client token-services-ref="oauth2TokenServices" />
 <beans:bean id="oauth2TokenServices"
 class="org.springframework.security.oauth2.consumer.token.InMemoryOAuth2ClientTokenServices" />
 <oauth:resource id="story" type="authorization_code"
 clientId="movie" accessTokenUri="http://localhost:8080/story/oauth/authorize"
 userAuthorizationUri="http://localhost:8080/story/oauth/user/authorize" />
 <beans:bean id="storyService" class="org.springsource.oauth.StoryServiceImpl">
 <beans:property name="storyURL" value="http://localhost:8080/story/stories"></beans:property>
 <beans:property name="storyRestTemplate">
 <beans:bean class="org.springframework.security.oauth2.consumer.OAuth2RestTemplate">
 <beans:constructor-arg ref="story"/>
 </beans:bean>
 </beans:property>
 <beans:property name="tokenServices" ref="oauth2TokenServices"></beans:property>
 </beans:bean>
</beans:beans>

它是如何工作的...

您必须首先访问movieCompanyapp站点。movieCompanyapp反过来从storyapp站点获取故事。因此,我们必须在相同的端口上部署这两个应用程序。

我们创建了两个用户(raghu/raghu123用于movieCompanyappanju/anju123用于storyapp)。当用户单击从 storyapp 获取故事链接时,用户将被要求再次登录。这次用户必须输入他们的凭据,然后他们将能够阅读故事。

访问 URL:http://localhost:8080/movieCompanyapp/spring_security_login;jsessionid=3b654cf3917d105caa7c273283b5

它是如何工作的...它是如何工作的...

您将被要求授权以向公司展示故事。这发生在storyapp应用程序中。

它是如何工作的...

授权后,故事将在movieCompanyapp中可用。

它是如何工作的...

另请参阅

  • 使用 Spring Social 访问 Facebook 的 Spring 安全配方

  • 使用 Spring Social 访问 Twitter 的 Spring 安全配方

  • 具有多个身份验证提供程序的 Spring 安全配方

第十章:Spring 安全与 Spring Web 服务

在本章中,我们将涵盖:

  • 在 RESTful web 服务上应用 Spring Security

  • 使用 cURL 工具为 Spring RESTful web 服务配置 Spring Security

  • 将 Spring Security 与 Apache CXF RESTful 服务集成

  • 将 Spring Security 与 Apache CXF 基于 SOAP 的 web 服务集成

  • 将 Spring Security 与 Apache Camel 集成

介绍

SOAPSimple Object Access Protocol)是基于 XML 的 web 服务。它用于在 web 服务之间传输请求和响应消息。

RESTRepresentational State Transfer)是一种以 XML、文本或 JSON 文件形式通过 HTTP 协议发送数据的手段。

在本节中,我们将向 web 服务应用 Spring Security。任何 web 服务的正常流程是将服务 WSDL 或 URL 暴露给最终用户。在应用 Spring Security 后,最终用户可以被验证和授权使用服务。

在 RESTful web 服务上应用 Spring Security

REST 已成为提供 web 服务的另一种手段。

数据可以使用 XML、文本或 JSON 格式在应用程序之间共享。REST web 服务被认为是轻量级的 web 服务。

让我们应用 Spring Security 来访问 REST web 服务,以便只有经过授权的用户才能访问 RESTful web 服务。由于 RESTful web 服务是通过 URL 访问并使用 HTTP 协议,我们可以轻松应用 URL 级别的安全性。此示例演示了基于表单的身份验证。但用户也可以使用基本和摘要身份验证。

以下是与 Spring 一起使用的注释,用于生成 RESTful web 服务:

  • @PathVariable

  • @RequestMapping

  • @RequestMethod

准备工作

  • 使用 Spring web 服务 API 创建一个 RESTful web 服务

  • 添加 Spring Security 依赖项

  • 将 Spring 过滤器配置添加到Web.xml文件中

  • 配置application-security.xml文件

  • 创建一个AccessController类来处理登录和注销操作

  • 在应用程序中配置 Spring Security 以对用户进行身份验证

如何做...

以下是将 RESTful web 服务与 Spring Security 集成的步骤:

  1. 让我们创建一个BookController类,其中包含@PathVariable,如下面的代码片段所示:
package org.springframework.rest;
@Controller
public class BookController {
  private static final Map<Integer, Books> books = new HashMap<Integer, Books>();
  static {
    try {
      books.put(1, new Books(1, "Someone Like You", "Penguin", "Durjoy Datta-Nikita Singh"));
      books.put(2, new Books(2, "The Secret Wish List", "Westland", " Preeti Shenoy"));
      books.put(3, new Books(3, "Love Stories That Touched My Heart ", "Metro Reads", " Preeti Shenoy"));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  @RequestMapping(value = "/books/{book_id}", method = RequestMethod.GET)
  @ResponseBody
  public Books findCharacter(@PathVariable int book_id) {
    return books.get(book_id);
  }
}
  1. 创建一个Books POJO 类,其中包含@JsonAutoDetect注释,如下面的代码片段所示:
@JsonAutoDetect
public class Books {
    private int book_id;
    private String book_name;
    private String book_publication;
    private String book_author;
    public Books(int book_id, String book_name, String book_publication, String book_author) {
      this.book_id = book_id;
      this.book_name = book_name;
      this.book_publication = book_publication;
      this.book_author = book_author;
    }
    public String getBook_author() {
      return book_author;
    }
    public void setBook_author(String book_author) {
      this.book_author = book_author;
    }
    public int getBook_id() {
      return book_id;
    }
    public void setBook_id(int book_id) {
      this.book_id = book_id;
    }
    public String getBook_name() {
      return book_name;
    }
    public void setBook_name(String book_name) {
      this.book_name = book_name;
    }
    public String getBook_publication() {
      return book_publication;
    }
    public void setBook_publication(String book_publication) {
      this.book_publication = book_publication;
    }
}
  1. 创建一个AccessController类来处理登录和注销操作:
package org.springframework.booksservice;
@Controller
public class AccessController {
  @RequestMapping(value = "/", method = RequestMethod.GET)
  public String defaultPage(ModelMap map) {
    return "redirect:/login";
  }
  @RequestMapping(value = "/login", method = RequestMethod.GET)
  public String login(ModelMap model) {
    return "login";
  }
  @RequestMapping(value = "/accessdenied", method = RequestMethod.GET)
  public String loginerror(ModelMap model) {
    model.addAttribute("error", "true");
    return "denied";
  }
  @RequestMapping(value = "/logout", method = RequestMethod.GET)
  public String logout(ModelMap model) {
    return "logout";
  }
}
  1. 配置Application-security.xml文件,如下面的代码片段所示:
  <http auto-config="false"  use-expressions="true">
    <intercept-url pattern="/login" access="permitAll" />
    <intercept-url pattern="/logout" access="permitAll" />
    <intercept-url pattern="/accessdenied" access="permitAll" />
    <intercept-url pattern="/**" access="hasRole('ROLE_EDITOR')" />
    <form-login login-page="/login" default-target-url="/books" authentication-failure-url="/accessdenied" />
    <logout logout-success-url="/logout" />
  </http>
  <authentication-manager>
    <authentication-provider>
    <user-service>
      <user name="anjana" password="packt123" authorities="ROLE_EDITOR" />
    </user-service>
  </authentication-provider>
</authentication-manager>

工作原理...

访问 URL:http://localhost:8080/booksservice/books/1。这是基于 REST 的 URL,使用 Spring Security 限制了访问。当用户调用基于 REST 的 web 服务 URL 时,Spring Security 将用户重定向到登录页面。在成功验证后,用户将被重定向到授权的基于 REST 的 web 服务页面。

以下是基于 REST 的应用程序与 Spring Security 的工作流程。您将被重定向到登录页面,如下面的屏幕截图所示:

工作原理...

在认证和授权后,您将能够访问 RESTful web 服务,如下面的屏幕截图所示:

工作原理...

另请参阅

  • 将 Spring Security 与 Apache CXF RESTful web 服务集成配方

  • 将 Spring Security 与 Apache CXF 基于 SOAP 的 web 服务集成配方

  • 将 Spring Security 与 Apache Camel 集成配方

使用 cURL 工具为 Spring RESTful web 服务配置 Spring Security

在这个例子中,我们明确使用 Spring Security API 类和接口。我们将使用curl命令对 RESTful web 服务进行身份验证。使用 cURL 工具,您可以通过 URL 传输数据。它可以用来测试身份验证。这是一个相同的书籍服务示例,其中包含一些明确的 Spring Security 相关 API 类,如AuthenticationEntryPointSimpleURLAuthenticationSuccessHandler。在这里,目标是演示它们在 Spring Security 中的内部使用。

准备工作

  • 实现AuthenticationEntryPoint接口并在 XML 文件中进行配置

  • 扩展SimpleUrlAuthenticationSuccessHandler并在 XML 文件中进行配置

  • 配置Application-security.xml文件

  • 将安全相关的过滤器添加到Web.xml文件

  • 下载适用于您操作系统的 cURL 工具

如何做...

以下是使用AuthenticationEntryPoint接口和SimpleURLAuthenticationSuccessHandler类应用 Spring Security 身份验证和授权机制的步骤:

  1. AuthenticationEntryPoint类是身份验证的入口类,它实现了AuthenticationEntryPointImpl类。
public final class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
  @Override
  public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException {
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
  }
}
  1. 扩展SimpleURLAuthenticationSuccessHandler类,如下面的代码片段所示:
  public class MySimpleUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    private RequestCache requestCache = new HttpSessionRequestCache();
    @Override
    public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws ServletException, IOException {
      final SavedRequest savedRequest = requestCache.getRequest(request, response);
      if (savedRequest == null) {
        clearAuthenticationAttributes(request);
        return;
      }
      final String targetUrlParameter = getTargetUrlParameter();
      if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
        requestCache.removeRequest(request, response);
        clearAuthenticationAttributes(request);
        return;
      }
      clearAuthenticationAttributes(request);
    }
    public void setRequestCache(final RequestCache requestCache) {
      this.requestCache = requestCache;
    }
  }
  1. 配置Application-security.xml文件。
  <http entry-point-ref="authenticationEntryPoint">
    <intercept-url pattern="/**" access="ROLE_EDITOR"/>
    <form-login authentication-success-handler-ref="mySuccessHandler" />
    <logout />
  </http>
  <beans:bean id="mySuccessHandler"class="org.springframework.booksservice.MySimpleUrlAuthenticationSuccessHandler"/>
  <beans:bean id="authenticationEntryPoint"class="org.springframework.booksservice.AuthenticationEntryPointImpl"/>
  <authentication-manager>
    <authentication-provider>
      <user-service>
        <user name="anjana" password="packt123" authorities="ROLE_EDITOR" />
      </user-service>
    </authentication-provider>
  </authentication-manager>
  </beans:beans>

它是如何工作的...

现在访问 URL:http://localhost:8080/booksservice/books/1

您将看到一个页面,上面写着您没有权限查看页面。

让我们使用 cURL 工具,它会给我们一个 cookie。200 OK消息意味着我们已经通过身份验证。

Command: curl -i -X POST -d j_username=anjana -d j_password=packt123 http://localhost:8080/booksservice/j_spring_security_check
curl -i --header "Accept:application/json" -X GET -b cookies.txt http://localhost:8080/booksservice/books/1

cookie 存储在名为mycookies.txt的文件中。

它是如何工作的...它是如何工作的...

另请参阅

  • 将 Spring Security 与 Apache CXF RESTful web 服务集成的方法

  • 将 Spring Security 与 Apache CXF 基于 SOAP 的 web 服务集成的方法

  • 将 Spring Security 与 Apache Camel 集成的方法

将 Spring Security 与 Apache CXF RESTful web 服务集成

在这一部分,让我们创建一个 Apache CXF RESTful web 服务。这是一个开源的 web 服务框架。让我们为这个演示使用基本身份验证。

CXF 支持契约优先和契约后的 web 服务。它还支持 RESTful web 服务。

让我们将 Spring Security 与 CXF 集成,并授权 RESTful web 服务。

准备工作

  • cxf依赖项添加到pom文件中

  • 使用 CXF 设置 RESTful web 服务

  • 配置spring-security.xml文件

如何做...

以下是将 Spring Security 与 Apache CXF RESTful web 服务集成的步骤:

  1. 配置Book POJO 类。
@XmlRootElement(name = "book")
public class Book {
    private int book_id;
    private String book_name;
    private String book_publication;
    private String book_author;
    public Book(int book_id, String book_name, String book_publication, String book_author) {
      this.book_id = book_id;
      this.book_name = book_name;
      this.book_publication = book_publication;
      this.book_author = book_author;
    }
    public String getBook_author() {
      return book_author;
    }
    public void setBook_author(String book_author) {
      this.book_author = book_author;
    }
    public int getBook_id() {
      return book_id;
    }
    public void setBook_id(int book_id) {
      this.book_id = book_id;
    }
    public String getBook_name() {
      return book_name;
    }
    public void setBook_name(String book_name) {
      this.book_name = book_name;
    }
    public String getBook_publication() {
      return book_publication;
    }
    public void setBook_publication(String book_publication) {
      this.book_publication = book_publication;
    }
}
  1. 配置BookCollection POJO 类。
  @XmlType(name = "BookCollection")
  @XmlRootElement
  public class BookCollection {
    private Collection books;
    public BookCollection() {
    }
    public BookCollection(Collection books) {
      this.books = books;
    }
    @XmlElement(name="books")
    @XmlElementWrapper(name="books")
    public Collection getUsers() {
      return books;
    }
  }
  1. 配置BookService接口。
public interface BookService {
    BookCollection getBooks();
    Book getBook(Integer id);
    Response add(Book book);
}
  1. 配置BookServiceImpl类。
  @Path ("/services/")
  public class BookServiceImpl implements BookService {
    private static final Map<Integer, Book> books = new HashMap<Integer, Book>();
    private static int index = 4;
    static {
      try {
        books.put(1, new Book(1, "Someone Like You", "Penguin", "Durjoy Datta-Nikita Singh"));
          books.put(2, new Book(2, "The Secret Wish List", "Westland", " Preeti Shenoy"));
          books.put(3, new Book(3, "Love Stories That Touched My Heart ", "Metro Reads", " Preeti Shenoy"));
        } catch (Exception e) {
          e.printStackTrace();
        }
    }
 @Override
 @POST
 @Path("/book")
 @Consumes("application/json")
    public Response add(Book book) {
      System.out.println("Adding :" + book.getBook_name());
      book.setBook_id(index++);
      return Response.status(Response.Status.OK).build();
    }
 @Override
 @GET
 @Path("/book/{book_id}")
 @Produces("application/json")
    public Book getBook(@PathParam("book_id") Integer book_id) {
      return books.get(book_id);
    }
 @Override
 @GET
 @Path("/books")
 @Produces("application/json")
    public BookCollection getBooks() {
      return new BookCollection(books.values());
    }
}
  1. 配置application-security.xml文件:
  <sec:global-method-security pre-post-annotations="enabled" />
  <sec:http auto-config="true"  use-expressions="true">
    <sec:intercept-url pattern="/**" access="hasRole('ROLE_EDITOR')"/>
    <sec:http-basic></sec:http-basic>
    <sec:logout logout-success-url="/logout" />
  </sec:http>
  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
  <jaxrs:server address="/" id="myService">
    <jaxrs:serviceBeans>
      <ref bean="bookserviceImpl"/>
    </jaxrs:serviceBeans>
    <jaxrs:providers>
      <ref bean="jacksonProvider"/>
    </jaxrs:providers>
  </jaxrs:server>
  <bean id="jacksonProvider"
  class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
  <bean id="bookserviceImpl"
  class="org.springframework.booksservice.BookServiceImpl"/>
  <sec:authentication-manager>
    <sec:authentication-provider>
      <sec:user-service>
        <sec:user name="anjana" password="packt123" authorities="ROLE_EDITOR" />
      </sec:user-service>
    </sec:authentication-provider>
  </sec:authentication-manager>
</beans>
  1. 配置Web.xml文件。
    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/application-security.xml</param-value>
  </context-param>
  <!-- Creates the Spring Container shared by all Servlets and Filters -->
  <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!-- Processes application requests -->
  <servlet>
    <servlet-name>cxf</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>cxf</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
  <!-- Spring child -->
  <!-- <servlet>
  <servlet-name>bookservice_cxf</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>bookservice_cxf</servlet-name>
    <url-pattern>/bookservice_cxf/*</url-pattern>
  </servlet-mapping>-->
  <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>
</web-app>

它是如何工作的...

这个例子中,RESTful 服务是由 CXF 框架提供的。然后应用程序集成了 Spring Security,以提供安全的身份验证和授权模块给 RESTful web 服务。Spring Security 过滤器链管理身份验证和授权过程。当您访问服务时,将提示您登录,如下面的屏幕截图所示。登录后,您可以查看 RESTful 数据。Mozilla Firefox 浏览器将提示用户以文件格式下载数据。

它是如何工作的...

现在访问 URL:http://localhost:8080/booksservice_cxf/services/services/book/1

它是如何工作的...

另请参阅

  • 将 Spring Security 与 Apache CXF RESTful web 服务集成的方法

  • 将 Spring Security 与 Apache Camel 集成的方法

将 Spring Security 与 Apache CXF 基于 SOAP 的 web 服务集成

在这一部分,让我们创建一个基于 SOAP 的 web 服务。我们将演示 Spring Security 与 Apache CXF 基于 SOAP 的 web 服务的集成。

使用 Apache CXF 创建基于 SOAP 的 web 服务已经变得简单。

准备就绪

  • 将 CXF-SOAP 依赖项添加到pom文件中。

  • pom文件添加基于 Spring Security 的依赖项。

  • 使用interfaceImpl类设置基于 SOAP 的 Web 服务。

  • 配置spring-security.xml文件。

  • 将 jar 添加到Tomcat_7.0/lib文件夹作为设置的一部分。 Tomcat 需要以下 jar 文件才能与 CXF Web 服务一起工作。缺少这些 jar 可能会导致一些错误:

  • streambuffer.jar

  • stax-ex

  • jaxws-ap-2.1

  • jaxws-rt

如何做...

以下是将 Apache CXF 基于 SOAP 的 Web 服务与 Spring Security 集成的步骤:

  1. Book POJO 具有 getter 和 setter 方法。它还具有参数化构造函数。Book POJO 在BookService接口中使用,以提供有关从客户端应用程序请求的Book的详细信息。
package org.packt.cxf.domain;
public class Book {
  private int book_id;
  private String book_name;
  private String book_publication;
  private String book_author;
  public Book() {
  }
  public Book(int book_id, String book_name, String book_publication, String book_author) {
    this.book_id = book_id;
    this.book_name = book_name;
    this.book_publication = book_publication;
    this.book_author = book_author;
  }
  public String getBook_author() {
    return book_author;
    }
    public void setBook_author(String book_author) {
        this.book_author = book_author;
  }
  public int getBook_id() {
    return book_id;
  }
  public void setBook_id(int book_id) {
    this.book_id = book_id;
  }
  public String getBook_name() {
    return book_name;
  }
  public void setBook_name(String book_name) {
    this.book_name = book_name;
  }
  public String getBook_publication() {
    return book_publication;
  }
  public void setBook_publication(String book_publication) {
    this.book_publication = book_publication;
  }
}
  1. BookService接口使用@WebService注解创建,其中getBookDetails是 WSDL 中的服务方法。
package org.packt.cxf.service;
import javax.jws.WebService;
import org.packt.cxf.domain.Book;
@WebService
public interface BookService {
  public Book getBookDetails(int book_id);
}
  1. BookServiceImpl类是BookService接口的实现类,并使用@webservice注解包org.packt.cxf.service配置为端点接口。
import java.util.HashMap;
import java.util.Map;
import javax.jws.WebService;
import org.packt.cxf.domain.Book;
@WebService(endpointInterface = "org.packt.cxf.service.BookService")
public class BookServiceImpl implements BookService{
    private static final Map<Integer, Book> books = new HashMap<Integer, Book>();
    private static int index = 4;
    static {
      try {
        books.put(1, new Book(1, "Someone Like You", "Penguin", "Durjoy Datta-Nikita Singh"));
        books.put(2, new Book(2, "The Secret Wish List", "Westland", " Preeti Shenoy"));
        books.put(3, new Book(3, "Love Stories That Touched My Heart ", "Metro Reads", " Preeti Shenoy"));
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    @Override
    public Book getBookDetails(int book_id) {
      return books.get(book_id);
    }}
  1. Cxf-servlet.xml文件中,我们注册了 Web 服务接口和实现类。
  <beans 

  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://cxf.apache.org/jaxwshttp://cxf.apache.org/schemas/jaxws.xsd">
  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
  <import resource="classpath:META-INF/cxf/cxf-extension-http.xml" />
  <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
  <jaxws:endpoint id="bookService"implementor="org.packt.cxf.service.BookServiceImpl" address="/BookService" />
  </beans>
  1. Web.xml文件中,我们引用cxf-servlet.xml的位置,并配置CXFSservlet
  <web-app version="2.5"   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>SampleWSCxf</display-name>
    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>WEB-INF/cxf-servlet.xml</param-value>
    </context-param>
    <servlet>
      <servlet-name>CXFServlet</servlet-name>
      <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
      <servlet-name>CXFServlet</servlet-name>
      <url-pattern>/*</url-pattern>
    </servlet-mapping>
  </web-app>

工作原理...

在本节中,我们演示了 Web 服务的基本身份验证。访问 URL:http://localhost:8080/bookservice/

我们使用了 CXF 框架创建基于 SOAP 的 Web 服务。当用户访问 URL 时,期望的行为是允许访问 WSDL 及其服务。但是 Spring Security 中断了请求,并为用户弹出登录对话框。成功验证后,用户可以访问 WSDL。

工作原理...工作原理...

生成的 WSDL 可在以下 URL 找到:http://localhost:8080/bookservice/BookService?wsdl

<wsdl:definitions    name="BookServiceImplService" targetNamespace="http://service.cxf.packt.org/">
  <wsdl:types>
  <xs:schema elementFormDefault="unqualified" targetNamespace="http://service.cxf.packt.org/" version="1.0">
  <xs:element name="getBookDetails"type="tns:getBookDetails"/>
  <xs:element name="getBookDetailsResponse" type="tns:getBookDetailsResponse"/>
  <xs:complexType name="getBookDetails">
    <xs:sequence>
      <xs:element name="arg0" type="xs:int"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="getBookDetailsResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return"type="tns:book"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="book">
    <xs:sequence>
      <xs:element minOccurs="0" name="book_author" type="xs:string"/>
      <xs:element name="book_id" type="xs:int"/>
      <xs:element minOccurs="0" name="book_name" type="xs:string"/>
      <xs:element minOccurs="0" name="book_publication" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
  </xs:schema>
  </wsdl:types>
  <wsdl:message name="getBookDetails">
    <wsdl:part element="tns:getBookDetails" name="parameters"></wsdl:part>
  </wsdl:message>
  <wsdl:message name="getBookDetailsResponse">
    <wsdl:part element="tns:getBookDetailsResponse" name="parameters"></wsdl:part>
    </wsdl:message>
  <wsdl:portType name="BookService">
    <wsdl:operation name="getBookDetails">
      <wsdl:input message="tns:getBookDetails"name="getBookDetails"></wsdl:input>
      <wsdl:outputmessage="tns:getBookDetailsResponse"name="getBookDetailsResponse"></wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:bindingname="BookServiceImplServiceSoapBinding"type="tns:BookService">
    <soap:bindingstyle="document"transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operationname="getBookDetails">
        <soap:operationsoapAction=""style="document"/>
      <wsdl:inputname="getBookDetails">
        <soap:bodyuse="literal"/>
      </wsdl:input>
      <wsdl:outputname="getBookDetailsResponse">
        <soap:bodyuse="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:servicename="BookServiceImplService">
    <wsdl:portbinding="tns:BookServiceImplServiceSoapBinding"name="BookServiceImplPort">
      <soap:addresslocation="http://localhost:8080/bookservice/BookService"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

另请参阅

  • 将 Spring Security 与 Apache CXF RESTful Web 服务集成配方

  • 将 Spring Security 与 Apache Camel 集成配方

将 Spring Security 与 Apache Camel 集成

Apache Camel 可用于定义路由和调解应用程序的规则。 Spring Security 可用于与 Apache Camel 一起对路由器进行身份验证。 Spring Security 身份验证策略对象控制对路由器的访问。 Spring Security 身份验证策略对象包含角色信息,并引用 Spring 身份验证管理器。您可以从网站下载源代码。

准备就绪

  • 创建 Camel 上下文

  • 使用 XML 配置添加路由规则

  • 在 Spring XML 文件中配置以下内容:

  • 访问决策管理器

  • 角色投票者

  • 身份验证管理器

  • 用户详细信息服务

  • 使用权限配置身份验证策略对象

  • 添加camel-spring-security依赖

如何做...

以下是将 Apache Camel 与 Spring Security 集成的步骤:

  1. 创建Camel–context.xml文件,并使用 Spring Security 定义路由规则。
  <spring-security:http realm="User Access Realm">
    <spring-security:intercept-url pattern="/apachecamel/**"     access="ROLE_EDITOR"/>
    <spring-security:http-basic/>
    <spring-security:remember-me/>
  </spring-security:http>
  <spring-security:authentication-manager alias="authenticationManager">
    <spring-security:authentication-provider user-service-ref="userDetailsService"/>
  </spring-security:authentication-manager>
  <spring-security:user-service id="userDetailsService">
    <spring-security:user name="anju" password="anju123" authorities="ROLE_EDITOR,ROLE_AUTHOR"/>
    <spring-security:user name="shami" password="shami123" authorities="ROLE_EDITOR"/>
  </spring-security:user-service>
  <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <property name="allowIfAllAbstainDecisions" value="true"/>
    <property name="decisionVoters">
      <list>
        <bean class="org.springframework.security.access.vote.RoleVoter"/>
      </list>
    </property>
  </bean>
  <!-- The Policy for checking the authentication role of AUTHOR -->
  <authorizationPolicy id="author" access="ROLE_AUTHOR"
    authenticationManager="authenticationManager"
    accessDecisionManager="accessDecisionManager"
    />
  <!-- The Policy for checking the authentication role of EDITOR -->
  <authorizationPolicy id="editor" access="ROLE_EDITOR"/>
  <camelContext id="myCamelContext" >
    <!-- Catch the authorization exception and set the Access Denied message back -->
    <onException>
    <exception>org.apache.camel.CamelAuthorizationException</exception>
    <handled>
      <constant>true</constant>
    </handled>
    <transform>
      <simple>Access Denied with the Policy of ${exception.policyId} !</simple>
      </transform>
    </onException>
 <route>
 <from uri="servlet:///editor"/>
 <!-- wrap the route in the policy which enforces security check -->
 <policy ref="editor">
 <transform>
 <simple>Normal user can access this service</simple>
 </transform>
 </policy>
 </route>
 <route>
 <from uri="servlet:///author"/>
 <!-- wrap the route in the policy which enforces security check -->
 <policy ref="author">
 <transform>
 <simple>Call the admin operation OK</simple>
 </transform>
 </policy>
 </route>
  </camelContext>
</beans>
  1. Web.xml中配置 Camel servlet。
<!-- location of spring xml files -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:camel-context.xml</param-value>
  </context-param>
  <!-- the listener that kick-starts Spring -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <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>
  <servlet>
    <servlet-name>CamelServlet</servlet-name>
    <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>CamelServlet</servlet-name>
    <url-pattern>/apachecamel/*</url-pattern>
  </servlet-mapping>
</web-app>

工作原理...

现在访问 URL:http://localhost:8080/apachecamel/editor

camel-context.xml文件具有路由规则;camel-context.xml文件的位置在Web.xml中配置,同时配置了CamelServlet来处理路由机制。<authorizationpolicy>标签处理在spring-security.xml文件中配置的资源的身份验证和授权。<spring-security:user-service>标签包含用户和角色的详细信息,在路由请求之前可以给予访问权限。以下是 Apache Camel 使用 Spring Security 中断路由过程的工作流程。用户被授权使用两个角色中的任意一个:EDITORAUTHOR

工作原理...

另请参阅

  • 将 Spring Security 与 Apache CXF RESTful Web 服务集成配方

  • Spring Security 与 Apache Camel 集成的配方

第十一章:更多关于 Spring 安全

在本章中,我们将涵盖:

  • 具有多个认证提供者的 Spring 安全

  • 具有多个输入认证的 Spring 安全

  • 集成验证码的 Spring 安全

  • Spring Security with JAAS

介绍

在本章中,我们将看到 Spring 安全的一些更多示例。让我们看看如何将 Spring 安全与多个认证提供者集成。我们还将看到使用 Spring 进行多个输入认证的示例。

具有多个认证提供者的 Spring 安全

Spring Security 提供了添加多个认证提供者的选项。过滤器链会检查每个认证提供者,直到成功认证。

在本节中,让我们看看如何配置多个认证提供者以及 Spring 如何使用多个认证提供者进行认证。

例如,我们正在使用horrormovie应用程序,其中认证和授权由 Spring Security 与数据库处理。

准备工作

  • 创建一个 maven web 项目

  • 添加spring-security依赖项

  • 添加与 spring-core 相关的依赖项

  • Web.xml文件中配置 Spring 上下文监听器

  • 创建AddHorroMovieController.java控制器,并添加用于添加、删除和列出的请求映射方法

  • 编辑application-security.xml文件以添加另一个认证提供者

如何做...

以下是将多个认证提供者与 Spring Security 集成的步骤:

  1. 编辑application-security.xml文件。
  <authentication-manager alias="authentication Manager">
    <authentication-provider>
 <jdbc-user-service data-source-ref="tenant1DataSource"users-by-username-query=" select username, password ,'true' as enabled from users where username=?"authorities-by-username-query=" select u.username as username, ur.authority as authority from users u, user_roles ur where u.user_id = ur.user_id and u.username =?" />
    </authentication-provider>
 <authentication-provider>
 <user-service>
 <user name="anjana" password="anjana123" authorities="ROLE_EDITOR"/>
 <user name="raghu" password="raghu123" authorities="ROLE_AUTHOR"/>
 <user name="shami" password="shami123" authorities="ROLE_EDITOR"/>
 </user-service>
 </authentication-provider>
  </authentication-manager>

工作原理...

将应用部署到 GlassFish 应用服务器;访问以下 URL:http://localhost:8080/list,并使用用户名/密码(Vikash/Vikash123)登录。

这是在 derby 数据库中创建的用户,具有访问权限(ROLE_EDITOR)。

然后注销并再次使用用户名shami和密码shami123登录。在这里,用户会按顺序通过两个认证提供者进行认证。

工作原理...

另请参阅

  • 具有多个输入认证的 Spring 安全配方

  • 集成验证码的 Spring 安全

  • 具有 JAAS 的 Spring 安全配方

具有多个输入认证的 Spring 安全

在本节中,我们将演示多个输入认证。这也被称为双因素认证。到目前为止,在我们的所有示例中,我们都是根据用户名和密码进行认证。在这个示例中,我们将提供另一个字段用于电话号码以及用户名。这是使用 hibernate 和 derby 数据库的相同horrormovie应用程序。

准备工作

  • 创建一个自定义过滤器来处理新的登录表单

  • Springsecurity.xml文件中配置自定义过滤器

  • 更新UserDetailsService实现类以处理额外的输入

  • 在数据库中添加一个名为MOBILE_NO的额外列

  • 更新login.jsp文件以接受MOBILE_NO作为输入

如何做...

以下是使用 Spring Security 实现多个输入认证的步骤:

  1. 创建名为MultipleInputAuthenticationFilter的自定义过滤器以提取额外的手机号参数。
  public class MultipleInputAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
    private String extraParameter = "mobile_no";

    public String getExtraParameter() {
        return extraParameter;
    }

    public void setExtraParameter(String extraParameter) {
      this.extraParameter = extraParameter;
    }
    private String delimiter = ":";

    @Override
    protected String obtainUsername(HttpServletRequest request)
      {
        String username = request.getParameter(getUsernameParameter());
        String mobile_no = request.getParameter(getExtraParameter());
        String combinedUsername = username + getDelimiter() + mobile_no;
        System.out.println("Combined username = " + combinedUsername);
        return combinedUsername;
      }

    public String getDelimiter()
    {
      return this.delimiter;
    }
    /**
      * @param delimiter The delimiter string used to separate the username and extra input values in the
        * string returned by <code>obtainUsername()</code>
    */
    public void setDelimiter(String delimiter) {
      this.delimiter = delimiter;
    }
  1. 更新application-security.xml文件以处理自定义过滤器。
  <global-method-security pre-post-annotations="enabled" />
    <http auto-config="false"  use-expressions="true" entry-point-ref="loginUrlAuthenticationEntryPoint">
      <intercept-url pattern="/login" access="permitAll" />
      <intercept-url pattern="/logout" access="permitAll" />
      <intercept-url pattern="/accessdenied" access="permitAll" />
      <intercept-url pattern="/list" access="hasRole('ROLE_EDITOR')" />
      <intercept-url pattern="/add" access="hasRole('ROLE_EDITOR')" />
      <custom-filter position="FORM_LOGIN_FILTER" ref="multipleInputAuthenticationFilter" />
      <!--<form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied" />-->
      <logout logout-success-url="/logout" />
    </http>
    <authentication-manager alias="authenticationManager">
      <authentication-provider user-service-ref="MyUserDetails">
        <password-encoder hash="plaintext" />
      </authentication-provider>
    </authentication-manager>
 <beans:bean id="multipleInputAuthenticationFilter" class="com.packt.springsecurity.controller.MultipleInputAuthenticationFilter">
 <beans:property name="authenticationManager" ref="authenticationManager" />
 <beans:property name="authenticationFailureHandler" ref="failureHandler" />
 <beans:property name="authenticationSuccessHandler" ref="successHandler" />
 <beans:property name="filterProcessesUrl" value="/j_spring_security_check" />
 <beans:property name="postOnly" value="true" />
 <beans:property name="extraParameter" value="mobile_no" />
 </beans:bean>
    <beans:bean id="horrorMovieDAO" class="com.packt.springsecurity.dao.HorrorMovieDaoImpl" />
    <beans:bean id="horrorMovieManager" class="com.packt.springsecurity.service.HorrorMovieManagerImpl" />
    <beans:bean id="UsersDAO" class="com.packt.springsecurity.dao.UsersDAOImpl" />
    <beans:bean id="UsersManager" class="com.packt.springsecurity.service.UsersManagerImpl" />
    <beans:bean id="UserRoleDAO" class="com.packt.springsecurity.dao.UserRoleDAOImpl" />
    <beans:bean id="UserRoleManager" class="com.packt.springsecurity.service.UserRoleManagerImpl" />
    <beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
      <beans:property name="loginFormUrl" value="/login" />
    </beans:bean>
    <beans:bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
      <beans:property name="defaultTargetUrl" value="/list" />
    </beans:bean>

    <beans:bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
      <beans:property name="defaultFailureUrl" value="/accessdenied" />
    </beans:bean>
 <beans:bean id="MyUserDetails" class="com.packt.springsecurity.service.MyUserDetails" />
 </beans:beans> 

  1. 更新UsersDAOImpl以处理额外的输入。
@Override
 @Transactional
 public Users findByUserNameMobile(String userName, String mobile_no) {
 List<Users> userList = new ArrayList<Users>();
 Query query = (Query) sessionFactory.getCurrentSession().createQuery("from Users u where u.userName = :userName and u.mobile_no=:mobile_no");
 query.setParameter("userName", userName);
 query.setInteger("mobile_no", Integer.parseInt(mobile_no));
 userList = query.list();
 if (userList.size() > 0) {
 return userList.get(0);
 } else {
 return null;
 }
 }

  1. 在实现UserDetailsService接口的MyUserDetails类中实现方法,以处理额外的输入。
public UserDetails loadUserByUsername(String str)throws UsernameNotFoundException {
 String[] splitstring = str.split(":");
 if (splitstring.length < 2) {
 System.out.println("User did not enter both username and mobile number.");
 throw new UsernameNotFoundException("Must specify both username and mobile number");
 }
 String username = splitstring[0];
 String mobile = splitstring[1];

 System.out.println("Username = " + username);
 System.out.println("Mobile = " + mobile);

 Users users = UsersDAO.findByUserNameMobile(username, mobile);
 boolean enabled = true;
 boolean accountNonExpired = true;
 boolean credentialsNonExpired = true;
 boolean accountNonLocked = true;
 return new User(
 users.getUserName(),
 users.getUserPassword(),
 enabled,
 accountNonExpired,
 credentialsNonExpired,
 accountNonLocked,
 getAuthorities(users.getRole().getRoleId().intValue()));
}

工作原理...

访问以下 URL:http://localhost:8080/SpringSecurity_MultipleInputAuth/login

用户不仅通过用户名和密码进行认证,如本书中所有应用程序中所示,还通过手机号参数进行认证。

当用户在登录页面提交信息并点击提交查询时,用户名和手机号将与分隔符合并,并且 Spring 安全性将调用MyUserDetails类,该类将根据用户使用 hibernate 提供的输入再次拆分参数并对用户进行身份验证。

成功验证后,用户将被重定向到经过授权的页面。

工作原理...

另请参阅

  • 具有多个身份验证提供程序的 Spring 安全性配方

  • 具有验证码集成的 Spring 安全性配方

  • 具有 JAAS 的 Spring 安全性配方

具有验证码集成的 Spring 安全性

让我们演示 Spring 安全性与验证码的集成。我们已经为此目的下载了Kaptcha.jar验证码提供程序。我们需要将 jar 文件安装到 maven 本地存储库中,以使应用程序正常工作。

该示例是前一个配方的扩展,其中考虑了额外的输入,即手机号码,用于 Spring 安全性的授权和身份验证。在此示例中,我们将从用户那里获取用户名和密码的代码以及验证码代码。用户名将与数据库进行身份验证,并且还将比较请求的验证码和用户输入的验证码。

当所有条件匹配时,用户被认为已经通过验证,否则认证失败。

准备就绪

  • Kaptcha servlet 添加到Web.xml文件中

  • 在您的Springsecurity.xml文件中配置自定义过滤器

  • 更新UserDetailsService实现类以处理Kaptcha

  • 更新login.jsp文件以将Kaptcha作为输入

  • 扩展UsernamePasswordAuthenticationFilter

如何做...

以下是将 Spring 安全性与验证码集成的步骤:

  1. Kaptcha servlet 添加到Web.xml文件中。
  <servlet>
    <servlet-name>Kaptcha</servlet-name>
    <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Kaptcha</servlet-name>
    <url-pattern>/kaptcha.jpg</url-pattern>
  </servlet-mapping>
  1. 更新application-security.xml以处理自定义过滤器。
  <beans:bean id="multipleInputAuthenticationFilter" class="com.packt.springsecurity.controller.MultipleInputAuthenticationFilter">
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="authenticationFailureHandler" ref="failureHandler" />
    <beans:property name="authenticationSuccessHandler" ref="successHandler" />
    <beans:property name="filterProcessesUrl" value="/j_spring_security_check" />
    <beans:property name="postOnly" value="true" />
    <beans:property name="extraParameter" value="kaptcha" />
  </beans:bean>
  1. 更新UsersDAOImpl以处理额外的输入。
 @Override
 @Transactional
 public Users findByUserNameCaptcha(String userName, String kaptchaReceived, String kaptchaExpected) {
 List<Users> userList = new ArrayList<Users>();
 Query query = (Query) sessionFactory.getCurrentSession().createQuery("from Users u where u.userName = :userName");
 query.setParameter("userName", userName);
 userList = query.list();
 if (userList.size()>0 && kaptchaReceived.equalsIgnoreCase(kaptchaExpected)) {
 return (Users)userList.get(0);
 }  else {
 return null;
 }
 }

  1. 更新UserDetailsService类以处理额外的输入。
public UserDetails loadUserByUsername(String str)throws UsernameNotFoundException {
 String[] splitstring = str.split(":");
 if (splitstring.length < 2) {
 System.out.println("User did not enter both username and captcha code.");
 throw new UsernameNotFoundException("Must specify both username captcha code");
 }
 String username = splitstring[0];
 String kaptchaReceived = splitstring[1];
 String kaptchaExpected = splitstring[2];
 Users users = UsersDAO.findByUserNameCaptcha(username, kaptchaReceived,kaptchaExpected);
 boolean enabled = true;
 boolean accountNonExpired = true;
 boolean credentialsNonExpired = true;
 boolean accountNonLocked = true;
 return new User(
 users.getUserName(),
 users.getUserPassword(),
 enabled,
 accountNonExpired,
 credentialsNonExpired,
 accountNonLocked,
 getAuthorities(users.getRole().getRoleId().intValue())
 );
}

  1. 扩展UsernamePasswordAuthenticationFilter并重写MultipleInputAuthenticationFilter类中的obtainUsernameHttpServletRequest请求)方法。
@Override
  protected String obtainUsername(HttpServletRequest request) {
  String username = request.getParameter(getUsernameParameter());
  String kaptcha = request.getParameter(getExtraParameter());
  String kaptchaExpected = (String) request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
  String combinedUsername = username + getDelimiter() + kaptcha + getDelimiter() + kaptchaExpected;
  System.out.println("Combined username = " + combinedUsername);
  return combinedUsername;
  }

工作原理...

访问以下 URL:

http://localhost:8080/SpringSecurity_MultipleInputAuth/login

Kaptcha servlet 在浏览器上为用户显示不同的图表。

用户输入的值和Kaptcha生成的值与UsersDAOImpl.java类中的Username字段一起与数据库中的值进行比较。当所有条件匹配时,即用户输入的Kaptcha应与浏览器显示的Kaptcha相同,并且用户名应存在于数据库中,那么用户被认为已通过验证。用户将被重定向到经过验证和授权的页面。

工作原理...

另请参阅

  • 具有多个身份验证提供程序的 Spring 安全性配方

  • 具有多个输入身份验证的 Spring 安全性配方

  • 具有 JAAS 的 Spring 安全性配方

具有 JAAS 的 Spring 安全性

在第一章中,基本安全性,我们已经演示了如何在 JBOSS 中使用 JAAS 配置进行身份验证和授权。 Spring 安全性还提供了完全支持以实现基于 JAAS 的身份验证。我们需要将DefaultJaasAuthenticationProvider配置为身份验证提供程序。在本节中,我们将演示将 Spring 安全性与 JAAS 集成。

让我们看一些由 Spring 安全性 API 提供的基于 JAAS 的类和接口:

  • org.springframework.security.authentication.jaas

  • AbstractJaasAuthenticationProvider

  • AuthorityGranter

  • DefaultJaasAuthenticationProvider

  • DefaultLoginExceptionResolver

  • JaasAuthenticationCallbackHandler

  • JaasAuthenticationToken

  • JaasGrantedAuthority

  • JaasNameCallbackHandler

  • 登录异常解析器

  • SecurityContextLoginModule

准备就绪

  • 通过org.springframework.security.authentication.jaas.AuthorityGranter实现AuthorityGranter接口

  • 通过javax.security.auth.spi.LoginModule实现LoginModule接口

  • context.xml文件中配置DefaultJaasAuthenticationProvider类。实现AuthorityGranter接口及其配置。

如何做...

以下是使用 Spring 安全实现 JAAS 的步骤:

  1. 使用AuthorityGranterImpl类实现AuthorityGranter
public class AuthorityGranterImpl implements AuthorityGranter {
  public Set<String> grant(Principal principal) {
    if (principal.getName().equals("publisher"))
      return Collections.singleton("PUBLISHER");
    else
      return Collections.singleton("EDITOR");
  }
}
  1. 使用javax.security.auth.spi包中的LoginModule类,使用LoginModuleImpl
public class LoginModuleImpl implements LoginModule {
  private String password;
  private String username;
  private Subject subject;
  public boolean login() throws LoginException {
    // Check the password against the username "publisher" or "editor"
    if (username == null || (!username.equals("publisher") && !username.equals("editor"))) {
      throw new LoginException("User not valid");
    }
    if (password == null || (!password.equals("publisher123") && !password.equals("editor123"))) {
      throw new LoginException("Password not valid");
    } else {
      subject.getPrincipals().add(new UserPrincipal(username));
      return true;
    }
  }

  @Override
  public boolean abort() throws LoginException {
    // TODO Auto-generated method stub
    return false;
  }

  @Override
  public boolean commit() throws LoginException {
    // TODO Auto-generated method stub
    return true;
  }

  @Override
  public boolean logout() throws LoginException {
    // TODO Auto-generated method stub
    return false;
  }

  public void initialize(Subject subject, CallbackHandler callbackHandler,
    Map<String, ?> state, Map<String, ?> options) {
    this.subject = subject;
    try {
      NameCallback nameCallback = new NameCallback("prompt");
      PasswordCallback passwordCallback = new PasswordCallback("prompt", false);
      callbackHandler.handle(new Callback[]{nameCallback,passwordCallback});
      password = new String(passwordCallback.getPassword());
      username = nameCallback.getName();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}
  1. 使用 JAAS 配置 Spring 安全。
  <sec:authentication-manager>
    <sec:authentication-provider ref="jaasAuthProvider" />
  </sec:authentication-manager>
  <bean id="jaasAuthProvider" class="org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider">
    <property name="configuration">
      <bean class="org.springframework.security.authentication.jaas.memory.InMemoryConfiguration">
        <constructor-arg>
          <map><entry key="SPRINGSECURITY">
            <array>
              <bean class="javax.security.auth.login.AppConfigurationEntry">
                <constructor-arg value="org.packt.springsecurityjaas.LoginModuleImpl" />
                <constructor-arg>
                  <util:constant static-field="javax.security.auth.login.AppConfigurationEntry$LoginModuleControlFlag.REQUIRED" />
                </constructor-arg>
                <constructor-arg>
                  <map></map>
                </constructor-arg>
              </bean>
            </array>
          </entry>
          </map>
        </constructor-arg>
      </bean>
    </property>
    <property name="authorityGranters">
      <list>
        <bean class="org.packt.springsecurityjaas.AuthorityGranterImpl" />
      </list>
    </property>
  </bean>
</beans>

它是如何工作的...

访问 URL:http://localhost:8080/SpringSecurity_Jaas/

使用以下凭据登录:publisher/publisher123editor/editor123

身份验证由DefaultJaasAuthenticationProvider处理。用户信息和身份验证由InMemoryConfiguration处理,这意味着 JAAS 的LoginModule类使用callbackhandlers进行身份验证和授权。成功验证后,用户将被重定向到授权页面。以下截图显示了应用程序的工作流程:

它是如何工作的...它是如何工作的...

另请参阅

  • 使用多个身份验证提供程序的 Spring 安全配方

  • 使用多个输入验证的 Spring 安全配方

  • 使用 JAAS 的 Spring 安全配方

posted @ 2024-05-24 10:56  绝不原创的飞龙  阅读(4)  评论(0编辑  收藏  举报