使用 Spring Security LDAP 实现身份验证

news/2024/12/29 18:24:18/

如果你曾经在生产级应用程序中实现过登录功能,你一定听说过 LDAP 身份验证。

LDAP 可用于任何类型的分层目录信息,LDAP 最流行的用途是存储组织数据。

举个例子,比如说你们公司一个典型的组织结构,那么一般都有董事、经理、监事等职位。这些呢就是 LDAP 机制最适合实现的分层数据。通过这种方式,大多数组织使用它来维护组织信息,包括其凭证。

因此,我们接下来演示一下 Spring Security LDAP 身份验证示例。

1、LDAP 是什么?

LDAP 代表轻量级目录访问协议。它是一种开放的、供应商中立的行业标准应用程序协议,用于通过网络访问和维护分布式目录信息服务。

2、LDAP 是怎么工作的?

比如上面那个例子,LDAP 可用来存储组织的用户信息。

当用户尝试登录应用程序时,它会检查 LDAP 以查看其是否是有效用户以及该用户是否具有所需的权限和有效凭据。

在 Spring Security 的上下文中,应用程序连接到 LDAP 服务器,以便根据该 LDAP 验证有效用户。

3、LDIF 是什么?

LDIF 是 LDAP 数据交换格式的缩写。

它是文件的标准纯文本数据交换格式,用于表示 LDAP 目录内容,文件的扩展名是“.ldif”。

LDIF 文件中有多个字段,详细了解一下:

3.1 LDIF 文件中有哪些常见字段?

1)dn:专有名称

“dn”代表唯一标识目录中条目的名称。

DN 是必填字段,如果 DN 中存在逗号,则必须使用反斜杠(\)对逗号进行转义。例如,dn: uid=sam, ou=people, o=example.com Bolivia\,S.A.

2)o:组织

“o”代表组织名称。例如:juejin.cn

3)dc:域分量

“dc”代表域的每个组成部分。例如 www.example.com 将写为 DC = www, DC = example, DC = com

4)ou:组织单位

“ou”代表用户所属的组织单位(或有时是用户组)。如果用户属于多个组,您可以通过提供多个条目来指定它。例如:OU = 员工,OU = 管理员。

5)cn:通用名

“cn”代表您正在查询的单个对象(人名、会议室、菜谱名称、职位等)。它是人们常用的。例如,cn:郭德纲。至少需要一个通用名称。

6)sn:姓氏

“sn”代表人的姓氏。例如,sn:郭。需要提供姓氏。

7)objectClass:对象类

“objectClass”指定与此条目一起使用的对象类。

它是必填字段。例如:objectClass:person,objectClass:organizationalPerson。

应包含此对象类规范,因为许多 LDAP 客户端在对个人或组织人员进行搜索操作期间需要它。

3.2 LDIF 文件的格式是什么?

LDIF 由一个或多个以空行分隔的目录条目组成。每个 LDIF 条目都包含一个可选的条目 ID、一个必需的专有名称、一个或多个对象类以及多个属性定义。

LDIF 格式在 RFC 2849 LDAP 数据交换格式 (LDIF) 中定义。 Sun Java System Directory Server 符合此标准。

LDIF 中表示的目录项的基本形式如下:

dn: distinguished_name 
objectClass: object_class 
objectClass: object_class 
...
attribute_type[;subtype]:attribute_value
attribute_type[;subtype]:attribute_value 
... 

我们必须提供 DN 和至少一个对象类定义。此外,我们必须包含为条目定义的对象类所需的任何属性。

所有其他属性和对象类都是可选的。我们可以按任何顺序指定对象类和属性。冒号后面的空格也是可选的。

例如,下面是 LDIF 文件中的有效条目,这里的“uid”字段代表该人的用户 ID。

dn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: Bob Hamilton
sn: Hamilton
uid: bob
userPassword: bobspassword

4、如何使用 Spring Boot 实现 Spring Security LDAP 身份验证?

假设我们有一个应用程序,我们已经使用 Spring 框架提供的 Spring Security API 实现了基本的安全性。现在我们必须实施 LDAP 身份验证以使其更加安全。

因此,我们需要开发一个基本的 Web 应用程序,然后实现 LDAP 身份验证概念来满足 Spring Security LDAP 身份验证示例的要求。

以下是详细步骤:

步骤1:创建 Spring Boot Starter 项目

创建入门项目时,选择“Spring Web”、“Spring LDAP”、“嵌入式 LDAP 服务器”、“Spring security”和“Spring Boot DevTools”作为入门项目依赖项。

这里“Spring Boot Dev Tools”是可选添加的。我们使用 Spring Boot Dev Tools 来实现热加载,避免在测试应用程序时多次重新启动 tomcat 服务器。除此之外,我们还需要添加一个额外的依赖项“spring-security-ldap”。

以下是我们使用 Spring Boot 开发此 Spring Security LDAP 身份验证示例所使用的依赖项。

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> 
</dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-ldap</artifactId> 
</dependency> 
<dependency> <groupId>com.unboundid</groupId> <artifactId>unboundid-ldapsdk</artifactId> <!-- <scope>test</scope> --> 
</dependency> 
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-ldap</artifactId> 
</dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> 
</dependency> 
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> 
</dependency> 

步骤2:创建一个 .ldif 文件作为 ldap-data.ldif

该文件将具有强制的 .ldif 扩展名。在 src/main/resources 文件夹下创建一个名为“ldap-data.ldif”的文件并更新数据,如下所示。

我们从 Spring 官方文档网站获取此文件。它将作为本地目录工作,并具有兼容的数据以相应地与 LDAP 配合使用。

dn: dc=springframework,dc=org
objectclass: top
objectclass: domain
objectclass: extensibleObject
dc: springframeworkdn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groupsdn: ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: subgroupsdn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: peopledn: ou=space cadets,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: space cadetsdn: ou="quoted people",dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: "quoted people"dn: ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: otherpeopledn: uid=ben,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Ben Alex
sn: Alex
uid: ben
userPassword: $2a$10$c6bSeWPhg06xB1lvmaWNNe4NROmZiSpYhlocU/98HNr2MhIOiSt36dn: uid=admin,ou=people,dc=springframework,dc=org 
objectclass: top 
objectclass: person 
objectclass: organizationalPerson 
objectclass: inetOrgPerson 
cn: Admin 
sn: Admin 
uid: admin 
userPassword: $2a$10$ODZhWAyD0i8zruWQcMYx9ePkW2xXsqIljhh4K8spSOUCY897ERkwudn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Bob Hamilton
sn: Hamilton
uid: bob
userPassword: bobspassworddn: uid=joe,ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Joe Smeth
sn: Smeth
uid: joe
userPassword: joespassworddn: cn=mouse, jerry,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Mouse, Jerry
sn: Mouse
uid: jerry
userPassword: jerryspassworddn: cn=slash/guy,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: slash/guy
sn: Slash
uid: slashguy
userPassword: slashguyspassworddn: cn=quote"guy,ou="quoted people",dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: quote"guy
sn: Quote
uid: quoteguy
userPassword: quoteguyspassworddn: uid=space cadet,ou=space cadets,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Space Cadet
sn: Cadet
uid: space cadet
userPassword: spacecadetspassworddn: cn=developers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfUniqueNames
cn: developers
ou: developer
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
uniqueMember: uid=bob,ou=people,dc=springframework,dc=orgdn: cn=managers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfUniqueNames
cn: managers
ou: manager
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
uniqueMember: cn=mouse, jerry,ou=people,dc=springframework,dc=orgdn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfUniqueNames
cn: submanagers
ou: submanager
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org

在这里,我们突出显示了一个用户的数据,我们将使用该数据来测试已实施的示例。

步骤3:更新 application.properties 文件

在此文件中,我们将拥有嵌入式 LDAP 的一些属性,如下所示。

spring.ldap.embedded.port=8389
spring.ldap.embedded.ldif=classpath:ldap-data.ldif
spring.ldap.embedded.base-dn=dc=springframework,dc=org

步骤4:创建一个用于基本身份验证的控制器类

下一步是创建一个 RestController 类。

当我们输入凭据以登录应用程序时,我们将看到一条成功登录消息。让我们创建一个简单的控制器 LoginController.java,如下所示。

@RestController
public class LoginController {@GetMapping("/")public String getLoginPage() {return "登录成功!";}
} 

步骤5:从 LdapSecurityConfig.java 创建一个配置类

这是我们将保留 LDAP 身份验证的整个逻辑的配置类,如下所示。

@Configuration
public class LdapSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin();}@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.ldapAuthentication().userDnPatterns("uid={0},ou=people").groupSearchBase("ou=groups").contextSource().url("ldap://localhost:8389/dc=springframework,dc=org").and().passwordCompare().passwordEncoder(new BCryptPasswordEncoder()).passwordAttribute("userPassword");}
} 

⚠️ 注意:如果您使用 Spring Security 5.7.0-M2 或更高版本,不要使用 WebSecurityConfigurerAdapter。

5、如何测试应用程序?

为了测试应用程序,我们需要打开浏览器并点击 URL http://localhost:8080/。

点击 URL 后,将显示默认登录页面。此外,输入 ldap-data.ldif 文件中给出的任何凭据。

如果输入的凭据正确,您将看到使用 LDAP 身份验证成功登录的消息。例如,让我们观察 ldap-data.ldif 文件中的以下用户数据。

dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Admin
sn: Admin
uid: admin
userPassword: $2a$10$ODZhWAyD0i8zruWQcMYx9ePkW2xXsqIljhh4K8spSOUCY897ERkwu

这里,aid是用户名,userpassword是密码的加密值。密码的真实值为“admin@123”。

因此,我们需要提供 admin@123 作为密码。单击登录按钮后,您应该在屏幕上看到消息“登录成功!”。

我们可以使用下面的程序类来对任何密码进行编码。

public class PasswordEncoder {public static void main(String[] args) {System.out.println(new BCryptPasswordEncoder().encode("admin@123"));}
} 

6、总结

在本文中,我们涵盖了“使用 Spring Boot 的 Spring Security LDAP 身份验证示例”的所有理论和示例部分。

最后,您应该能够实现 Spring Security LDAP 身份验证。同样,您也可以根据自己的要求进一步扩展此示例。也尝试在您的项目中相应地实现它。


http://www.ppmy.cn/news/1042331.html

相关文章

第 4 章 链表(1)

4.1链表(Linked List)介绍 链表是有序的列表&#xff0c;但是它在内存中是存储如下 小结: 链表是以节点的方式来存储,是链式存储每个节点包含 data 域&#xff0c; next 域&#xff1a;指向下一个节点.如图&#xff1a;发现链表的各个节点不一定是连续存储.链表分带头节点的链…

Unity制作一个简单的登入注册页面

1.创建Canvas组件 首先我们创建一个Canvas画布&#xff0c;我们再在Canvas画布底下创建一个空物体&#xff0c;取名为Resgister。把空物体的锚点设置为全屏撑开。 2.我们在Resgister空物体底下创建一个Image组件&#xff0c;改名为bg。我们也把它 的锚点设置为全屏撑开状态。接…

rust踩雷笔记3——生命周期的理解

目录 概念和基本使用一个例子彻底理解最基本的内容 一个例子理解函数签名为什么要有生命周期标注⭐️能不能对编译器蒙混过关&#xff1f; 生命周期是rust中最难的概念——鲁迅 这一块内容即便是看rust圣经&#xff0c;第一遍也有点懵。今天早上二刷突然有了更直观的认识&…

springboot数据库密码加密的配置方法_Java

前言 由于系统安全的考虑&#xff0c;配置文件中不能出现明文密码的问题&#xff0c;本文就给大家详细介绍下springboot配置数据库密码加密的方法&#xff0c;下面话不多说了&#xff0c;来一起看看详细的介绍吧 1.导入依赖 <!--数据库密码加密--> <dependency>&…

【算法心得】正确估计dfs时间复杂度;剪枝优化不怕重构

https://leetcode.cn/problems/verbal-arithmetic-puzzle/ 这题看到题&#xff0c;“表达式中使用的不同字符数最大为 10”&#xff0c;就觉得dfs就完事了&#xff0c;最多不过10!&#xff0c;10!才1e6&#xff0c;1e7这样。如果字符再少点&#xff0c;6! 7! 8!的&#xff0c;…

Java之抽象类

Java之抽象类 抽象类概念抽象类如何使用抽象类的特性 作者简介&#xff1a; zoro-1&#xff0c;目前大一&#xff0c;正在学习Java&#xff0c;数据结构等 作者主页&#xff1a;zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f49…

Pandas+Pyecharts | 成都大运会奖牌数据分析可视化

文章目录 &#x1f3f3;️‍&#x1f308; 1. 导入模块&#x1f3f3;️‍&#x1f308; 2. Pandas数据处理2.1 读取数据2.2 数据信息2.3 数据处理 &#x1f3f3;️‍&#x1f308; 3. Pyecharts数据可视化3.1 每日奖牌数量分布3.2 奖牌榜单TOP20金银铜牌分布3.3 各比赛项目金牌…

微服务参数透传实现

说明&#xff1a;在微服务架构中&#xff0c;用户身份经网关验证后&#xff0c;我们可以将用户信息&#xff0c;如ID加入到请求头上。后面的微服务中&#xff0c;可以设置一个拦截器&#xff0c;拦截请求&#xff0c;获取请求头上的用户ID&#xff0c;加入到ThreadLocal中。 最…