1、非常完美的流程图
2、非常完美的流程说明
下面是举例来详细说明CAS实现单点登录的流程:
一、第一次访问系统A
1、用户访问系统A (www.app1.com),%25EF%25BC%258C) 跳转认证中心 client(www.sso.com),%25EF%25BC%258C) 然后输入用户名,密码登录,然后认证中心 serverSSO 把 cookieSSO 种在认证中心的域名下 (www.sso.com),%25EF%25BC%258C) 重定向到系统A,并且带上生成的 ticket 参数 (www.app1.com?ticket =xxx)
2、系统A (www.app1.com?ticket =xxx)请求系统A的后端 serverA ,serverA 去 serverSSO 验证,通过后,将cookieA种在 www.app1.com下
二、第二次访问系统A 直接携带 cookieA 去访问后端,验证通过后,即登录成功。
三、第三次访问系统B
1、访问系统B (www.app2.com),%25EF%25BC%258C) 跳转到认证中心 client(www.sso.com),%25EF%25BC%258C) 这个时候会把认证中心的cookieSSO也携带上,发现用户已登录过,则直接重定向到系统B(www.app2.com), 并且带上生成的ticket参数(www.app2.com?ticket =xxx)
2、系统B (www.app2.com?ticket =xxx)请求系统B的后端 serverB,serverB 去 serverSSO 验证,通过后,将cookieB种在www.app2.com下
注意cookie生成时机及种的位置。
- cookieSSO,SSO域名下的cookie
- cookieA,系统A域名下的cookie
- cookieB,系统B域名下的cookie、
3、个人总结的流程
1、用户在浏览器访问系统A:http://www.A.com;
2、服务端验证发现没有登录信息,重定向到SSO认证中心,让你登录,但是会携带网址
3、这时候浏览器访问http://www.sso.com/cas/login?service=http://www.A.com
4、我们提交登录信息,这个时候有个很关键的一步认证中心会生成一个cookieSSO种在认证中心的域名下(注
意:用户在 SSO 认证中心登录后,认证中心会 创建一个 Cookie(<font style="color:#DF2A3F;">cookieSSO</font>
),并将它发送到浏览器的响应
中。这个 Cookie 会存储在 <font style="color:#DF2A3F;">http://www.sso.com</font>
域名下。这是浏览器自动触发的机制,后端的响应头有cookei,浏览器会识别出来放置到对应的域名下。)
5、浏览器在 http://www.sso.com 这个域名下会有一个cookie,后面专门用于认证中心的登录认证
6、同时认证中心会生成http://www.A.com对应的一个ticketA
7、携带认证中心提供的ticketA重定向到系统A,此时浏览器网址为:http://www.A.com?ticket=ticketA
8、访问呢系统A,系统A的后台serverA会拿着ticketA去认证中心认证,认证通过后会生成一个cookieA
9、登录成功,后续的请求直接携带cookieA即可,不需要再去认证中心认证
10、用户访问系统B:http://www.B.com;
11、直接重定向到http://www.sso.com/cas/login?service=http://www.B.com
12、注意在http://www.sso.com这个域名下我们种了一个cookieSSO,所以能够访问到认真中心
13、认证中心生成一个ticketB,重定向到http://www.B.com?ticket=ticketB访问系统B
14、系统B的后台serverB会拿着ticketB去认证中心认证,认证通过后会生成一个cookieB
15、登录系统B已经成功,并且后续的请求直接携带cookieB即可
4、GPT的总结
- 用户访问系统A (
http://www.A.com
):
- 系统A检测不到登录信息(比如
cookieA
)。 - 系统A将用户重定向到SSO认证中心 (
http://www.sso.com/cas/login?service=http://www.A.com
),同时携带service
参数,表示登录完成后的回调地址。
- 用户访问 SSO 登录页面 (
http://www.sso.com/cas/login
):
- 用户输入凭据(用户名、密码)。
- 如果登录成功,SSO认证中心生成一个全局会话,并在
http://www.sso.com
域下种植一个cookieSSO
,以标记用户的认证状态。
- 生成并返回
ticketA
:
- SSO认证中心根据
service
参数(http://www.A.com
)生成一个一次性票据(ticketA
)。 - 用户被重定向回系统A (
http://www.A.com?ticket=ticketA
)。
- 系统A验证
ticketA
:
- 系统A的后端(
serverA
)向SSO认证中心验证ticketA
的有效性(通过一个后端接口,如/cas/validate
)。 - 验证通过后,系统A生成自己的登录状态,并为用户种植一个
cookieA
。
- 完成系统A的登录:
- 以后用户访问系统A的请求都携带
cookieA
,无需再次认证。
- 用户访问系统B (
http://www.B.com
):
- 系统B检测不到登录信息(比如
cookieB
),重定向到SSO认证中心 (http://www.sso.com/cas/login?service=http://www.B.com
)。
- SSO认证中心检测
cookieSSO
:
- 因为SSO认证中心域下已经种植了
cookieSSO
,可以直接识别用户已登录,无需再次输入凭据。
- 生成并返回
ticketB
:
- SSO认证中心生成一次性票据
ticketB
,并重定向回系统B (http://www.B.com?ticket=ticketB
)。
- 系统B验证
ticketB
:
- 系统B的后端(
serverB
)向SSO认证中心验证ticketB
。 - 验证通过后,系统B生成自己的登录状态,并为用户种植一个
cookieB
。
- 完成系统B的登录:
- 以后用户访问系统B的请求都携带
cookieB
,无需再次认证。
5、关于4的提问
提问1:
1、 用户访问系统A (http://www.A.com): 系统A检测不到登录信息(比如cookieA)。 系统A将用户重定向到SSO认证中心 (http://www.sso.com/cas/login?service=http://www.A.com), 同时携带service参数,表示登录完成后的回调地址。 这一步是前端做的还是后端做的,系统A检测不到登录信息,是前端检测还是后端检测是怎么检测的,还有系统A将用户重定向到SSO认证中心 (http://www.sso.com/cas/login?service=http://www.A.com), 同时携带service参数,表示登录完成后的回调地址,这个重定向是前端做的还是后端做的,怎么做到的?
回答1:
可以是前端,也可以是后端检测,但建议以后端检测为主。
后端检测:
- 用户请求系统A时,浏览器会携带系统A的
cookieA
(如果存在)。 - 后端会检查该
cookieA
是否有效(比如通过解密、校验签名、或查询会话数据库)。 - 如果
cookieA
无效或不存在,说明用户未登录。
// Express 后端示例
app.use((req, res, next) => {
const token = req.cookies.cookieA; // 获取用户的 cookie
if (!token || !isValidToken(token)) {
// 如果没有有效的 cookie,重定向到 SSO 登录页面
const serviceUrl = encodeURIComponent("http://www.A.com");
// 下面这一步直接引发浏览器自动跳转到指定的 URL
return res.redirect(`http://www.sso.com/cas/login?service=${serviceUrl}`);
}
next();
});
function isValidToken(token) {
// 验证 token 的逻辑(如解密或校验)
return true; // 假设始终有效
}
前端检测:
- 前端检查是否存在某种本地存储(如
localStorage
或cookieA
)。 - 如果不存在,就直接重定向到 SSO 登录页面。
// Vue 前端示例
router.beforeEach((to, from, next) => {
const token = localStorage.getItem("token"); // 或从 cookie 中读取
if (!token) {
const serviceUrl = encodeURIComponent(window.location.href);
window.location.href = `http://www.sso.com/cas/login?service=${serviceUrl}`;
} else {
next();
}
});
提问2:
在用户访问 http://www.A.com?ticket=ticketA
时,系统 A 的后端会自动处理 ticketA
的验证逻辑。这是通过后端监听请求并与 SSO 认证中心进行通信实现的。以下是详细流程和实现示例。
回答2:
- 用户被重定向到
http://www.A.com?ticket=ticketA
- 浏览器发送一个 HTTP GET 请求到系统 A
- 请求中包含 URL 参数
ticket=ticketA
- 系统 A 的后端接收到请求
- 提取
ticketA
参数 - 向 SSO 认证中心发送验证请求(通常是通过一个后端 API,比如
http://www.sso.com/cas/validate
) - SSO 认证中心返回验证结果
- 如果
ticketA
有效 - 后端生成一个本地会话或 JWT(
cookieA
)供用户后续请求使用 - 向用户返回登录成功的页面或数据
- 如果
ticketA
无效 - 返回错误信息或重定向用户重新登录
代码实现:
- 重定向到http://www.A.com?ticket=ticketA, 发起htp请求
GET http://www.A.com?ticket=ticketA
- 后端处理
ticket
并验证
const express = require("express");
const axios = require("axios"); // 用于发送验证请求
const cookieParser = require("cookie-parser");
const app = express();
app.use(cookieParser());
app.get("/", async (req, res) => {
const ticket = req.query.ticket;
// 如果 URL 中包含 ticket 参数
if (ticket) {
try {
// 向 SSO 认证中心验证 ticketA
const ssoResponse = await axios.get(
`http://www.sso.com/cas/validate?ticket=${ticket}&service=${encodeURIComponent("http://www.A.com")}`
);
if (ssoResponse.data && ssoResponse.data.valid) {
// Ticket 验证成功
const user = ssoResponse.data.user; // 假设返回用户信息
res.cookie("cookieA", generateToken(user)); // 设置本地 Cookie 或 Session
return res.send("Login successful! Welcome to System A.");
} else {
// Ticket 验证失败
return res.status(401).send("Invalid ticket. Please login again.");
}
} catch (error) {
console.error("Error validating ticket:", error);
return res.status(500).send("Error validating ticket.");
}
}
// 如果没有 ticket 参数,检查是否已登录
const cookieA = req.cookies.cookieA;
if (cookieA) {
return res.send("Welcome back to System A!");
}
// 未登录,重定向到 SSO 登录页面
const serviceUrl = encodeURIComponent("http://www.A.com");
return res.redirect(`http://www.sso.com/cas/login?service=${serviceUrl}`);
});
// 假设生成 JWT 或 Session 标识
function generateToken(user) {
return `TOKEN_FOR_${user.id}`; // 伪代码,请用实际 JWT 或其他机制生成
}
app.listen(3000, () => console.log("System A running on http://localhost:3000"));
提问3:
后端认证成功后,执行:return res.send(“Login successful! Welcome to System A.”);前端是怎么实现路由跳转到首页的
回答3:
- 可以后端重定向到路由首页
// 假设认证成功后重定向到首页
return res.redirect('http://www.A.com/home');
- 也可以前端可以用生命周期钩子或者路由前置守卫去判断有没有ticket或者cookieA等判断,也可以从路由参数里获取ticket
<template>
<div>
<h1>Welcome to System A</h1>
</div>
</template>
<script>
import axios from 'axios';
export default {
mounted() {
// 获取 URL 中的 ticket 参数
const ticket = new URLSearchParams(window.location.search).get('ticket');
if (ticket) {
// 发送请求到后端验证 ticket
axios.get('/api/validate-ticket', { params: { ticket } })
.then(response => {
if (response.data.success) {
// 登录成功,跳转到首页或其他页面
this.$router.push('/home');
} else {
// 验证失败,跳转到登录页或提示错误
this.$router.push('/login');
}
})
.catch(error => {
console.error('验证 ticket 失败:', error);
});
} else {
// 如果没有 ticket 参数,跳转到登录页
this.$router.push('/login');
}
}
};
</script>
6、最后的实例总结
步骤 1: 用户访问系统 A
假设用户访问 http://www.A.com
,系统 A 会检查是否已经登录。
- 前端: 在页面加载时,检查用户是否已登录。如果没有登录,系统 A 会重定向到 SSO 认证中心。
// 我们可以直接在主页/home路由对应的组件写
// 在 Vue 组件的 created/mounted 钩子中
mounted() {
// 检查是否已经登录(检查 cookieA 或 token)
const isLoggedIn = this.checkLoginStatus();
if (!isLoggedIn) {
// 如果没有登录,重定向到 SSO 认证中心
const serviceUrl = encodeURIComponent(window.location.href); // 当前页面 URL,作为回调地址
window.location.href = `http://www.sso.com/cas/login?service=${serviceUrl}`;
}
},
methods: {
checkLoginStatus() {
// 检查本地存储或 cookie 中是否有登录信息
return document.cookie.includes('cookieA');
}
}
步骤 2: 用户被重定向到 SSO 认证中心
当用户未登录时,浏览器会重定向到 http://www.sso.com/cas/login?service=http://www.A.com
。在这个 URL 中,service
参数是指用户登录成功后返回的系统 A 地址。
- 前端: SSO 认证中心会要求用户输入登录信息,登录后会通过浏览器自动生成一个 cookie(
cookieSSO
)。
步骤 3: 提交登录信息,SSO 设置 Cookie
在用户提交登录信息后,SSO 认证中心会生成一个 cookieSSO
,并在浏览器中保存该 Cookie。这个 Cookie 主要用于在 SSO 认证中心下识别用户的登录状态。
- 前端: 这个步骤由认证中心完成,前端无需干预。
步骤 4: 认证中心生成 ticketA,并重定向回系统 A
认证中心认证成功后,会生成一个 ticketA
,并重定向用户回到 http://www.A.com?ticket=ticketA
。浏览器会携带 ticketA
,用于验证用户身份。
- 前端: 系统 A 在接收到
ticketA
后,需要验证ticketA
是否有效。
步骤 5: 系统 A 验证 ticketA
在接收到 ticketA
后,前端需要将其发送到后端,后端验证该 ticketA
的有效性。
// 前端发请求到后端验证 ticketA
mounted() {
const ticket = new URLSearchParams(window.location.search).get('ticket');
if (ticket) {
axios.get('/api/validate-ticket', { params: { ticket } })
.then(response => {
if (response.data.success) {
// 登录成功,设置本地的登录状态(比如存储 cookieA)
document.cookie = 'cookieA=some-value; Path=/';
this.$router.push('/home');
} else {
// 验证失败,跳转到登录页
this.$router.push('/login');
}
})
.catch(error => {
console.error('验证 ticketA 失败:', error);
this.$router.push('/login');
});
}
}
步骤 6: 后端验证 ticketA
后端接收到 ticketA
后,需要去 SSO 认证中心进行验证。后端调用 SSO 接口进行验证,如果成功,生成 cookieA
并发送给浏览器。
// 后端:验证 ticketA
app.get('/api/validate-ticket', (req, res) => {
const ticket = req.query.ticket;
// 调用 SSO 认证中心验证 ticket
axios.get(`http://www.sso.com/cas/validate?ticket=${ticket}`)
.then(response => {
if (response.data.success) {
// ticket 验证成功,生成 cookieA 并发送给浏览器
res.cookie('cookieA', 'some-value', { httpOnly: true });
res.json({ success: true });
} else {
res.json({ success: false });
}
})
.catch(error => {
res.status(500).json({ success: false, message: '验证失败' });
});
});
步骤 7: 用户访问系统 B
假设用户访问 http://www.B.com
,由于 SSO 认证中心会检查 cookieSSO
,如果用户已经登录,认证中心会直接跳转到系统 B,并生成 ticketB
。
- 前端: 当系统 B 检测到没有登录时,重定向到 SSO 认证中心。由于浏览器已携带
cookieSSO
,认证中心会直接跳过登录过程,生成ticketB
。
// 用户访问系统 B,重定向到 SSO 认证中心
mounted() {
const isLoggedIn = this.checkLoginStatus();
if (!isLoggedIn) {
const serviceUrl = encodeURIComponent(window.location.href); // 当前页面 URL,作为回调地址
window.location.href = `http://www.sso.com/cas/login?service=${serviceUrl}`;
}
}
步骤 8: 系统 B 验证 ticketB
系统 B 验证 ticketB
,如果验证成功,生成 cookieB
。
// 前端发请求到后端验证 ticketB
mounted() {
const ticket = new URLSearchParams(window.location.search).get('ticket');
if (ticket) {
axios.get('/api/validate-ticket', { params: { ticket } })
.then(response => {
if (response.data.success) {
// 登录成功,设置本地的登录状态(比如存储 cookieB)
document.cookie = 'cookieB=some-value; Path=/';
this.$router.push('/home');
} else {
// 验证失败,跳转到登录页
this.$router.push('/login');
}
})
.catch(error => {
console.error('验证 ticketB 失败:', error);
this.$router.push('/login');
});
}
}