IIS 10+Laravel 5.4配置SSO及登录表单共存问题求助
老哥,刚接手IIS项目就碰这种认证兼容问题确实闹心,我来帮你一步步捋清楚。你的核心需求是同时搞定内网自动SSO(Windows认证)和VPN场景的表单登录,现在开了Windows Auth后弹窗口还登不上,咱们从IIS配置、PHP环境、Laravel逻辑三层入手解决:
一、先把IIS的认证配置捋顺
你之前直接关了匿名认证是关键错误——要同时支持两种登录,站点级别得同时开启匿名认证和Windows认证,然后给不同路由单独配置认证规则,不然表单登录的路由会被Windows Auth强制拦截。
1. 调整Windows认证提供者顺序
在IIS站点的「Windows Authentication」设置里,打开「Providers」,把Negotiate拖到NTLM前面。IE11在内网环境优先用Negotiate做Kerberos自动认证,能实现无感知SSO;VPN环境Kerberos失效时,会自动 fallback到NTLM(需要手动输账号,但至少能登录)。
2. 用web.config给路由分配置
在站点根目录的web.config里添加路由级别的认证规则,比如:
<!-- SSO专属路由:禁用匿名,启用Windows认证 --> <location path="sso-login"> <system.webServer> <security> <authentication> <anonymousAuthentication enabled="false" /> <windowsAuthentication enabled="true" /> </authentication> </security> </system.webServer> </location> <!-- 表单登录路由:禁用Windows认证,保留匿名 --> <location path="login"> <system.webServer> <security> <authentication> <anonymousAuthentication enabled="true" /> <windowsAuthentication enabled="false" /> </authentication> </security> </system.webServer> </location>
这样不同路由走不同认证逻辑,就不会互相冲突了。
二、让PHP能拿到Windows Auth的用户信息
你说$_SERVER里没有AUTH_USER,是因为IIS默认不会把Windows认证的用户信息传递给PHP的FastCGI进程,得手动配置环境变量:
方法1:通过IIS图形界面配置
- 打开IIS管理器,找到「FastCGI设置」
- 找到你的PHP应用池(比如
PHP_via_FastCGI),右键「编辑」 - 切换到「环境变量」标签,添加两个变量:
- 名称:
AUTH_USER,值:{AUTH_USER} - 名称:
AUTH_PASSWORD,值:{AUTH_PASSWORD}
- 名称:
方法2:直接在web.config里加
如果嫌图形界面麻烦,直接在web.config的PHP handler节点里加环境变量:
<handlers> <add name="PHP_via_FastCGI" path="*.php" verb="GET,HEAD,POST" modules="FastCgiModule" scriptProcessor="C:\PHP\php-cgi.exe" resourceType="Either" requireAccess="Script"> <environmentVariables> <add name="AUTH_USER" value="{AUTH_USER}" /> <add name="AUTH_PASSWORD" value="{AUTH_PASSWORD}" /> </environmentVariables> </add> </handlers>
配置完重启IIS,PHP就能拿到$_SERVER['AUTH_USER']了,格式一般是DOMAIN\username,正好给adldap做验证。
三、Laravel里实现双认证逻辑
现在要写个分支逻辑:内网用户走SSO自动登录,VPN用户走表单登录。
1. 新增SSO登录路由
在routes/web.php里加:
// SSO自动登录路由 Route::get('/sso-login', 'Auth\LoginController@ssoLogin')->name('sso.login'); // 原表单登录路由保留 Route::get('/login', 'Auth\LoginController@showLoginForm')->name('login'); Route::post('/login', 'Auth\LoginController@login');
2. 在LoginController里写SSO逻辑
public function ssoLogin() { // 检查是否有Windows认证的用户信息 if (isset($_SERVER['AUTH_USER'])) { // 拆分域名和用户名(格式:DOMAIN\username) [$domain, $username] = explode('\\', $_SERVER['AUTH_USER'], 2); // 用adldap验证用户身份(不需要密码,因为Windows Auth已经验证过) if (Adldap::auth()->attempt($username, null, $bindAsUser = false)) { // 查找或自动创建Laravel用户 $user = User::firstOrCreate( ['username' => $username], [ 'name' => Adldap::search()->users()->find($username)->getCommonName(), 'email' => Adldap::search()->users()->find($username)->getEmail(), // 可以设置一个随机密码,因为SSO用户不需要用密码登录 'password' => bcrypt(str_random(16)), ] ); // 自动登录用户 Auth::login($user); return redirect()->intended('/dashboard'); } return abort(403, 'LDAP身份验证失败'); } // 如果没有Windows Auth信息,跳转到表单登录页 return redirect()->route('login'); }
四、IE11的兼容性配置
内网用户要实现无感知SSO,得确保IE11的设置正确:
- 把你的站点加入「本地Intranet」区域(工具→Internet选项→安全→本地Intranet→站点→添加)
- 开启自动登录:「本地Intranet」→「自定义级别」→「用户验证」→选择「自动登录到Intranet区域」
这样内网用户访问/sso-login时,IE会自动用当前Windows账号完成认证,不会弹登录窗口;VPN用户访问/login时,走原来的表单登录流程。
内容的提问来源于stack exchange,提问作者sseggio

