我已经在ExpressJS,NodePass应用程序上使用Express,Passport和Passport-Saml在ADFS上实现了SSO。我可以登录ADFS,并与SAML令牌一起正确发布回我的回调路由(即localhost:3000 / adfs / postResponse)。但是,当我到达回调路由时,似乎SAML令牌已被拒绝,因此我被发送回ADFS登录。然后重复此过程。
有人可以建议出什么问题吗?任何帮助将不胜感激。
更多详细信息:
此SAML请求已发送到ADFS:
<?xml version="1.0"?>
<samlp:AuthnRequest AssertionConsumerServiceURL="https://localhost:3000/adfs/postResponse"
Destination="https://nonp-adfs.dsgapps.dk/adfs/ls" ID="_5e09625f5c3f5dbb0b6b"
IssueInstant="2018-06-28T11:57:35.962Z"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">acme_tools_com</saml:Issuer>
<samlp:RequestedAuthnContext Comparison="exact" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
</samlp:AuthnRequest>
此SAML响应已从ADFS服务器发布到我的回调https://localhost:3000/adfs/postResponse:
<samlp:Response Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified"
Destination="https://localhost:3000/adfs/postResponse" ID="_e543e979-0d99-48fe-947f-1d1469da8c70"
InResponseTo="_49ab1e1060c3d7849902" IssueInstant="2018-06-28T19:46:27.782Z" Version="2.0"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://nonp-adfs.dsgapps.dk/adfs/services/trust</Issuer>
<samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>
<Assertion ID="_cf245f57-1380-47cd-a5d3-05b13e4d9416" IssueInstant="2018-06-28T19:46:27.782Z"
Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
<Issuer>http://nonp-adfs.dsgapps.dk/adfs/services/trust</Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#_cf245f57-1380-47cd-a5d3-05b13e4d9416">
<ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>r6voAsVq4yAJTn4BQLFsyaoiCK3b7KQbJ5jVqi53ceY=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>F55JA6jNp3qFfp7p/BSzQBRTtVPOlQvIfVNG3JiqjohVC7Et0+aiRVlHHvZNghPJxhmxhuAUbo2kOweN+lZKb+fqDgK51kZ/DrIVpkljmwP2gJYgOGpJti53wfH2qkdDsxNkR3e13mG7RKwBuA4gJ0NxUFshmxyun0HKefd10wjnFwHY6dELWFmTL1W5xd2ZF/98ahIaqEWAMCYsJewEg4ND8z4vG74miht3lWHfTJL6kQ0UGkTJVwGZy9L8zaY8AMDRujs8SlXvBx9nvUnvufpYqto4kd0O0USWMCOPipcF2sVYDOVzidRSRb79TK256Wg9EGiw1usVThfAJ8IBzQ==</ds:SignatureValue>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>MIIC5DCCAcygAwIBAgIQWKHI7vunT6hIeNtPiejvbTANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNBREZTIFNpZ25pbmcgLSBub25wLWFkZnMuZHNnYXBwcy5kazAeFw0xODA2MTUxNDQ4NTdaFw0xOTA2MTUxNDQ4NTdaMC4xLDAqBgNVBAMTI0FERlMgU2lnbmluZyAtIG5vbnAtYWRmcy5kc2dhcHBzLmRrMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyN0SSHVjJML1lmcHB8RBNLXnegEISB66Nc75xEpscFGNPKoQloLck6XLPYvhmiL8WiVHTzghiJpU/faViR7s+wksj3n4IXVfCxb6wMd78LiOfeE6yyED+C/EprwoRWGXncUK4lwfLDGOPbWVqaPy0u14rQR0mvn0BsIOiML1JJvAPtf8fhavNmce2aEeRltLY3N8aoLMw8/TMrG+wk1imUo+JScp3gOPqrDnQBGgcjdBY/EaC9mFfAUbhyly0vKl/gYkOv1HFhUMtH7NlLUmDsvOCt3Nrbf6aKmi+H1EAfwJR/POnMbsoC8sqf4PWk/kMtj1POOpZAnQOBE8u4NtPwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDIdu6cr7LgoNdXpgtwd/Zt7sb1N6dJ/GgULuxBm7Bm1Mdsc9+Q0lDhxeGtay9AIvpbF67xvSzrCz3eL9xPuNV6BYmZpYFsyBPP4MROlkgq1MkqLDpkB/zkiKQqZiJG3RHl5e+WniFrAmNxuuUAtdhKbh1ADJKc1bxte6uiY0dN/Mfw6WnY3m3VOtae9xoqHNM2i4uhEbMvXV9Pmb8BVv4eIZLtOgo+vgkusp3FZa2PL4UWQIPNiEggIxhs7MfpaoADT4taGeavpHWKuxIGvDQzoe7GP2iDGzyH1kS24rSeJRYOiyBq1zPJHrSPeLFsef/7LapCaz5x5+T/eWPhyJKd</ds:X509Certificate>
</ds:X509Data>
</KeyInfo>
</ds:Signature>
<Subject>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData InResponseTo="_49ab1e1060c3d7849902"
NotOnOrAfter="2018-06-28T19:51:27.782Z" Recipient="https://localhost:3000/adfs/postResponse"/></SubjectConfirmation>
</Subject>
<Conditions NotBefore="2018-06-28T19:46:27.781Z" NotOnOrAfter="2018-06-28T20:46:27.781Z">
<AudienceRestriction>
<Audience>acme_tools_com</Audience>
</AudienceRestriction>
</Conditions>
<AuthnStatement AuthnInstant="2018-06-28T19:45:51.797Z">
<AuthnContext>
<AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
</AuthnContext>
</AuthnStatement>
</Assertion>
</samlp:Response>
NB:可以使用SAML Chrome扩展程序检查SAML请求和响应。
我的NodeJS程序的最核心部分是:
const verifyFunction = function(profile, done) {
console.log("Verifying"+ profile);
return done(null,
{
upn: profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'],
// e.g. if you added a Group claim
group: profile['http://schemas.xmlsoap.org/claims/Group']
});
};
var strategy = new SamlStrategy(
{
entryPoint: 'https://nonp-adfs.dsgapps.dk/adfs/ls',
issuer: 'acme_tools_com',
callbackUrl: 'https://localhost:3000/adfs/postResponse',
privateCert: fs.readFileSync(root + '/acme_tools_com.key', 'utf-8'),
cert: fs.readFileSync(root + '/acme_tools_com.cert', 'utf-8'),
authnContext: 'http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password',
// not sure if this is necessary?
acceptedClockSkewMs: -1,
identifierFormat: null,
signatureAlgorithm: 'sha256'
},
verifyFunction
);
strategy.userProfile = function(accessToken, done) {
console.log("UserProfile:" + accessToken);
done(null, accessToken);
};
passport.use('provider', strategy);
passport.serializeUser(function(user, done) {
console.log("Serializing user");
done(null, user);
});
passport.deserializeUser(function(user, done) {
console.log("Deserializing user");
done(null, user);
});
app.get('/login',
passport.authenticate('provider', { failureRedirect: '/', failureFlash: true })
);
app.post('/adfs/postResponse',
function(req, res, next) {
console.log("Before authenticating: " );
next();
},
passport.authenticate('provider', { failureRedirect: '/', failureFlash: true }),
function(req, res) {
console.log("User: " + util.inspect(req.user));
res.cookie('accessToken', req.user);
res.redirect('/');
}
);
进行登录时,我在ADFS服务器上没有看到任何错误,因此在那里看起来不错。
在NodeJS服务器上,我看到:
Express server started on https://localhost:3000
postResponse entered:
postResponse entered:
...
换句话说,永远不会调用verifyFunction。那不是很奇怪吗?似乎passport-saml模块无法接收SAML响应。
从ADFS到我的回调的POST如下:
curl 'https://localhost:3000/adfs/postResponse' -H 'Origin: https://nonp-adfs.dsgapps.dk' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' -H 'Referer: https://nonp-adfs.dsgapps.dk/adfs/ls?SAMLRequest=nVJLc9owEP4rHt2xZMdD....' -H 'Accept-Encoding: gzip, deflate, br' -H 'Accept-Language: en-US,en;q=0.9,da;q=0.8' -H --data 'SAMLResponse=PHNhbWxwOlJlc3.......' --compressed --insecure
这是完整的NodeJS程序:
'use strict';
var util = require('util');
var https = require('https');
var app = require('express')();
var cookieParser = require('cookie-parser');
var passport = require('passport');
var fs = require('fs');
var SamlStrategy = require('passport-saml').Strategy;
var path = require("path");
const root = __dirname;
const verifyFunction = function(profile, done) {
console.log("Verifying"+ profile);
return done(null,
{
upn: profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'],
// e.g. if you added a Group claim
group: profile['http://schemas.xmlsoap.org/claims/Group']
});
};
var strategy = new SamlStrategy(
{
entryPoint: 'https://nonp-adfs.dsgapps.dk/adfs/ls',
issuer: 'acme_tools_com',
callbackUrl: 'https://localhost:3000/adfs/postResponse',
privateCert: fs.readFileSync(root + '/acme_tools_com.key', 'utf-8'),
cert: fs.readFileSync(root + '/acme_tools_com.cert', 'utf-8'),
authnContext: 'http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password',
// not sure if this is necessary?
acceptedClockSkewMs: -1,
identifierFormat: null,
signatureAlgorithm: 'sha256'
},
verifyFunction
);
strategy.userProfile = function(accessToken, done) {
console.log("UserProfile:" + accessToken);
done(null, accessToken);
};
passport.use('provider', strategy);
passport.serializeUser(function(user, done) {
console.log("Serializing user");
done(null, user);
});
passport.deserializeUser(function(user, done) {
console.log("Deserializing user");
done(null, user);
});
function validateAccessToken(accessToken) {
console.log("AccessToken: "+ accessToken);
return;
}
// Configure express app
app.use(cookieParser());
app.use(passport.initialize());
app.get('/login',
passport.authenticate('provider', { failureRedirect: '/', failureFlash: true })
);
app.post('/adfs/postResponse',
function(req, res, next) {
console.log("Before authenticating: " );
next();
},
passport.authenticate('provider', { failureRedirect: '/', failureFlash: true }),
function(req, res) {
console.log("User: " + util.inspect(req.user));
res.cookie('accessToken', req.user);
res.redirect('/');
}
);
app.get('/', function (req, res) {
req.user = req.cookies['accessToken'];
res.send(
!req.user ? '<a href="/login">Log In</a>' : '<a href="/logout">Log Out</a>' +
'<pre>' + JSON.stringify(req.user, null, 2) + '</pre>');
});
app.get('/logout', function (req, res) {
res.clearCookie('accessToken');
res.redirect('/');
});
var certOptions = {
key: fs.readFileSync(root + '/localhost.key'),
cert: fs.readFileSync(root + '/localhost.cert')
}
var server = https.createServer(certOptions, app).listen(3000)
console.log('Express server started on https://localhost:3000');
答案 0 :(得分:0)
问题原来很简单。我需要包括中间件来解析POST数据。
将这些行添加到我的NodeJS模块的顶部后,它开始工作。
list1 = ['ab','cd','gfa','eha','tu','asb','acd','cgf','ceh','dtu','ased','ra','re','sdgfsycbjs','jcjhcbsd']
list2 = ['abx','cd','gfr','eha','tu','asb','acl','cgfta','cpah','adtu','assa','fd','as','sbddsvc','jbcbh']
list3 = ['abs','cd','gfv','eh','tu','asb','ased','cgf','ceh','adtu','assa','qw','uy','hdsjb','bcjh']
a = []
b = []
c = []
ab = []
bc = []
ca = []
abc = []
for item in list1:
if item in list2:
if item in list3:
if item not in abc:
abc.append(item)
else:
ab.append(item)
else:
if item not in ca:
a.append(item)
for item in list2:
if item in list3:
if item in list1:
if item not in abc:
abc.append(item)
else:
bc.append(item)
else:
if item not in ab:
b.append(item)
for item in list3:
if item in list1:
if item in list2:
if item not in abc:
abc.append(item)
else:
ca.append(item)
else:
if item not in bc:
c.append(item)