Spring Security Kerberos 环境搭建

本文详述了Spring Security Kerberos的环境搭建过程,包括域环境准备、服务器端口配置、测试程序编写和浏览器配置。针对服务器操作系统已入域的情况,通过创建keytab文件实现与KDC的认证。主要内容包括配置AD域、KDC/DNS设置、服务器和客户端的调整,以及解决现场防火墙端口问题。文章旨在帮助开发者理解如何在实际项目中应用Kerberos进行身份验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2019年初,领导刘哥给了我一个很棘手的任务,让研究下Kerberos认证原理,因为半年后有个项目会依赖Kerberos实现SSO。我心里暗暗骂了一句,我这么忙能不能让那些每天吹牛的人研究下呀,我激动的拍了下桌子,满脸笑容的答应了。

Kerberos是啥呀,我问了问百度,翻掉前两页的广告之后,我终于查到了一些资料。

  1. Spring Security Kerberos 配置IWA的关键步骤备忘:https://blog.csdn.net/wwwcomy/article/details/84883086
  2. Kerberos基本概念及原理汇总:https://cloud.tencent.com/developer/article/1381306
  3. 第十二章 Spring Security扩展:https://www.iteye.com/blog/lengyun3566-1404943
  4. spring-security-kerberos官网:https://spring.io/projects/spring-security-kerberos

以上内容讲的都挺好,头一个礼拜我Google了各种网站,终于在我的努力下懂得了原来这就是Kerberos,我真的懂了,但我应该怎么做呢。就好像我懂得了这就是涡轮增压的原理,行了,那你去设计台车吧,我拿着手中的锤子不知道该砸向谁。其实网上的内容大部分是讲解Kerberos的认证原理,都是很底层的内容,当然对于开发人员来说掌握其中的原理是必要的,但只需要简单了解即可,不需要深入研究,因为有很多框架已经把内部的逻辑实现了,我们只要按照相关的配置信息简单配置即可。针对Kerberos认证原理,我们只需了解:

  1. 大致的认证过程
  2. 所需要的机器种类
  3. 所需要的机器环境。

如何将Kerberos用到具体的项目中才是我们真正需要掌握的,而网上能够查到的资料又非常稀少,为了将我2个月的研究过程进行整理,并希望能给恰巧遇到同样问题的你提供一点帮助,特整理了本篇文章,当然内容肯定会由很多错误的地方,还望嘴下留情,我们才好共同探讨。

环境介绍

先说下甲方爸爸的机器环境:

  • 服务器操作系统:Windows Server2016 R2,服务器已经入域
  • 部署中间件:Tomcat 8.5,运行环境Java,框架Spring Boot 2B/S框架
  • 客户端操作系统:Windows 10,客户端同样已经入域、客户端浏览器为万恶的IE9(当时微软还没有丢弃它)
    再说下我的需求:

我们未来要做一个系统,这个系统支持两种登录方式:

  1. 用户名+密码。访问地址为http://www.jiafangbaba.com/login之后跳转到登录页面,输入用户名和密码,之后登录进入主页。这种实现过程就不说了,每个公司都有自己的一套权限认证体系。
  2. 域认证。访问地址http://www.jiafangbaba.com之后,根据当前的域用户信息直接跳转到系统首页。
    本篇内容只是关于如何实现SSO的,对于上述两种登录方式的逻辑实现,以后再写。

实施步骤

整个实现步骤大致分为下面几步:

  1. 域环境准备
  2. 服务器端口配置
  3. 编写测试程序
  4. 浏览器配置
  5. 验证成果
域环境准备

先在VM上创建三台虚机,相关配置如下:

KDC/DNSServerClient
OSWindows Server 2016Windows Server 2016Win10
IP192.168.150.138192.168.150.139192.168.150.140
DNS127.0.0.1192.168.150.138192.168.150.138
ADjiafangbaba.comjiafangbaba.comjiafangbaba.com
JDK-----Jdk1.8-----
Tomcat-----apache-tomcat-8.5.42-----
计算机名kdc.jiafangbaba.comserver.jiafangbaba.comclient.jiafangbaba.com
备注AD域控制器应用服务器客户端

虚机的创建步骤大家自己找找吧,到处都是:这个不会有人不知道吧。

搭建域环境的步骤大家找找吧,也到处都是:https://www.jb51.net/article/252392.htm

KDC/DNS端环境搭建详细步骤
  1. 修改计算机名为kdc
  2. 修改本机IP为静态IP(192.168.150.138),DNS地址为127.0.0.1
  3. 检查登录用户Administrator是否有密码,检查密码是否符合密码策略(默认策略密码要有大小写字母和数字)
  4. 搭建域服务器,域名为jiafangbaba.com
  5. 创建两个域用户
  • 用户名:user2(用于client端登录),密码:Tomcat2019
  • 用户名:tomcat1(用于生成keytab文件)密码:Tomcat2019
Server端
  1. 修改计算机名为server
  2. 修改本机IP为静态IP(192.168.150.139),DNS地址为192.168.150.138KDC的IP
  3. 加入域(jiafangbaba.com
Client端
  1. DNS地址为192.168.150.138(KDC的IP)
  2. 加入域(jiafangbaba.com),并用域用户(user2)登录

服务器端口配置

我没Google到端口配置的相关内容,这也是我在调试的过程中遇到的很头疼的一个问题。同样的包在我本地行,在用户现场死活跑不出来,研究了好长一段时间才明白,我本地都是把防火墙禁掉的,而现场环境是打开的,所以需要提前将相关端口策略开通,才能保证服务器之间的Kerberos验证是没有问题的。我通过抓包工具抓到了需要开放的端口。

相关端口见下表

本端本端端口本端类型对端对端端口对端类型协议功能
Client随机客户端KDC53服务端UDPDNS解析
Client随机客户端KDC88服务端TCPKerberosV5:AS Request
Client随机客户端KDC389服务端UDP访问LDAP服务
Client随机客户端KDC389服务端TCP访问LDAP服务
Server随机客户端KDC53服务端UDPDNS解析
Server随机客户端KDC88服务端UDPKerberosV5:AS Request
Server随机客户端KDC389服务端TCP访问LDAP服务
Client随机客户端Server80服务端TCPHTTP请求
备注:此处对端端口为Tomcat的端口,默认为8080,测试环境中已修改为80.

在测试环境中通过telnet命令确定机器之间是互通的,这一步非常关键。上表中的Client相关的端口大家可以忽略,因为本身Client对上面的端口是放开的,特殊情况下才需要考虑Client的上述配置。

编写测试程序

按照上述内容配置相关服务器信息之后,下面就到了真正的测试环节,首先我们要准备测试包部署到Server服务器上的Tomcat中。由于我使用的是SpringBoot框架,而spring-security-kerberosSpring对Kerbeors的实现,对于Spring Security,上来肯定是先来个WebSecurityConfig

此类的目的有两个:1、实现WebSecurityConfigurerAdapter的自动装载,其中下图红框中的adDomainadServer等参数是我们实现Kerberos认证的关键参数。

  1. 实现权限的拦截。
    我为了测试简单,把/homeall-new两个URL添加到了忽略列表,其中/home是为了验证kerberos认证是否通过,all-new是为了获取所有域用户,本篇文章不谈域用户的获取。
package com.example.demowin.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
import org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig;
import org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource;
import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;
import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Value("${app.ad-domain}")
	private String adDomain;

	@Value("${app.ad-server}")
	private String adServer;

	@Value("${app.service-principal}")
	private String servicePrincipal;

	@Value("${app.keytab-location}")
	private String keytabLocation;

	@Value("${app.ldap-search-filter}")
	private String ldapSearchFilter;
	
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.exceptionHandling()
				.authenticationEntryPoint(spnegoEntryPoint())
				.and()
			.authorizeRequests()
				.antMatchers( "/all-new/").permitAll()
				.and()
			.authorizeRequests()
				.antMatchers("/", "/home").permitAll()
				.anyRequest().authenticated()
				.and()
			.formLogin()
				.loginPage("/login").permitAll()
				.and()
			.logout()
				.permitAll()
				.and()
			.addFilterBefore(
					spnegoAuthenticationProcessingFilter(authenticationManagerBean()),
					BasicAuthenticationFilter.class);
		
		//初始化krb5config文件位置
//		initKrb5Config();
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth
			.authenticationProvider(activeDirectoryLdapAuthenticationProvider())
			.authenticationProvider(kerberosServiceAuthenticationProvider());
	}

	@Bean
	public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() 		{
		return new ActiveDirectoryLdapAuthenticationProvider(adDomain, adServer);
	}

	@Bean
	public SpnegoEntryPoint spnegoEntryPoint() {
		return new SpnegoEntryPoint("/login");
	}

	@Bean
	public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
			AuthenticationManager authenticationManager) {
		SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
		filter.setAuthenticationManager(authenticationManager);
		return filter;
	}

	/*@Bean
	public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() throws Exception {
		KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
		provider.setTicketValidator(sunJaasKerberosTicketValidator());
		provider.setUserDetailsService(ldapUserDetailsService());
		return provider;
	}*/

	@Bean
	public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
		SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
		ticketValidator.setServicePrincipal(servicePrincipal);
		ticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation));
		ticketValidator.setDebug(true);
		return ticketValidator;
	}

	@Bean
	public KerberosLdapContextSource kerberosLdapContextSource() throws Exception {
		KerberosLdapContextSource contextSource = new KerberosLdapContextSource(adServer);
		contextSource.setLoginConfig(loginConfig());
		return contextSource;
	}
	
	@Bean
	public LdapTemplate ldapTemplate() throws Exception {
		LdapTemplate template = new LdapTemplate(kerberosLdapContextSource());
		return template;
	}

	public SunJaasKrb5LoginConfig loginConfig() throws Exception {
		SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();
		loginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation));
		loginConfig.setServicePrincipal(servicePrincipal);
		loginConfig.setDebug(true);
		loginConfig.setIsInitiator(true);
		loginConfig.afterPropertiesSet();
		return loginConfig;
	}

	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}
	
	@Bean
    public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
        KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
        provider.setTicketValidator(sunJaasKerberosTicketValidator());
        provider.setUserDetailsService(dummyUserDetailsService());
        return provider;
    }
	
	@Bean
    public DummyUserDetailsService dummyUserDetailsService() {
        return new DummyUserDetailsService();
    }
}

相关配置信息说明如下:

  • ad-domain:該參數表示的是域名,可在入域的“計算機屬性”中查找,建議統一使用大寫字母表示,為JIANGFANGBABA.COM
  • ad-server:該參數表示的是ldap服務地址,對於windows server系統,我們在創建DC的時,會自動啓用該服務,所以此處地址應為域控服務器計算機名稱(包含域名),為:ldap://kdc.jiafangbaba.com;小写即可
  • service-principal:該參數表示的是認證主體的名稱,即部署應用程序的計算機名稱(包含域名),為:HTTP/server@JIAFANGBABA.COM
  • keytab-location:該參數表示的keytab文件位置,此处为/tmp/server.keytab,代表Tomcat所在的磁盘下的/tmp/server.keytab文件,假如Tomcat在D盘,那么需要将keytab文件拷贝到D:/tmp/下,具體路徑和名稱根據情況而定,此文件根據service-principal生成;
  • ldap-search-filter:ldap目錄查詢過濾條件,不需要修改。
创建keytab文件

上面的配置中有一步是需要创建keytab文件,此文件为Server服务器上的秘钥文件,我们知道kerberos认证中涉及到三方认证,分别是Client-KDCServer-KDCClient-Server之间的认证,Client-KDCClient-Server之间的认证其实是登录Client的域用户与KDC之间的认证,而Server为一台具体的服务器并没有具体的域用户登录信息,所以我们需要将Server上部署的应用程序作为一个域用户即principal进行权限的认证,那么就需要建立Server上的应用程序和域用户的映射即Server.jiafangbaba.comtomcat1域用户之间的映射关系。简单来说Client需要有个域用户,Server上同样需要一个域用户,并通过此域用户创建一个keytab秘钥文件,每次Server进行kerberos认证时,以此秘钥文件代替域用户名、密码进行相关的校验。

KDC中执行下列代码:

  1. 执行
setspn –a HTTP/server.jiafangbaba.com tomcat1
  1. 执行
ktpass /out c:\tmp\server.keytab /mapuser tomcat1@JIAFANGBABA.COM /princ HTTP/server.jiafangbaba.com@JIAFANGBABA.COM /pass Tomcat2019 /ptype KRB5_NT_PRINCIPAL /crypto ALL`
  1. 将生成的keytab文件拷贝到server端
    注意:keytab文件所在的目录一定已经被创建好
  • /out;指定keytab文件生成的位置
  • /mapuser 用户名@域名(大写)
  • /princ setspn设置的认证主体名@域名(大写)
  • /pass 用户密码
  • /ptype 认证主体类型(写这个就行)
  • /crypto 支持的加密类型

“/home”等其他地址的详细页面如下:

package com.example.demowin.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home.html");
        registry.addViewController("/").setViewName("home.html");
        registry.addViewController("/hello").setViewName("hello.html");
        registry.addViewController("/login").setViewName("login.html");
    }

}

home.html页面代码如下:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Kerberos Example</title>
    </head>
    <body>
        <h1>Welcome!</h1>
        <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
        <p>Click <a th:href="@{/all-new/}">all domain users</a> to see all domain users.</p>
    </body>
</html>

点击home.htmlClick here超链接,会跳转到/hello页面,因为我们在WebSecurityConfig类中并没有将/hello页面添加到permitAll,所以页面进入/hello之前会进行kerberos认证,认证通过之后会跳转到/hello页面,返回hello.html。同时Spring Security会将当前登录的域用户信息封装到后端HttpServletRequest对象中,在hello.html页面通过thymeleaf语法 [[${#httpServletRequest.remoteUser}]]就可将登录的域用户名打印出来。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Kerberos Example</title>
    </head>
    <body>
        <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
        <h1 th:inline="text">User Principal is [[${#httpServletRequest.userPrincipal}]]!</h1>
    </body>
</html>

浏览器配置

在IE浏览器中,将要访问的域名server.jiafangbaba.com路径添加到本地信任站点,否则浏览器会弹出Windows窗口输入用户名密码。

如果使用的是EDGEChrome浏览器,同样需要在IE浏览器下设置本地信任站点,因为浏览器都会使用IE中的信任站点进行权限的认证。

验证成果

在这里插入图片描述

在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Double Point

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值