vlambda博客
学习文章列表

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

第 7 章。带有 Wicket 的 Spring Security

在本章中,我们将介绍:

  • 带有 Wicket 的 Spring Security – 基本数据库身份验证

  • Spring Security with Wicket – 基于 Spring 表单的数据库身份验证

  • Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

  • 带有 Wicket 授权的 Spring 身份验证

  • 使用 Wicket 和 Spring Security 的多租户

介绍


在启动 Wicket 之前,我们正在检查可用的版本。最新的是6.9。在 Apache Wicket 站点中明确提到,最新的项目应该使用版本 6.9 作为基础。下载 NetBeans 的 Wicket 插件后,我们有了 NetBeans 7.1。我们发现 net beans Wicket 插件支持 1.5 版的 Wicket。

我们更喜欢使用最新的稳定版本;它将有许多错误修复和升级,并将使其更容易开发。

Wicket 还使用 Wicket 过滤器 来分派请求和响应。就像 GWT 和 Vaadin 应用程序一样,它们有 servlet,需要初始化一些参数,例如 UI 类,我们需要提供扩展 Web 应用程序 类作为过滤器的参数。然后是类,它们扩展了 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

访问以下网址:

http://localhost:8080/spring-security-wicket/

该 URL 将显示 Wicket 应用程序的欢迎页面。 Wicket 应用程序设置已准备就绪。

Wicket 还带有自己的身份验证和授权 API。让我们看看如何使用它。

带有 Wicket 的 Spring Security – 基本数据库身份验证


我们的目标是对 Wicket 应用程序进行简单的基本身份验证。当我们访问 Wicket 应用程序的 URL 时,我希望弹出一个登录对话框。成功后,它应该被重定向到主页。我们需要将 Spring Security 依赖项添加到 pom.xml 文件并重建 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>
  2. 使用 Spring 监听器和 Spring 过滤器 Web.xml 文件="indexterm"> 带有 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>
  3. 编辑 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 应用程序。

显示的页面如以下屏幕截图所示:

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证
读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

也可以看看

  • 带有 Wicket 的 Spring Security - 基于 Spring 表单的身份验证 配方

  • Spring Security with Wicket – 自定义 JSP 基于表单的身份验证 配方

  • 带有 Wicket 授权的 Spring 身份验证 配方

  • 使用 Wicket 和 Spring Security 的多租户 秘诀

Spring Security with Wicket – 基于 Spring 表单的数据库身份验证


我们之前的 recipe 中,我们发现 Wicket 6.9 与 Spring Security 非常兼容而且很容易集成。我们所做的只是添加 spring 依赖项并配置 applicationContext.xml 文件。

在本节中,我们将使用 Spring 表单进行身份验证。我们希望 Spring 表单出现在对话框的位置并为我们进行身份验证。

准备好

  • 创建 Maven Wicket 项目:spring-security-wicket_springform

  • 使用 Spring 依赖项更新 pom.xml 文件。

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

  • 使用 Spring 侦听器编辑 web.xml

  • 创建一个数据库 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,该 URL 将具有会话 ID。

这个怎么运作...

现在访问以下 URL:

http://localhost:8080/spring-security-wicket_springform/

在这个 示例中,我们展示了如何在 Wicket 应用程序中调用 Spring 的内部登录表单。当我们访问 Wicket 应用程序时,我们将被重定向到 Spring 自己的登录页面。用户输入他们的用户名和密码,这些用户名和密码将由 Spring 的身份验证提供程序进行身份验证和授权。成功后,用户可以访问 Wicket 应用程序。

当您访问上述 URL 时,您应该会看到以下屏幕:

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

也可以看看

  • Spring Security with Wicket – 自定义 JSP 基于表单的身份验证 配方

  • 带有 Wicket 授权的 Spring 身份验证 配方

  • 使用 Wicket 和 Spring Security 的多租户 秘诀

Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证


前两个秘籍是测试 Wicket 与 Spring Security 的 兼容性。它还 展示了将 spring 与 Wicket 集成是多么容易。我们从我们的两个 Wicket 配方中了解到,我们可以轻松地将 Spring-basic 和 Spring-form-b​​ased 身份验证与数据库一起使用,并且同样可以扩展到 LDAP。

在这个秘籍中,我们将添加一个定制的 JSP 表单。我们希望 Wicket 应用程序调用我们的 JSP 表单进行登录。如果开发人员不想创建 Wicket 表单,他们可以使用这种方法。这种方法也适用于 GWT 和 Vaadin。

您还需要授予对登录页面的匿名访问权限。

准备好

  • 创建一个 Maven Wicket 项目:spring-security-wicket_customized_jsp

  • 使用 Spring 依赖项更新 pom.xml 文件。

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

  • 使用 Spring 侦听器编辑 web.xml

  • 还将 login.jsp 配置作为 servlet 添加到 web.xml

  • 创建数据库,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 文件中配置为匿名用户。

  2. 编辑 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 时,您应该会看到以下屏幕截图:

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

也可以看看

  • 带有 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>
  2. 编辑 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" />
  3. 编辑 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);
          }
        });
      }
    }
  4. 编辑 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();
        }
      }
    }
  5. 编辑 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);
          }
        });
    
      }
    
    }
  6. 编辑 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 登录表单。点击 submit 后,我们需要获得一个经过身份验证的会话,这种方法将为我们提供将 Spring 安全性与我们拥有的 Wicket 应用程序集成的选项使用 Wicket 应用程序创建了一个 登录表单。 Spring 在成功时验证用户凭据并与 Wicket 框架通信以显示相应的授权页面。

与 Spring 安全集成的 Wicket 应用程序的工作流程解释如下。

当用户点击 URL: http://localhost:8080/spring-security-wicket/ 时,允许用户访问主页。主页显示两个链接,表示两个不同的角色和用户。成功认证后,用户将被授权使用基于角色的相应页面。这些页面显示在以下屏幕截图中:

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

应用启动首页

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

登录页面

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

作者页面

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

也可以看看

  • 使用 Wicket 和 Spring Security 的多租户 秘诀

使用 Wicket 和 Spring Security 的多租户


多租户 已成为 云的流行词。在多租户设置中,每个租户将有一个 单独的 数据源。我们需要创建两个不同的数据源并查找数据源。让我们使用一个带有自定义 JSP 的简单 Wicket 应用程序,它将有一个租户下拉菜单。用户从下拉菜单中选择一个租户,将设置一个与该租户对应的数据源。

我是 使用 NetBeans IDE, 可以轻松识别 Maven 项目。 NetBeans 还附带 glassfish 应用程序服务器和 derby 数据库。

准备好

  • 更新 login.jsp 文件

  • 使用 derby 数据库依赖项更新 pom.xml 文件

  • 编辑 applicationContext.xml

  • 编辑 spring-security.xml

  • 编辑 web.xml 文件

  • 创建过滤器以捕获租户 ID

  • 还要在derby中创建两个数据库

  • 在两个数据库中创建两个表 USERSUSER_ROLES

  • USERS 中添加列(USER_IDUSERNAME密码)

  • USER_ROLE 中添加列(USER_IDUSER_ROLE_IDAUTHORITY)

怎么做...

以下步骤用于使用 Spring Security API 在 Wicket 应用程序中实现多租户:

  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>
    
  2. 编辑 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" />
  3. 编辑 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>
  4. 编辑 TenantRoutingDataSource.java 文件以路由 租户到不同的 数据源。该类是 spring 的 AbstractRoutingDataSource 的子类。它用于设置数据源。

    网址:http://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;
      }
    }
  5. 编辑 MultitenantFilter 以捕获 tenant 类型并设置数据源:

    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. 中设置租户信息。用户通过租户数据源的身份验证并被带到主页。

登录页面应用启动 将如下图所示:

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

登录页面

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

如果租户没有例外

读书笔记《spring-security-3-x-cookbook》Spring Security with Wicket – 定制的基于 JSP 表单的数据库认证

显示选定的错误凭据异常