尽管有许多博客文章详细介绍了如何使用Spring Security,但是当问题域位于标准LDAP或数据库身份验证之外时,我仍然经常发现配置挑战。 在本文中,我将介绍一些针对Spring Security的简单自定义,使其能够与基于REST的API调用一起使用。 具体来说,用例是您拥有一个API服务,该服务将返回包含SHA-256密码哈希的用户对象。
设定
运行此样本的先决条件是Git和Maven,以及您选择的IDE(已通过Eclipse和IntelliJ进行了测试)。
可以在以下位置找到源代码: https : //github.com/dajevu/Spring3SecurityUsingAPI 。 拉下代码后,执行以下步骤:
- 在终端窗口中,cd到位于源代码所在根目录下的Shared目录。
- 发出命令mvn clean install。 这将构建Shared子项目,并将jar安装到本地mvn存储库中。
- 在Eclipse或IntelliJ中,将项目导入为Maven项目。 在Eclipse中,这将导致创建3个项目:Shared,SpringWebApp和RestfulAPI。 在IntelliJ中,这将表示为子项目。 编译过程完成后,不应有任何错误。
- 将目录更改为RestfulAPI。 然后,发出命令mvn jetty:run以运行API webapp。 然后,您可以发出以下URL,该URL将带回以JSON表示的User对象:http:// localhost:9090 / RestfulAPI / api / v1 / user / john
- 打开一个新的终端窗口,cd到位于项目根目录下的SpringWebApp目录。 发出命令mvn jetty:run。 这将启动一个包含Spring Security的标准Spring Webapp。 您可以在以下位置访问单个HTML页面:http:// localhost:8080 / SpringWebApp /。 单击“登录”链接后,使用用户名john和密码doe登录。 您应该被重定向到Hello Admin页面。
为了演示该解决方案,使用了三个Maven模块,如下所示:
- SpringWebApp 。 这是一个典型的Spring Webapp,仅提供一个JSP页面。 页面的内容将取决于用户当前是否登录。 首次访问该页面时,将显示一个Login链接,该链接将其定向到内置的Spring Security登录表单。 当他们尝试登录时, R ESTEasy客户端用于调用API服务(如下所述),该服务返回一个JSON字符串,该字符串通过RESTEasy客户端转换为Java对象。 以下各节讨论了如何配置Spring Security的详细信息。
- RestfulAPI 。 提供JSON请求的API服务。 它是使用RESTEasy(JAX-RS实现)配置的,下一节将对其进行详细描述。
- 共享的 。 它包含其他两个项目之间共享的一些Java类。 具体来说,用户对象DTO和RESTEasy代理定义(由于RESTEasy客户端也可以使用它,因此已共享)。
RestfulAPI解剖
API Webapp是使用RESTEasy的Spring实现配置的。 RESTEasy文档非常详尽,因此我将不对其设置进行详细说明。 定义了一个API调用(在Shared项目的UserProxy中),该调用返回静态JSON字符串。 API的代理(或接口)定义如下:
Resteasy API代理
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path(UserProxy.Urls.BASE_URL)
public interface UserProxy {public interface Urls {public static final String BASE_URL = "/api/v1";public static final String USER = "/user/{username}";}@GET@Produces( { MediaType.APPLICATION_JSON })@Path(UserProxy.Urls.USER)public User getUserByUsername(@PathParam("username") String username);
}
对于那些熟悉JAX-RS的人来说,您将可以轻松地遵循此配置。 它定义了一个API URI,它将响应发送到/ api / v1 / user / {username}的URL路径的请求,其中{username}被替换为实际的用户名值。 该服务的实现仅返回一个静态响应,如下所示:
关于唯一复杂的事情是使用用户密码的SHA-256哈希。 很快我们将看到Spring Security如何解释这一点。 访问URL时,将返回以下JSON字符串:
该Webapp的web.xml包含用于服务于RESTEasy请求的设置配置,因此,如果您好奇的话,请看一下。
SpringWebApp解剖
现在我们可以看一下Spring Security的配置。 项目的web.xml文件将其配置为Spring应用程序,并将文件applicationContext-security.xml指定为初始Spring配置文件。 让我们仔细看一下这个文件,因为这是大多数魔术发生的地方:
让我们遍历每个行号以描述其功能。 第3至5行指示Spring在com.acme目录中查找Spring支持的类,并且将支持Spring批注。 第7行用于加载application.properties文件中指定的属性(用于指定API主机)。 第9至11行为该应用程序启用Spring Security。 通常,作为http的子元素,您将指定使用角色来保护哪些页面,但是为了使本示例简单起见,未对其进行配置。
第13-17行是基于Spring Security的自定义开始的地方。 我们通过其bean ref定义了一个名为userDetailsSrv的自定义身份验证提供程序。 该bean通过自定义类com.acme.security.UserDetailsService实现(第19行)。 让我们仔细看一下这个类:
如您所见,此类实现了Spring接口org.springframework.security.core.userdetails.UserDetailsService。 这需要覆盖方法loadUserByUsername。 此方法负责从身份验证提供者/源中检索用户。 返回的用户(或者,如果找不到匹配的用户,则抛出UsernameNotFoundException-第28行)必须包含密码属性,以便Spring Security与表单中提供的内容进行比较。 如前所述,在这种情况下,密码以SHA-256哈希值返回。
在我们的API实现中,使用APIHelper类提取用户查找,我们将在后面介绍。 然后,将返回的API数据填充到名为UserDetails的自定义类中。 这将使用相同的名称实现Spring接口。 该接口需要getUsername()和getPassword()方法的具体实现。 Spring将在Security的下一个处理步骤中调用这些值,以将这些值与Web表单中记录的值进行比较。
Spring如何将SHA-256中返回的密码与表单密码值进行比较。 如果回头看一下XML配置,它包含以下设置:
注意passwordEncoder -该参考指向Spring类ShaPasswordEncoder。 此类将计算通过Web表单提供的密码的SHA-256密码,然后Spring会将计算出的值与我们通过API返回的值进行比较。
让我们通过看一下APIHelper类来解决这个问题:
第8和9行的第一件事是注入API.host属性。 您还记得,这是在application.properties文件中设置的。 这标识了要在其中发布API调用的主机(因为它在本地运行,所以指定了localhost)。 第17至20行使用RESTEasy客户端机制之一发布JSON RESTful调用(RESTEasy也具有所谓的客户端代理实现,它更易于使用/代码更少,但没有提供太多的低级控制)。 然后,通过第26行的Jackson方式,将来自API的结果响应从JSON转换为User Java对象。然后将该Java对象返回给UserDetails服务。
总结/总结
如您所见,定制Spring Security以针对API调用(或实际上是任何外部服务)进行身份验证所涉及的实际工作非常简单。 只需要实现几个类,但是尝试第一次弄清楚这一点可能很棘手。 因此,之所以我包括完整的端到端示例。
参考:来自Jeff's SOA Ruminations博客的JCG合作伙伴 Jeff Davis 使用API身份验证的Spring Security 。
翻译自: https://www.javacodegeeks.com/2012/10/spring-security-using-api-authentication.html