You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Angular与ASP.NET Core的Windows AD用户认证及任务管理问题咨询

可行解决方案架构与实现步骤

1. 核心通信流程确认

你的初始架构思路是正确的,完整流程应为:

  • 前端Angular收集用户登录凭证,通过HTTPS POST请求发送至ASP.NET Core后端API
  • 后端作为中间层,通过LDAP协议与远程Windows Active Directory交互完成身份验证
  • 验证通过后,后端生成JWT令牌返回给前端,前端后续请求携带令牌访问任务接口
  • 任务分配环节,后端从AD读取用户列表返回给前端,用于选择分配对象

2. 后端ASP.NET Core与AD的LDAP集成实现

由于仅拥有AD只读权限,需通过LDAP绑定验证用户身份并读取用户信息,以下是具体实现:

2.1 配置LDAP连接参数

在appsettings.json中添加AD连接配置:

"ActiveDirectory": {
  "LdapServer": "远程AD服务器地址",
  "LdapPort": 389, // 非SSL用389,SSL加密用636
  "SearchBase": "OU=Users,DC=domain,DC=com", // AD用户所在的搜索路径
  "ServiceAccountUsername": "只读权限服务账号名",
  "ServiceAccountPassword": "只读权限服务账号密码"
}

2.2 封装AD操作服务

创建ActiveDirectoryService类,统一处理LDAP交互:

using System.DirectoryServices.Protocols;
using System.Net;

public class ActiveDirectoryService
{
    private readonly LdapConnection _ldapConnection;
    private readonly string _searchBase;

    public ActiveDirectoryService(IConfiguration configuration)
    {
        var adConfig = configuration.GetSection("ActiveDirectory");
        var server = adConfig["LdapServer"];
        var port = int.Parse(adConfig["LdapPort"]);
        _searchBase = adConfig["SearchBase"];

        // 用只读服务账号建立LDAP连接
        var credential = new NetworkCredential(adConfig["ServiceAccountUsername"], adConfig["ServiceAccountPassword"]);
        _ldapConnection = new LdapConnection(new LdapDirectoryIdentifier(server, port));
        _ldapConnection.Credential = credential;
        _ldapConnection.AuthType = AuthType.Negotiate;
        _ldapConnection.Bind();
    }

    // 验证用户登录凭证
    public bool ValidateUserCredentials(string username, string password)
    {
        try
        {
            // 先搜索用户的DistinguishedName
            var searchFilter = $"(&(objectClass=user)(sAMAccountName={username}))";
            var searchRequest = new SearchRequest(_searchBase, searchFilter, SearchScope.Subtree, "distinguishedName");
            var searchResponse = (SearchResponse)_ldapConnection.SendRequest(searchRequest);

            if (searchResponse.Entries.Count == 0)
                return false;

            var userDn = searchResponse.Entries[0].DistinguishedName;

            // 用用户自身凭证绑定验证身份
            using var userConnection = new LdapConnection(new LdapDirectoryIdentifier(_ldapConnection.SessionOptions.HostName));
            userConnection.Credential = new NetworkCredential(userDn, password);
            userConnection.AuthType = AuthType.Negotiate;
            userConnection.Bind();

            return true;
        }
        catch (LdapException)
        {
            // 捕获无效凭证、用户不存在等LDAP错误
            return false;
        }
    }

    // 读取AD用户列表(用于任务分配)
    public List<AdUser> GetAllUsers()
    {
        var users = new List<AdUser>();
        var searchFilter = "(objectClass=user)";
        var searchRequest = new SearchRequest(_searchBase, searchFilter, SearchScope.Subtree, "sAMAccountName", "displayName");

        var searchResponse = (SearchResponse)_ldapConnection.SendRequest(searchRequest);

        foreach (SearchResultEntry entry in searchResponse.Entries)
        {
            users.Add(new AdUser
            {
                Username = entry.Attributes["sAMAccountName"][0].ToString(),
                DisplayName = entry.Attributes["displayName"][0].ToString()
            });
        }

        return users;
    }
}

public class AdUser
{
    public string Username { get; set; }
    public string DisplayName { get; set; }
}

2.3 注册服务并编写登录API

在Program.cs中注册AD服务:

builder.Services.AddScoped<ActiveDirectoryService>();

创建Auth控制器处理登录请求:

using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    private readonly ActiveDirectoryService _adService;
    private readonly IConfiguration _configuration;

    public AuthController(ActiveDirectoryService adService, IConfiguration configuration)
    {
        _adService = adService;
        _configuration = configuration;
    }

    [HttpPost("login")]
    public IActionResult Login([FromBody] LoginRequest request)
    {
        if (!_adService.ValidateUserCredentials(request.Username, request.Password))
            return Unauthorized("用户名或密码错误");

        // 生成JWT令牌
        var claims = new[]
        {
            new Claim(ClaimTypes.Name, request.Username)
        };

        var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _configuration["Jwt:Issuer"],
            audience: _configuration["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddHours(8),
            signingCredentials: creds);

        return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
    }
}

public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}

3. 前端Angular集成实现

3.1 登录组件开发

创建登录表单,提交时调用后端登录API:

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html'
})
export class LoginComponent {
  username: string = '';
  password: string = '';
  errorMessage: string = '';

  constructor(private http: HttpClient, private router: Router) {}

  onLogin() {
    this.http.post('/api/auth/login', { username: this.username, password: this.password })
      .subscribe({
        next: (response: any) => {
          localStorage.setItem('token', response.token);
          this.router.navigate(['/tasks']);
        },
        error: (err) => {
          this.errorMessage = err.error || '登录失败';
        }
      });
  }
}

3.2 添加HTTP拦截器自动携带令牌

创建拦截器,在所有请求头中添加Authorization:

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    const token = localStorage.getItem('token');
    if (token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });
    }
    return next.handle(request);
  }
}

在app.module.ts中注册拦截器:

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';

@NgModule({
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
  ]
})
export class AppModule { }

3.3 获取AD用户列表用于任务分配

创建服务获取用户列表:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface AdUser {
  username: string;
  displayName: string;
}

@Injectable({
  providedIn: 'root'
})
export class AdUserService {
  constructor(private http: HttpClient) {}

  getUsers(): Observable<AdUser[]> {
    return this.http.get<AdUser[]>('/api/users');
  }
}

4. 关键注意事项

  • LDAP端口与SSL:若AD服务器要求加密连接,切换至636端口并启用SSL配置
  • 服务账号权限:确保只读服务账号拥有读取用户信息的权限,避免搜索失败
  • 错误处理:后端需捕获LDAP异常并返回明确错误信息,前端做友好提示
  • 安全规范:所有通信使用HTTPS,JWT令牌设置合理过期时间,前端不存储明文密码

内容的提问来源于stack exchange,提问作者Raffael Nistelberger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 16:21:11