38

【深度分析】关于SPN不正确导致SQL数据库连接失败-嘉为科技

 4 years ago
source link: https://blog.51cto.com/11811406/2503770
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

连接SQL Server数据库时发生报错“The target principal name is incorrect.  Cannot generate SSPI context”,无法连接,可能是由于AD域中记录了错误的SPN,导致无法进行身份验证而连接失败。下文通过简述Kerberos认证过程、SPN的组成,引出由SPN错误引发报错的解决方法。

Kerberos认证


1. Kerberos认证步骤

Kerberos认证需要包含KDC(Key Distribution Center)、客户端用户、提供服务的服务器三个组件。其中KDC是域控的一部分,执行两个任务:认证服务(AS)、票据许可服务(TGS)

  • 当客户端用户登录到网络时,会向用户所在域的AS申请一个“票据请求票据”(TGT);

  • 当客户端要访问网络上某个资源时,需要出示TGT、认证码、SPN(Server Principal Name),借此从用户所在域的TGS获取session票据;

  • 客户端使用这个session票据和认证码向网络上的服务获取访问令牌,接下来就可以登录上该服务了。

2. 使用Kerberos身份验证的条件

  • 客户端和服务器需要加域,当客户端和服务器加入不同域时,两个域需要有相互信任关系;

  • 提供服务的服务器需要注册正确的SPN。

注:从Windows Server 2003开始默认使用Kerberos认证方式,当网络上没有注册SPN时,就会使用NTLM认证方式,这个步骤叫做NTLM Fallback;如果网络上有注册SPN,但这个SPN注册在了错误的账户下(例如不是SQL Server服务启动账号),则认证失败,且不会再次尝试NTLM认证。

SPN(Server Principal Name)


1.SPN的组成

SPN是服务器上所运行服务的唯一标示,每个使用Kerberos的服务都需要一个SPN,这样客户端才可以辨认这个服务。SPN需要注册在AD域的计算机账户或者域用户账户下。

一个SQL Server的SPN由以下元素组成:

服务类型:标示了服务的泛用类。对于SQL Server而言,是MSSQLSvc。

主机:有两种形式。一个是运行SQL Server的计算机的FQDN。还有一种就是SQL Server的计算机的netbios名字,俗称短名。

端口号/实例名:服务所监听的计算机端口号。对于SQL Server而言,如果SQL运行在默认端口(1433)上,则端口号可以省略。

从SQL Server 2008开始,Kerberos可以支持TCP, Named Pipes和Shared Memory三种协议。因此对于SQL Server 2008我们也可以使用SQL Server的实例名来替代端口号(仅就命名实例而言)。

MSSQLSvc/myserver.corp.mycomany.com:1433

MSSQLSvc/myserver:1433

MSSQLSvc/myserver.corp.mycomany.com

MSSQLSvc/myserver:

MSSQLSvc/myserver.corp.mycomany.com:instancename

MSSQLSvc/myserver:instancename

注:推荐为FQDN和netbios都注册SPN。

2. 检查、添加、删除SPN

详细的使用方法参考微软官方文档:

https://docs.microsoft.com/zh-cn/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc731241(v=ws.11)

查询SPN:

在命令行输入:

Setspn -L <Account>

其中<Account>可以是计算机账户或者域用户账户。

1.png
2.png

添加SPN:

在命令行输入:

Setspn -S MSSQLSvc/server4.main.local:1433 <Account>

3.png

注:使用“Setspn -A”也可以添加SPN,但推荐使用“Setspn -S”,因为“Setspn -S”在添加前会检查域内是否存在相同的SPN,防止重复的SPN注册在不同的账户下。

命令行输入:

Setspn -X

也可以检查域内是否存在重复的SPN。

4.png

删除SPN:

在命令行输入:

Setspn -D MSSQLSvc/server4.main.local:1433 <Account>

5.png

如何为SQL Server注册SPN

1. 数据库服务启动账户

使用Network Service或Local System

内置账户Network Service和Local System代表计算机本身,SPN需要注册在运行SQL Server的计算机账户下。但Network Service和Local System本身有权限为本机注册和删除SPN,一般情况不需要手动修改。(Local System权限过大,不推荐使用做服务启动账户)

使用域用户账户

一般域用户没有为自身注册SPN的权限,需要手动在该域用户账户下注册SPN;如果该域用户具有本地管理员或域管理员权限,则有权限为自身注册或删除SPN。

注:可以在域控中为特定账户添加注册SPN的权限,但官方不推荐这种做法。

2. 故障处理

文字开头提到的报错:“Cannot generate SSPI context”

6.png

本次处理的故障是由于更换了服务启动账户,旧的SPN注册在本地计算机账户下,更换后没有自动删除,导致域内存在不正确的SPN,无法完成Kerberos认证。

解决方法:

删除计算机账户下的SPN后,添加域用户账户下的SPN。操作步骤如下:

先查询SPN,命令行运行

Setspn -L server4

7.png

确认存在错误的SPN,下一步删除,命令行执行

Setspn -D MSSQLSvc/server4.main.local:1433 server4

Setspn -D MSSQLSvc/server4.main.local server4

8.png

删除后再为域用户账户添加SPN,命令行执行

Setspn -S MSSQLSvc/server4.main.local user-c

Setspn -S MSSQLSvc/server4.main.local:1433 user-c

9.png

添加成功后,再次查询SPN确认,命令行执行

Setspn -L server4

Setspn -L user-c

10.png

添加成功,检查报错是否还存在,在SSMS执行查询

select auth_scheme,* from sys.dm_exec_connections

11.png

连接成功,而且使用的是Kerberos认证。

3. 其他常见故障

"Login Failed for user 'NT Authority\ANONYMOUS' LOGON"

客户端可能正在使用Local System进行连接,而且SQL Server没有注册SPN,由于Local System账号继承自System Context而不是一个真实的user context,于是就被当成ANONYMOUS LOGON。

解决方法:在SQL Server服务启动账户下手动注册SPN。

"Login Failed for user ' ', the user is not associated with a trusted SQL Server connection"

这种情况是客户端用户没能被SQL Server识别出:

如果客户端程序是运行在一个本机用户(非域用户)或者是一个非本机管理员权限的机器帐户(非local system)下,那么无论SQL Server是否有注册SPN,都会得到这个错误。

解决方法:

在服务器端创建一个和客户端用户“同用户名用密码”的本机账号,然后在SQL Server中赋予相应的登录权限。这就是所谓pass through的方式。此时你实际上是在使用SQL Server那台计算机的同名帐户来访问SQL Server和相关的其他资源。因此SQL Server机器上该帐户的权限设置决定了客户端的操作权限。

如果客户端应用程序是运行在一个域用户下的话,那么该错误就说明Kerberos的验证失败了,这往往是由于没有SPN或者SPN不正确造成的。

解决方法:

注册正确的SPN,或者删除相应SPN放弃使用Kerberos认证。

"Could not open a connection to SQL Server[1326]"

和上面提到的故障情况类似,但上面使用TCP连接,这里我们使用Named Pipe连接,解决方法一样。

"Login failed for user '<domain>\<machinename>$' "

客户端可能在使用Local system或者Network service运行。

解决方法:

在SQL Server的login中添加一个"domain\machinename$"账号。其中Machinename是客户端的计算机名。

其他优质文章

【知识科普】广泛应用的敏捷开发方法论,极限编程与持续集成!

【Azure】混合环境下的身份验证

【知识科普】嵌入式软件开发是什么?

【原型设计】如何利用Axure实现下拉子菜单?

【软件开发】如何在DevOps实践中,持续优化体系构建?


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK