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

如何在SAML SSO流程中为Angular前端设置JWT令牌

可行的JWT传递方案(IDP发起SAML SSO场景)

针对你的场景,这里提供三个安全可行的方案,按推荐程度排序:

这是最安全的方式,利用浏览器Cookie机制自动处理令牌传递,避免前端直接操作JWT,降低XSS风险。

后端(DRF)实现:

在ACS端点验证SAML响应、确认用户身份并生成JWT后,将JWT设置为HttpOnly、Secure、SameSite属性的Cookie,再重定向到Angular着陆页:

from django.http import HttpResponseRedirect

def acs_view(request):
    # 验证SAML响应、确认用户身份、生成JWT令牌的逻辑...
    jwt_token = generate_jwt(user)
    
    response = HttpResponseRedirect("https://your-angular-app.com/landing")
    # 设置Cookie参数
    response.set_cookie(
        key="auth_token",
        value=jwt_token,
        httponly=True,  # 禁止前端JS读取,防范XSS攻击
        secure=True,    # 仅通过HTTPS传输
        samesite="Lax", # 限制跨站请求携带,防范CSRF
        max_age=3600*24, # 令牌有效期(示例为24小时)
        path="/",
        domain=".your-domain.com"  # 适配前后端同域名或子域名场景
    )
    return response

前端(Angular)配置:

后续所有API请求需携带Cookie,在HttpClient请求中开启withCredentials:

import { HttpClient } from '@angular/common/http';

constructor(private http: HttpClient) {}

fetchData() {
    this.http.get('https://your-drf-api.com/data', { withCredentials: true })
        .subscribe(data => { /* 处理返回数据 */ });
}

关键配置:

  • DRF需开启CORS支持凭证,在settings.py中设置:
    CORS_ALLOW_CREDENTIALS = True
    CORS_ALLOWED_ORIGINS = ["https://your-angular-app.com"]
    
  • 确保前后端域名符合SameSite策略要求,避免跨站Cookie被浏览器拦截。

方案二:临时一次性令牌中转

通过短时效的一次性令牌作为桥梁,间接获取JWT,避免直接暴露JWT在URL中。

后端(DRF)实现:

  1. ACS端点验证成功后,生成一个短时效(如5分钟)、一次性的临时令牌(UUID即可),将JWT与临时令牌绑定存储(推荐用Redis):
import uuid
import redis
from django.http import HttpResponseRedirect

redis_client = redis.Redis(host='localhost', port=6379, db=0)

def acs_view(request):
    # 验证SAML、生成JWT逻辑...
    jwt_token = generate_jwt(user)
    temp_token = str(uuid.uuid4())
    
    # 存储临时令牌与JWT,设置5分钟过期
    redis_client.setex(temp_token, 300, jwt_token)
    
    # 重定向到Angular,携带临时令牌(风险低,因为时效短且仅可使用一次)
    return HttpResponseRedirect(f"https://your-angular-app.com/landing?temp={temp_token}")
  1. 新增接口用于通过临时令牌换取JWT:
from django.http import HttpResponseBadRequest, HttpResponseUnauthorized
from django.http import JsonResponse

def exchange_token(request):
    temp_token = request.GET.get('temp')
    if not temp_token:
        return HttpResponseBadRequest("Missing temp token")
    
    jwt_token = redis_client.get(temp_token)
    if not jwt_token:
        return HttpResponseUnauthorized("Invalid or expired temp token")
    
    # 销毁临时令牌,确保只能使用一次
    redis_client.delete(temp_token)
    return JsonResponse({"token": jwt_token.decode('utf-8')})

前端(Angular)实现:

在着陆页组件中,读取URL中的临时令牌,调用接口换取JWT后存储:

import { ActivatedRoute } from '@angular/router';
import { HttpClient } from '@angular/common/http';

constructor(private route: ActivatedRoute, private http: HttpClient) {}

ngOnInit() {
    this.route.queryParams.subscribe(params => {
        const tempToken = params['temp'];
        if (tempToken) {
            this.http.get('https://your-drf-api.com/exchange-token', { params: { temp: tempToken } })
                .subscribe((res: any) => {
                    // 将JWT存储到sessionStorage或localStorage
                    sessionStorage.setItem('auth_token', res.token);
                    // 后续请求携带令牌逻辑
                });
        }
    });
}

方案三:隐藏iframe + postMessage 跨域传递

适合前后端完全跨域且无法使用Cookie的场景,通过iframe的postMessage机制安全传递令牌。

后端(DRF)实现:

  1. ACS端点重定向到Angular着陆页;
  2. 新增接口,返回包含JWT的脚本,通过postMessage发送给父页面:
from django.http import HttpResponseForbidden, HttpResponseUnauthorized, HttpResponse

def get_jwt_via_iframe(request):
    # 验证请求来源是可信的Angular域名
    referer = request.META.get('HTTP_REFERER')
    if not referer or not referer.startswith('https://your-angular-app.com'):
        return HttpResponseForbidden("Invalid referer")
    
    # 从会话或其他方式获取当前用户的JWT(需确保ACS验证后已建立会话)
    jwt_token = request.session.get('jwt_token')
    if not jwt_token:
        return HttpResponseUnauthorized("No token available")
    
    # 返回脚本,通过postMessage传递令牌
    script = f"""
    window.parent.postMessage({{ type: 'SAML_AUTH_TOKEN', token: '{jwt_token}' }}, 'https://your-angular-app.com');
    """
    return HttpResponse(script, content_type='text/javascript')

前端(Angular)实现:

在着陆页组件中,创建隐藏iframe并监听postMessage事件:

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'app-landing',
    template: '<iframe style="display:none" src="https://your-drf-api.com/get-jwt-via-iframe"></iframe>'
})
export class LandingComponent implements OnInit {
    ngOnInit() {
        const handleMessage = (event: MessageEvent) => {
            // 验证消息来源是后端域名
            if (event.origin !== 'https://your-drf-api.com') return;
            
            if (event.data.type === 'SAML_AUTH_TOKEN') {
                const jwtToken = event.data.token;
                // 存储令牌
                sessionStorage.setItem('auth_token', jwtToken);
                // 移除监听
                window.removeEventListener('message', handleMessage);
            }
        };
        window.addEventListener('message', handleMessage);
    }
}

内容的提问来源于stack exchange,提问作者Prithvi Raj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:33:28