🎤 面试官:你好,请介绍一下用户登录的基本流程。
面试者: 您好,一个标准的用户登录流程通常包括以下几个步骤:
- 前端收集用户的 用户名(或手机号/邮箱)和密码;
- 通过 HTTPS 加密传输到后端 API;
- 后端根据用户名查询用户信息;
- 使用加密算法(如 BCrypt、SM3)校验密码是否匹配;
- 校验通过后,生成一个 登录凭证(如 JWT token) 返回给客户端;
- 客户端后续请求携带 token,服务端通过拦截器验证合法性。
这是一个基础闭环。但在实际生产中,我们还需要加入登录防控机制,防止恶意攻击。
🎤 面试官:那你提到“登录防控”,具体有哪些常见的安全风险?怎么应对?
面试者: 是的,登录是系统的第一道门,也是黑客攻击的重点目标。常见的风险有以下几类,我们也有对应的防控策略:
🎤 面试官:比如呢?举个例子。
面试者: 好的,我来逐个说明。
风险一:暴力破解(Brute Force Attack)
黑客用脚本不断尝试密码,比如
admin/123456、admin/123457……
✅ 应对策略:多维度限流
- 同一个 用户名,5分钟内失败超过 5 次 → 锁定 30 分钟
- 同一个 IP,1分钟内请求超过 10 次 → 拦截
- 使用 Redis 记录失败次数,避免查库压力
String key = "login:fail:" + username;
Long count = redisTemplate.opsForValue().increment(key);
if (count == 1) {
redisTemplate.expire(key, Duration.ofMinutes(5));
}
if (count > 5) {
return Result.fail("账户已锁定");
}🎤 面试官:如果黑客用代理IP池,每个IP只试几次,绕过IP限流呢?
面试者: 非常好的问题。
这种叫分布式暴力破解,单靠IP限流不够,我们需要引入 设备指纹(Device Fingerprint)。
前端采集设备特征,比如:
- UserAgent
- 屏幕分辨率
- 时区、语言
- WebGL / Canvas 指纹
然后生成一个 device_id 上报:
const deviceId = md5(navigator.userAgent + screen.width + Intl.DateTimeFormat().resolvedOptions().timeZone);后端用这个 deviceId 做限流:
- 同一设备频繁失败 → 封设备
- 同一账号在多个设备登录 → 高风险标记
这样就算换IP,设备指纹不变,也能识别是同一个攻击源。
🎤 面试官:那什么时候该弹验证码?不能每次登录都弹吧?
面试者: 完全同意。弹验证码要智能触发,不能影响正常用户体验。
我们通常在以下高风险场景触发验证码:
| 场景 | 是否触发 |
|---|---|
| 新设备首次登录 | ✅ 是 |
| 登录失败2次以上 | ✅ 是 |
| IP来自国外或高风险地区 | ✅ 是 |
| 请求头异常(如无Referer) | ✅ 是 |
| 常用设备、本地登录 | ❌ 否 |
验证码类型我们也升级了,不用简单的数字验证码,而是用:
- 滑块拼图
- 点选文字
- 行为轨迹分析(鼠标移动曲线)
这些能有效区分真人和脚本。
🎤 面试官:如果黑客拿其他网站泄露的“用户名/密码”来试我们的系统,怎么办?
面试者: 这就是典型的 撞库攻击(Credential Stuffing)。
我们有三道防线:
1. 监控高频用户名
- 如果一个用户名被大量IP尝试 → 很可能是撞库目标
- 我们会加强对该账号的保护,比如强制短信验证
2. 密码泄露检测
- 对接“已泄露密码库”(如 HaveIBeenPwned)
- 用户注册或改密码时,如果密码在泄露库中 → 提示“该密码不安全”
3. 蜜罐账号(Honeypot)
- 创建一些“看起来真实但不存在”的账号,如
finance01、admin_test - 如果有人登录这些账号 → 一定是黑客 → 直接封IP + 告警
if (isHoneypotAccount(username)) {
securityService.alert("撞库攻击 detected from IP: " + ip);
blockIp(ip);
}🎤 面试官:用户平时在北京登录,突然从越南登录,你怎么判断风险?
面试者: 这是典型的异地登录风险,我们通过 IP地理定位 + 用户历史行为 来判断。
具体做法:
- 调用 IP 定位服务(如 GeoLite2、阿里云 IP 定位)获取登录城市;
- 查询该用户的历史常用登录地;
- 判断是否“异地”或“跨国”:
String currentCity = ipLocationService.getCity(ip);
String[] usualCities = userLoginHistoryService.getUsualCities(userId);
if (!Arrays.asList(usualCities).contains(currentCity)) {
double distance = GeoUtils.calculateDistance(currentCity, usualCities[0]);
if (distance > 1000) { // 超过1000公里
// 高风险,要求短信验证
return RiskLevel.HIGH;
}
}高风险登录时,我们会:
- 强制短信验证
- 发送安全通知:“您在XX地登录,如非本人操作请立即改密”
- 甚至直接拦截,需人工审核
🎤 面试官:登录成功后,还需要做什么?
面试者: 登录成功不是结束,而是风控的开始。我们还会做三件事:
清除失败记录
javaredisTemplate.delete("login:fail:" + username);避免下次登录被误限。
记录完整登录日志
- 用户ID、IP、设备ID、时间、地理位置
- 用于安全审计、对账、溯源
触发安全通知(可选)
- 邮件/短信通知用户:“您于 XX 时间在 XX 地登录”
- 高风险登录 → 强制改密
🎤 面试官:大厂是怎么做登录风控的?是手写这些逻辑吗?
面试者: 不会的。在大厂,我们会构建一个独立的风控中台系统,而不是在业务代码里写 if/else。
核心组件包括:
✅ 风控决策引擎
- 支持规则配置:如“失败5次 → 锁定”
- 支持模型打分:AI 判断本次登录风险值(0~100)
- 风险高 → 弹验证码 or 拦截
✅ 数据支撑
- 用户行为画像库
- 黑产IP/设备库
- 实时攻击情报
✅ 架构分层
用户
↓
API 网关(限流)
↓
设备指纹 + IP定位
↓
┌─ 风控引擎 ─┐
↓ ↓
放行 弹验/拦截
↓ ↓
查库登录 返回失败这样既能保证安全,又能快速响应新型攻击。
🎤 面试官:总结一下,登录防控的关键点是什么?
面试者: 我认为有 六个关键点,可以总结为“六防”:
| 防 | 说明 |
|---|---|
| 🔒 防暴力破解 | 多维度限流(用户名、IP、设备) |
| 🤖 防机器人 | 设备指纹 + 行为验证码 |
| 🌍 防异地登录 | IP定位 + 用户历史行为分析 |
| 💣 防撞库攻击 | 蜜罐账号 + 密码泄露检测 |
| 🧩 防体验下降 | 智能弹验,只在高风险时触发 |
| 📊 防无据可查 | 完整日志记录,支持审计 |
最后我想说: 登录是功能,防控是系统。 光能登录,不叫上线;能防住攻击,才叫上线。
🎤 面试官:用“手机验证码登录”是不是就没问题了?你怎么看?
面试者: 这是一个很常见的误解。很多人觉得:“我都不用密码了,发个验证码登录,总该安全了吧?” 但其实——手机验证码登录,并不等于绝对安全。它只是换了一种攻击面,黑客的手段也跟着升级了。
我可以从 三大风险 和 对应的防控策略 来说明。
🎤 面试官:哦?那你说说,手机验证码登录有哪些风险?
面试者: 好的,主要有以下三类高危场景:
🔴 风险一:短信轰炸(SMS Flooding)
黑客写个脚本,疯狂请求“发送验证码”,目标手机号被短信刷屏。
影响:
- 用户被骚扰,投诉率上升
- 短信成本飙升(每条几毛钱,一天几万条就是几万块)
- 服务被拖垮(短信接口被打满)
✅ 防控策略:
- 同一手机号,1分钟内只能请求1次
- 同一 IP 或 设备,1小时最多请求5个不同手机号
- 前端加图形验证码(防止脚本自动提交)
- 敏感操作走“滑块验证 + 风控打分”
// 限制发送频率
String key = "sms:send:" + phone;
if (redisTemplate.hasKey(key)) {
return Result.fail("操作太频繁,请60秒后重试");
}
redisTemplate.opsForValue().set(key, "1", Duration.ofSeconds(60));🔴 风险二:验证码被嗅探 or 手机被劫持
黑客通过以下方式获取验证码:
- 伪基站:在用户附近搭建假基站,截获短信
- SIM卡劫持:贿赂运营商员工,把用户号码转到黑客手机
- 手机木马:用户手机中毒,短信被自动转发
案例: 2023年某银行用户被盗刷,就是因为黑客用“SIM Swap”把验证码转走。
✅ 防控策略:
- 高风险操作(如大额转账)必须 二次验证(如指纹、人脸识别)
- 登录后提示:“您于 XX 时间在 XX 设备登录”,支持一键“异常退出”
- 支持用户绑定“可信设备”,非可信设备登录需额外验证
🔴 风险三:自动化脚本注册/登录(Bot Attack)
黑客用接码平台 + 虚拟手机号,批量注册账号,用于:
- 刷单
- 刷券
- 养号卖钱
典型特征:
- 批量手机号(如
170xxxx、171xxxx) - 同一设备注册多个账号
- 注册后立即做任务,行为高度一致
✅ 防控策略:
- 限制新账号权限
- 新注册账号不能立即参与秒杀、领券
- 需完成实名 or 行为积累后才开放
- 接码平台识别
- 对接第三方库,识别“虚拟运营商号码”、“接码平台号段”
- 如:
170、171、165等号段重点监控
- 行为风控模型
- 分析用户行为:点击间隔、页面停留、滑动轨迹
- 机器人行为太“整齐”,真人有“噪声”
🎤 面试官:那是不是说,手机验证码登录还不如密码登录?
面试者: 不是的,我并不是说“验证码登录不好”,而是想强调:任何登录方式都有攻击面,关键在于有没有配套的风控体系。
✅ 手机验证码登录的优点依然明显:
- 用户体验好(不用记密码)
- 避免弱密码问题
- 适合移动端为主的产品
但你必须配套:
- 短信发送限流
- 设备指纹识别
- 行为风控模型
- 异常登录告警
就像“开车不用系安全带”——车本身是好的,但少了防护,风险就大了。
🎤 面试官:那有没有更安全的登录方式?
面试者: 有的,我们可以组合使用多种方式,做到“安全 + 体验”平衡:
✅ 推荐方案:多因子认证(MFA)
| 场景 | 登录方式 |
|---|---|
| 日常登录 | 手机验证码 + 设备指纹 |
| 高风险操作 | 短信 + 人脸识别 |
| 管理员登录 | 动态令牌(如 Google Authenticator) + IP 白名单 |
✅ 未来趋势:无密码登录(Passwordless)
- Apple 的 Sign in with Apple
- 微软的 Windows Hello
- 基于 FIDO2 / WebAuthn 的生物识别登录
这些才是真正更安全的方向。
🎤 面试官:总结一下?
面试者: 好的,总结三点:
- 手机验证码登录 ≠ 绝对安全,它面临“短信轰炸、验证码截获、批量注册”三大风险;
- 必须配套风控系统:限流、设备指纹、行为分析、接码识别;
- 最佳实践是“多因子 + 智能风控”,根据风险等级动态调整验证强度。
记住一句话: 没有绝对安全的登录方式,只有持续对抗的风控体系。
🎤 面试官:你有没有做过第三方登录?比如微信、QQ、支付宝、GitHub 登录?
面试者: 有的,我在多个项目中都实现过第三方登录,比如:
- 微信扫码登录(Web 端)
- 手机 QQ 快捷登录(App 端)
- GitHub 登录(后台管理系统)
- 支付宝生活号授权登录
这类需求很常见,尤其是在 C 端产品中,第三方登录能大幅提升注册转化率,减少用户流失。
🎤 面试官:那你说说,第三方登录的流程是怎样的?
面试者: 好的,我以 微信扫码登录 Web 网站 为例,讲一下核心流程。
🔁 第三方登录通用流程(OAuth 2.0 协议)
- 用户点击“微信登录”按钮;
- 前端跳转到微信的授权页面:https://open.weixin.qq.com/connect/qrconnect?appid=XXX&redirect_uri=YYY&response_type=code&scope=...
- 用户在手机上确认授权;
- 微信回调我们的
redirect_uri,带上一个临时code; - 后端用
code + appid + appsecret向微信服务器换取access_token; - 再用
access_token换取用户信息(如 openid、昵称、头像); - 根据
openid判断是否是新用户:- 是新用户 → 自动创建账号,绑定 openid
- 是老用户 → 直接登录,生成自己的 token
- 返回登录凭证给前端,完成登录。
核心是:我们不拿用户密码,只拿授权后的唯一标识(openid)
🎤 面试官:那如果用户第一次用微信登录,你怎么创建账号?
面试者: 这是个关键问题。我们通常这样处理:
✅ 新用户注册逻辑:
- 拿到微信返回的
openid(微信用户的唯一 ID) - 查询本地数据库:
SELECT * FROM user WHERE wechat_openid = ? - 如果不存在:
- 创建新用户记录
- 昵称、头像从微信拉取(可选)
- 生成一个本地
user_id - 绑定
wechat_openid
- 如果用户后续用手机号登录,可以再做账号绑定,实现多方式登录。
💡 关键设计:
openid是第三方平台的用户唯一标识,必须建唯一索引- 一个用户可以绑定多个第三方登录方式(微信、QQ、GitHub)
- 要支持“解绑”和“换绑”功能(安全考虑)
ALTER TABLE user ADD COLUMN wechat_openid VARCHAR(64) UNIQUE;
ALTER TABLE user ADD COLUMN qq_openid VARCHAR(64) UNIQUE; -- 创建索引🎤 面试官:如果同一个微信账号,在手机 App 和网页都登录,是同一个用户吗?
面试者: 不一定,要看我们对接的是哪个接口。
微信有两个不同的 openid:
| 类型 | 说明 |
|---|---|
openid | 同一个公众号下,用户唯一 |
unionid | 同一个微信开放平台账号下,用户全局唯一 |
✅ 最佳实践:
- 如果你的 App、公众号、小程序都绑定在同一个微信开放平台账号下
- 那就可以拿到
unionid—— 它才是用户的真实唯一标识 - 用
unionid做用户合并,实现“跨端统一账号”
// 推荐用 unionid 做用户识别
String unionid = wechatUserService.getUnionId(accessToken, openid);
User user = userService.findByUnionId(unionid);如果不用
unionid,同一个用户在 App 和小程序里会被当成两个人。
🎤 面试官:第三方登录有没有安全风险?
面试者: 有,虽然我们不碰密码,但仍有几个风险点需要注意:
🔴 风险一:code 被截获
code` 是一次性凭证,如果被中间人拿到,可能被用来换取 `access_token
✅ 防范:
- 回调地址必须是 HTTPS
redirect_uri必须严格匹配预设白名单code有效期很短(通常 5 分钟),且只能使用一次
🔴 风险二:伪造授权回调
黑客伪造一个回调页面,传一个假
code或openid
✅ 防范:
- 所有 token 交换都在后端进行,前端不参与
- access_token 换用户信息也由后端调用
- 严格校验 openid、access_token 的合法性
🔴 风险三:用户冒用
黑客退出自己的微信,用别人的手机扫码登录,完成授权
✅ 防范:
- 这属于“物理层风险”,我们无法控制
- 但可以在登录后提示:“您使用微信登录,设备:iPhone 15”
- 支持用户“退出所有设备”
🎤 面试官:如果用户用微信登录了一次,下次换手机登录,怎么处理?
面试者: 流程是一样的,只要用户授权,我们就能拿到
openid或unionid,查到原来的账号,直接登录。
但我们可以做得更智能:
✅ 优化体验:
- 记录“常用设备”
- 新设备登录 → 提示“新设备登录”,可选是否信任
- 结合 IP、地理位置判断风险,高风险时要求短信验证
✅ 安全兜底:
- 用户可在个人中心查看“登录设备列表”
- 支持“一键退出所有其他设备”
🎤 面试官:总结一下,做第三方登录要注意什么?
面试者: 我总结为 “三要三不要”:
| 要 | 不要 |
|---|---|
| ✅ 要用 OAuth 2.0 标准流程 | ❌ 不要在前端做 token 交换 |
✅ 要用 unionid 做用户统一 | ❌ 不要用 openid 当唯一标识(跨端时) |
| ✅ 要做账号绑定与解绑 | ❌ 不要强制用户绑定手机号(影响转化) |
最后一点:第三方登录是提升体验的利器,但背后要有安全 + 风控 + 用户体系的支撑。
🎤 面试官:听说过单点登录吗?能讲讲什么是 SSO 吗?
面试者: 有的,我不仅听说过,还在项目中实际设计和实现过单点登录系统。
SSO(Single Sign-On),中文叫“单点登录”,它的核心理念是:
用户只需要登录一次,就可以访问多个相互信任的系统或应用,无需重复登录。
✅ 举个例子:
假设我们公司有三个系统:
oa.company.com(办公系统)crm.company.com(客户管理系统)mail.company.com(邮箱系统)
如果每个系统都要单独登录,用户体验很差。
而用了 SSO 之后:
- 用户访问 OA,跳转到统一登录页;
- 输入一次账号密码,登录成功;
- 再去访问 CRM 或 邮箱,自动登录,无需再输密码。
这就是 SSO 的价值:提升体验、降低密码疲劳、统一安全管理。
🎤 面试官:那 SSO 的实现原理是什么?比如用户第一次登录,流程是怎样的?
面试者: 我以最典型的 基于 Cookie + 重定向 的 SSO 流程为例,比如企业内部系统常用的方案。
假设:
- SSO Server:
sso.company.com - 应用 A:
oa.company.com - 应用 B:
crm.company.com
🔁 流程如下(用户第一次访问 OA):
用户访问
oa.company.com;OA 发现未登录,重定向到 SSO Server:https://sso.company.com/login?redirect_uri=https://oa.company.com/callback
用户在 SSO 页面输入账号密码,提交;
SSO Server 验证通过,创建全局会话(Global Session),比如:
javaredis.set("sso:ticket:TGT-123", "user_id=1001", 有效期30分钟);SSO Server 给浏览器种一个 SSO Cookie(域:
.company.com),值为TGT-123;同时生成一个一次性token(叫 ST,Service Ticket),重定向回 OA:
urlhttps://oa.company.com/callback?st=ST-456OA 后端收到
st=ST-456,拿着它去 SSO Server 验证:httpPOST https://sso.company.com/verify { "ticket": "ST-456", "service": "https://oa.company.com" }SSO Server 验证 ST 合法,返回用户信息;
OA 创建自己的本地会话(如 JWT),登录完成。
✅ 关键点:SSO Server 负责认证,应用系统负责自己的授权。
🎤 面试官:那用户再去访问 CRM,为什么不用再登录?
面试者: 因为用户浏览器里已经有 SSO Cookie 了!
流程如下:
用户访问
crm.company.com;CRM 发现本地未登录,重定向到 SSO Server:
https://sso.company.com/login?redirect_uri=https://crm.company.com/callbackSSO Server 收到请求,检查浏览器是否带有
.company.com域的 SSO Cookie;如果有,说明用户已在其他系统登录过;
SSO Server 不再要求输密码,直接生成一个新的 ST(如
ST-789),重定向回 CRM;CRM 拿 ST 去验证,拿到用户信息,创建本地会话,登录成功。
🔑 核心:SSO Cookie 是跨应用的身份凭证,只要它有效,用户就在“已登录状态”。
🎤 面试官:如果用户退出 OA,其他系统也退出吗?
面试者: 这要看我们实现的是 单点退出(Single Logout) 还是 局部退出。
✅ 理想情况:支持单点退出
用户在 OA 点“退出登录”;
OA 删除自己的本地会话;
同时重定向到 SSO Server 的 logout 接口:
https://sso.company.com/logout?redirect_uri=https://oa.company.comSSO Server 删除全局会话(
TGT-123),并清除 SSO Cookie;SSO Server 返回一个“退出通知页面”,页面中包含对其他已登录应用的隐藏 iframe 注销请求;
html<iframe src="https://crm.company.com/logout-sso"></iframe> <iframe src="https://mail.company.com/logout-sso"></iframe>每个应用收到请求后,删除自己的本地会话。
⚠️ 注意:单点退出实现复杂,因为要通知所有应用,可能有失败情况。
🎤 面试官:SSO 有哪些常见的实现方案?
面试者: 常见的有三种:
| 方案 | 说明 | 适用场景 |
|---|---|---|
| 自研 SSO | 基于 Cookie + Token,自己实现登录中心 | 中小公司、内部系统 |
| OAuth 2.0 / OIDC | 使用标准协议,如 Keycloak、Auth0 | 多租户、开放平台 |
| SAML | 企业级标准,XML 格式,较重 | 传统企业、政府系统 |
我们项目中用的是 自研 + JWT 的方式,轻量、可控。
🎤 面试官:SSO 有什么安全风险?
面试者: 有,主要风险包括:
🔴 1. CSRF 攻击
- 黑客诱导用户访问恶意页面,自动发起 SSO 登录或退出
- ✅ 防范:使用
state参数 + 同源检测
🔴 2. Ticket 被盗用
ST是一次性 token,但如果被截获,在有效期内可能被重放- ✅ 防范:HTTPS + 短有效期 + 服务端验证 referer
🔴 3. SSO Cookie 被窃取
- 如果
.company.com域被 XSS 攻击,Cookie 可能泄露 - ✅ 防范:设置
HttpOnly、Secure、SameSite=Strict
🔴 4. 跨域问题
- 如果应用不在同一个主域下(如
a.com和b.com),Cookie 无法共享 - ✅ 解决方案:
- 使用 OAuth 2.0(基于重定向传 token)
- 或通过 前端中转(登录页嵌 iframe + postMessage)
🎤 面试官:总结一下,什么时候该用 SSO?
面试者: 我总结为以下几种场景适合上 SSO:
✅ 适合上 SSO 的场景:
- 公司有多个内部系统(OA、CRM、ERP)
- 用户是同一群体(如员工、合作伙伴)
- 希望统一账号管理、统一安全策略
- 有权限中心、审计中心需求
❌ 不需要 SSO 的场景:
- 只有一个应用
- 系统之间完全独立,用户群体不同
- 安全要求不高,或不想引入复杂性