问题描述
我正在通过带有React前端的passport-saml实现saml。当我在本地计算机上的docker容器中运行的saml提供程序(simplesamlPHP)从本地主机运行时,一切工作正常(回调URL为我的应用程序的本地主机:8080)。我在开发网络中运行了另一个saml提供程序(也为simplesamlPHP),该网络将我的静态IP地址回调到该应用程序(仍在localhost:8080上运行)。登录回调工作正常,但是当重定向回到我的快速服务器,并且重定向到我的React应用程序时,所有React资源(.js / .css文件)现在都是https://而不是http:// (通过Chrome开发者工具验证)。这导致没有任何我的反应应用程序资源加载。我想知道这是否可能是Mac通过其网络ip地址寻址时所特有的吗?
快速设置:
const express = require('express');
const http = require('http');
const path = require('path');
const bodyParser = require('body-parser');
const compression = require('compression');
const helmet = require('helmet');
const cors = require('cors');
const passport = require('passport');
require('./passport')(passport);
const app = express();
app.use(cors({
credentials: true,origin: function(origin,callback){
return callback(null,true);
},optionsSuccessstatus: 200,}));
app.use(helmet());
app.use(compression());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.json());
app.use(passport.initialize());
app.use(passport.session());
require('./routes')(app,passport);
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname,'build')));
app.get('*',(req,res) => {
res.sendFile(path.join(__dirname,'build','index.html'));
});
} else if (process.env.NODE_ENV === "development") {
app.get('*',res) => {
res
.redirect(`http://127.0.0.1:3000`);
});
}
const httpServer = http.createServer(app);
const port = process.env.PORT || 8080;
httpServer.listen(port,(httpError) => {
if (httpError) {
console.error(httpError);
}
console.info(`GraphQL Server is Now running on port ${port}`);
if (typeof process.send === 'function') {
process.send('ready');
}
});
const killServer = (signal) => {
console.info(`Shutting down server: ${signal}`);
httpServer.close(() => {
// add additional close processes here
process.exit(0);
});
};
process.on('SIGINT',() => {
killServer('SIGINT');
});
Routes.js:
const SAML = require('passport-saml');
const jwt = require('jsonwebtoken');
module.exports = (app,passport) => {
app.get('/saml/Metadata',res) => {
const saml = new SAML({
issuer: process.env.SAML_ISSUER,callbackUrl: `${process.env.SAML_APP_URL}/login/callback`,logoutCallbackUrl: `${process.env.SAML_APP_URL}/logout/callback`
});
const xml = saml.generateServiceProviderMetadata();
res.set('Content-Type','application/samlMetadata+xml').send(xml);
});
app.get('/saml/login',passport.authenticate('saml'));
app.post('/login/callback',passport.authenticate('saml',{ failureRedirect: '/error' }),async (req,res) => {
const {
error,} = req.user;
if (error) {
return res.redirect('/error');
} else {
return res.redirect(`/logincb?token=${req.user.token}`);
}
});
app.get('/saml/logout',res) => {
// eslint-disable-next-line no-underscore-dangle
const samlStrategy = passport._strategy('saml');
return samlStrategy.logout(req,(err,reqUrl) => {
if (err) {
return res.redirect('/error');
}
req.logout();
return res.redirect(reqUrl);
});
});
app.all('/logout/callback',res) => {
req.logout();
return res.redirect('/');
});
app.get('/user',res) => {
const tokenStr = req.header('Authorization').split(' ');
const token = tokenStr[1];
try {
const decoded = jwt.verify(token,process.env.APP_SECRET);
return res
.json({ user: decoded });
} catch(err) {
return res
.status(401)
.json( {error: 'Invalid JWT' });
}
});
}
passport.js:
const https = require('https');
const os = require('os');
const Metadata = require('passport-saml-Metadata');
const { Strategy: SamlStrategy } = require('passport-saml');
const { Strategy: JWTStrategy,ExtractJwt } = require('passport-jwt');
const jwt = require('jsonwebtoken');
const axios = require('axios');
const fileCache = require('file-system-cache').default;
const client = axios;
const clientProxy = axios.create({
proxy: false,httpsAgent: new https.Agent({
rejectUnauthorized: false,}),});
const config = {
issuer: process.env.SAML_ISSUER,path: '/login/callback',logoutCallbackUrl: '/logout/callback',Metadata: {
client: process.env.SAML_MetaDATA_PROXY && process.env.SAML_MetaDATA_PROXY === "proxy" ? clientProxy : client,url: `${process.env.SAML_MetaDATA}?PartnerSpId=${process.env.SAML_ISSUER}`,timeout: 5000,backupStore: fileCache({
basePath: os.tmpdir(),ns: process.env.SAML_ISSUER
})
}
}
module.exports = (passport) => {
Metadata.fetch(config.Metadata)
.then(async (reader) => {
const strategyConfig = Metadata.toPassportConfig(reader);
strategyConfig.issuer = config.issuer;
strategyConfig.callbackUrl = process.env.SAML_APP_URL + config.path;
strategyConfig.logoutCallbackUrl = process.env.SAML_APP_URL + config.logoutCallbackUrl;
strategyConfig.protocol = 'saml2';
strategyConfig.acceptedClockSkewMs = -1;
const jwtConfig = {
secretorKey: process.env.APP_SECRET,jwtFromrequest: ExtractJwt.fromAuthHeaderAsBearerToken(),issuer: config.issuer,audience: process.env.SAML_APP_URL,};
const samlStrategy = new SamlStrategy(
strategyConfig,async (profile,done) => {
const { nameID,nameIDFormat } = profile;
// use nameId to look up user on database
const user = {
uid: nameID.toLowerCase(),nameID,nameIDFormat,auth: 'something'
};
const token = await jwt.sign(
user,process.env.APP_SECRET,{
expiresIn: '12h',issuer: process.env.SAML_ISSUER,}
);
user.token = token;
return done(null,user);
}
);
const jwtStrategy = new JWTStrategy(
jwtConfig,async (jwtPayload,done) => done(null,jwtPayload)
);
passport.use('saml',samlStrategy);
passport.use('jwt',jwtStrategy);
passport.serializeUser((user,done) => {
done(null,user);
});
passport.deserializeUser(async (user,user);
});
})
.catch((err) => {
console.error('Error loading SAML Metadata',err);
process.exit(1);
});
};
react应用目前是标准的create-react-app,在package.json上设置了以下内容:
"homepage": ".","proxy": "http://127.0.0.1:8080"
和1个回调路由:
import React,{ useEffect,useState } from 'react';
import { Redirect,useLocation } from 'react-router-dom';
const LoginCB = () => {
const location = useLocation();
const { search } = location;
const params = new URLSearchParams(search);
const token = params.get('token');
const [redirect,setRedirect] = useState(false);
useEffect(() => {
fetch('/user',{
headers: {
Authorization: `Bearer ${token}`
}
})
.then(res => res.json())
.then(data => {
console.log(data);
setRedirect(true)
});
},[token]);
return (
<div>
{redirect ? (
<Redirect
to={'/'}
/>
) : null}
<span>something</span>
</div>
)
}
export default LoginCB;
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)