Skip to content

🎤 面试官:你好,请介绍一下用户登录的基本流程。

面试者: 您好,一个标准的用户登录流程通常包括以下几个步骤:

  1. 前端收集用户的 用户名(或手机号/邮箱)和密码
  2. 通过 HTTPS 加密传输到后端 API;
  3. 后端根据用户名查询用户信息;
  4. 使用加密算法(如 BCrypt、SM3)校验密码是否匹配;
  5. 校验通过后,生成一个 登录凭证(如 JWT token) 返回给客户端;
  6. 客户端后续请求携带 token,服务端通过拦截器验证合法性。

这是一个基础闭环。但在实际生产中,我们还需要加入登录防控机制,防止恶意攻击。


🎤 面试官:那你提到“登录防控”,具体有哪些常见的安全风险?怎么应对?

面试者: 是的,登录是系统的第一道门,也是黑客攻击的重点目标。常见的风险有以下几类,我们也有对应的防控策略:


🎤 面试官:比如呢?举个例子。

面试者: 好的,我来逐个说明。

风险一:暴力破解(Brute Force Attack)

黑客用脚本不断尝试密码,比如 admin/123456admin/123457……

应对策略:多维度限流

  • 同一个 用户名,5分钟内失败超过 5 次 → 锁定 30 分钟
  • 同一个 IP,1分钟内请求超过 10 次 → 拦截
  • 使用 Redis 记录失败次数,避免查库压力
java
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 上报:

java
const deviceId = md5(navigator.userAgent + screen.width + Intl.DateTimeFormat().resolvedOptions().timeZone);

后端用这个 deviceId 做限流:

  • 同一设备频繁失败 → 封设备
  • 同一账号在多个设备登录 → 高风险标记

这样就算换IP,设备指纹不变,也能识别是同一个攻击源。


🎤 面试官:那什么时候该弹验证码?不能每次登录都弹吧?

面试者: 完全同意。弹验证码要智能触发,不能影响正常用户体验。

我们通常在以下高风险场景触发验证码:

场景是否触发
新设备首次登录✅ 是
登录失败2次以上✅ 是
IP来自国外或高风险地区✅ 是
请求头异常(如无Referer)✅ 是
常用设备、本地登录❌ 否

验证码类型我们也升级了,不用简单的数字验证码,而是用:

  • 滑块拼图
  • 点选文字
  • 行为轨迹分析(鼠标移动曲线)

这些能有效区分真人和脚本。


🎤 面试官:如果黑客拿其他网站泄露的“用户名/密码”来试我们的系统,怎么办?

面试者: 这就是典型的 撞库攻击(Credential Stuffing)

我们有三道防线:

1. 监控高频用户名

  • 如果一个用户名被大量IP尝试 → 很可能是撞库目标
  • 我们会加强对该账号的保护,比如强制短信验证

2. 密码泄露检测

  • 对接“已泄露密码库”(如 HaveIBeenPwned)
  • 用户注册或改密码时,如果密码在泄露库中 → 提示“该密码不安全”

3. 蜜罐账号(Honeypot)

  • 创建一些“看起来真实但不存在”的账号,如 finance01admin_test
  • 如果有人登录这些账号 → 一定是黑客 → 直接封IP + 告警
java
if (isHoneypotAccount(username)) {
    securityService.alert("撞库攻击 detected from IP: " + ip);
    blockIp(ip);
}

🎤 面试官:用户平时在北京登录,突然从越南登录,你怎么判断风险?

面试者: 这是典型的异地登录风险,我们通过 IP地理定位 + 用户历史行为 来判断。

具体做法:

  1. 调用 IP 定位服务(如 GeoLite2、阿里云 IP 定位)获取登录城市;
  2. 查询该用户的历史常用登录地;
  3. 判断是否“异地”或“跨国”:
java
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地登录,如非本人操作请立即改密”
  • 甚至直接拦截,需人工审核

🎤 面试官:登录成功后,还需要做什么?

面试者: 登录成功不是结束,而是风控的开始。我们还会做三件事:

  1. 清除失败记录

    java
    redisTemplate.delete("login:fail:" + username);

    避免下次登录被误限。

  2. 记录完整登录日志

    • 用户ID、IP、设备ID、时间、地理位置
    • 用于安全审计、对账、溯源
  3. 触发安全通知(可选)

    • 邮件/短信通知用户:“您于 XX 时间在 XX 地登录”
    • 高风险登录 → 强制改密

🎤 面试官:大厂是怎么做登录风控的?是手写这些逻辑吗?

面试者: 不会的。在大厂,我们会构建一个独立的风控中台系统,而不是在业务代码里写 if/else。

核心组件包括:

✅ 风控决策引擎

  • 支持规则配置:如“失败5次 → 锁定”
  • 支持模型打分:AI 判断本次登录风险值(0~100)
  • 风险高 → 弹验证码 or 拦截

✅ 数据支撑

  • 用户行为画像库
  • 黑产IP/设备库
  • 实时攻击情报

✅ 架构分层

        用户

     API 网关(限流)

   设备指纹 + IP定位

    ┌─ 风控引擎 ─┐
    ↓           ↓
放行        弹验/拦截
    ↓           ↓
查库登录     返回失败

这样既能保证安全,又能快速响应新型攻击。


🎤 面试官:总结一下,登录防控的关键点是什么?

面试者: 我认为有 六个关键点,可以总结为“六防”:

说明
🔒 防暴力破解多维度限流(用户名、IP、设备)
🤖 防机器人设备指纹 + 行为验证码
🌍 防异地登录IP定位 + 用户历史行为分析
💣 防撞库攻击蜜罐账号 + 密码泄露检测
🧩 防体验下降智能弹验,只在高风险时触发
📊 防无据可查完整日志记录,支持审计

最后我想说: 登录是功能,防控是系统。 光能登录,不叫上线;能防住攻击,才叫上线。

🎤 面试官:用“手机验证码登录”是不是就没问题了?你怎么看?

面试者: 这是一个很常见的误解。很多人觉得:“我都不用密码了,发个验证码登录,总该安全了吧?” 但其实——手机验证码登录,并不等于绝对安全。它只是换了一种攻击面,黑客的手段也跟着升级了。

我可以从 三大风险对应的防控策略 来说明。


🎤 面试官:哦?那你说说,手机验证码登录有哪些风险?

面试者: 好的,主要有以下三类高危场景:


🔴 风险一:短信轰炸(SMS Flooding)

黑客写个脚本,疯狂请求“发送验证码”,目标手机号被短信刷屏。

影响

  • 用户被骚扰,投诉率上升
  • 短信成本飙升(每条几毛钱,一天几万条就是几万块)
  • 服务被拖垮(短信接口被打满)

防控策略

  • 同一手机号,1分钟内只能请求1次
  • 同一 IP 或 设备,1小时最多请求5个不同手机号
  • 前端加图形验证码(防止脚本自动提交)
  • 敏感操作走“滑块验证 + 风控打分”
java
// 限制发送频率
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)

黑客用接码平台 + 虚拟手机号,批量注册账号,用于:

  • 刷单
  • 刷券
  • 养号卖钱

典型特征

  • 批量手机号(如 170xxxx171xxxx
  • 同一设备注册多个账号
  • 注册后立即做任务,行为高度一致

防控策略

  1. 限制新账号权限
    • 新注册账号不能立即参与秒杀、领券
    • 需完成实名 or 行为积累后才开放
  2. 接码平台识别
    • 对接第三方库,识别“虚拟运营商号码”、“接码平台号段”
    • 如:170171165 等号段重点监控
  3. 行为风控模型
    • 分析用户行为:点击间隔、页面停留、滑动轨迹
    • 机器人行为太“整齐”,真人有“噪声”

🎤 面试官:那是不是说,手机验证码登录还不如密码登录?

面试者: 不是的,我并不是说“验证码登录不好”,而是想强调:任何登录方式都有攻击面,关键在于有没有配套的风控体系

手机验证码登录的优点依然明显

  • 用户体验好(不用记密码)
  • 避免弱密码问题
  • 适合移动端为主的产品

但你必须配套:

  • 短信发送限流
  • 设备指纹识别
  • 行为风控模型
  • 异常登录告警

就像“开车不用系安全带”——车本身是好的,但少了防护,风险就大了。


🎤 面试官:那有没有更安全的登录方式?

面试者: 有的,我们可以组合使用多种方式,做到“安全 + 体验”平衡:

✅ 推荐方案:多因子认证(MFA)

场景登录方式
日常登录手机验证码 + 设备指纹
高风险操作短信 + 人脸识别
管理员登录动态令牌(如 Google Authenticator) + IP 白名单

✅ 未来趋势:无密码登录(Passwordless)

  • Apple 的 Sign in with Apple
  • 微软的 Windows Hello
  • 基于 FIDO2 / WebAuthn 的生物识别登录

这些才是真正更安全的方向。


🎤 面试官:总结一下?

面试者: 好的,总结三点:

  1. 手机验证码登录 ≠ 绝对安全,它面临“短信轰炸、验证码截获、批量注册”三大风险;
  2. 必须配套风控系统:限流、设备指纹、行为分析、接码识别;
  3. 最佳实践是“多因子 + 智能风控”,根据风险等级动态调整验证强度。

记住一句话: 没有绝对安全的登录方式,只有持续对抗的风控体系。

🎤 面试官:你有没有做过第三方登录?比如微信、QQ、支付宝、GitHub 登录?

面试者: 有的,我在多个项目中都实现过第三方登录,比如:

  • 微信扫码登录(Web 端)
  • 手机 QQ 快捷登录(App 端)
  • GitHub 登录(后台管理系统)
  • 支付宝生活号授权登录

这类需求很常见,尤其是在 C 端产品中,第三方登录能大幅提升注册转化率,减少用户流失。


🎤 面试官:那你说说,第三方登录的流程是怎样的?

面试者: 好的,我以 微信扫码登录 Web 网站 为例,讲一下核心流程。

🔁 第三方登录通用流程(OAuth 2.0 协议)

  1. 用户点击“微信登录”按钮;
  2. 前端跳转到微信的授权页面:https://open.weixin.qq.com/connect/qrconnect?appid=XXX&redirect_uri=YYY&response_type=code&scope=...
  3. 用户在手机上确认授权;
  4. 微信回调我们的 redirect_uri,带上一个临时 code
  5. 后端用 code + appid + appsecret 向微信服务器换取 access_token
  6. 再用 access_token 换取用户信息(如 openid、昵称、头像);
  7. 根据openid判断是否是新用户:
    • 是新用户 → 自动创建账号,绑定 openid
    • 是老用户 → 直接登录,生成自己的 token
  8. 返回登录凭证给前端,完成登录。

核心是:我们不拿用户密码,只拿授权后的唯一标识(openid)


🎤 面试官:那如果用户第一次用微信登录,你怎么创建账号?

面试者: 这是个关键问题。我们通常这样处理:

✅ 新用户注册逻辑:

  1. 拿到微信返回的 openid(微信用户的唯一 ID)
  2. 查询本地数据库:SELECT * FROM user WHERE wechat_openid = ?
  3. 如果不存在:
    • 创建新用户记录
    • 昵称、头像从微信拉取(可选)
    • 生成一个本地 user_id
    • 绑定 wechat_openid
  4. 如果用户后续用手机号登录,可以再做账号绑定,实现多方式登录。

💡 关键设计:

  • openid 是第三方平台的用户唯一标识,必须建唯一索引
  • 一个用户可以绑定多个第三方登录方式(微信、QQ、GitHub)
  • 要支持“解绑”和“换绑”功能(安全考虑)
sql
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 做用户合并,实现“跨端统一账号”
java
// 推荐用 unionid 做用户识别
String unionid = wechatUserService.getUnionId(accessToken, openid);
User user = userService.findByUnionId(unionid);

如果不用 unionid,同一个用户在 App 和小程序里会被当成两个人。


🎤 面试官:第三方登录有没有安全风险?

面试者: 有,虽然我们不碰密码,但仍有几个风险点需要注意:

🔴 风险一:code 被截获

code` 是一次性凭证,如果被中间人拿到,可能被用来换取 `access_token

✅ 防范:

  • 回调地址必须是 HTTPS
  • redirect_uri 必须严格匹配预设白名单
  • code 有效期很短(通常 5 分钟),且只能使用一次

🔴 风险二:伪造授权回调

黑客伪造一个回调页面,传一个假 codeopenid

✅ 防范:

  • 所有 token 交换都在后端进行,前端不参与
  • access_token 换用户信息也由后端调用
  • 严格校验 openid、access_token 的合法性

🔴 风险三:用户冒用

黑客退出自己的微信,用别人的手机扫码登录,完成授权

✅ 防范:

  • 这属于“物理层风险”,我们无法控制
  • 但可以在登录后提示:“您使用微信登录,设备:iPhone 15”
  • 支持用户“退出所有设备”

🎤 面试官:如果用户用微信登录了一次,下次换手机登录,怎么处理?

面试者: 流程是一样的,只要用户授权,我们就能拿到 openidunionid,查到原来的账号,直接登录。

但我们可以做得更智能:

✅ 优化体验:

  • 记录“常用设备”
  • 新设备登录 → 提示“新设备登录”,可选是否信任
  • 结合 IP、地理位置判断风险,高风险时要求短信验证

✅ 安全兜底:

  • 用户可在个人中心查看“登录设备列表”
  • 支持“一键退出所有其他设备”

🎤 面试官:总结一下,做第三方登录要注意什么?

面试者: 我总结为 “三要三不要”

不要
✅ 要用 OAuth 2.0 标准流程❌ 不要在前端做 token 交换
✅ 要用 unionid 做用户统一❌ 不要用 openid 当唯一标识(跨端时)
✅ 要做账号绑定与解绑❌ 不要强制用户绑定手机号(影响转化)

最后一点:第三方登录是提升体验的利器,但背后要有安全 + 风控 + 用户体系的支撑。

🎤 面试官:听说过单点登录吗?能讲讲什么是 SSO 吗?

面试者: 有的,我不仅听说过,还在项目中实际设计和实现过单点登录系统。

SSO(Single Sign-On),中文叫“单点登录”,它的核心理念是:

用户只需要登录一次,就可以访问多个相互信任的系统或应用,无需重复登录。

✅ 举个例子:

假设我们公司有三个系统:

  • oa.company.com(办公系统)
  • crm.company.com(客户管理系统)
  • mail.company.com(邮箱系统)

如果每个系统都要单独登录,用户体验很差。

而用了 SSO 之后:

  1. 用户访问 OA,跳转到统一登录页;
  2. 输入一次账号密码,登录成功;
  3. 再去访问 CRM 或 邮箱,自动登录,无需再输密码

这就是 SSO 的价值:提升体验、降低密码疲劳、统一安全管理


🎤 面试官:那 SSO 的实现原理是什么?比如用户第一次登录,流程是怎样的?

面试者: 我以最典型的 基于 Cookie + 重定向 的 SSO 流程为例,比如企业内部系统常用的方案。

假设:

  • SSO Server:sso.company.com
  • 应用 A:oa.company.com
  • 应用 B:crm.company.com

🔁 流程如下(用户第一次访问 OA):

  1. 用户访问 oa.company.com

  2. OA 发现未登录,重定向到 SSO Server:https://sso.company.com/login?redirect_uri=https://oa.company.com/callback

  3. 用户在 SSO 页面输入账号密码,提交;

  4. SSO Server 验证通过,创建全局会话(Global Session),比如:

    java
    redis.set("sso:ticket:TGT-123", "user_id=1001", 有效期30分钟);
  5. SSO Server 给浏览器种一个 SSO Cookie(域:.company.com),值为 TGT-123

  6. 同时生成一个一次性token(叫 ST,Service Ticket),重定向回 OA:

    url
    https://oa.company.com/callback?st=ST-456
  7. OA 后端收到st=ST-456,拿着它去 SSO Server 验证:

    http
    POST https://sso.company.com/verify
    { "ticket": "ST-456", "service": "https://oa.company.com" }
  8. SSO Server 验证 ST 合法,返回用户信息;

  9. OA 创建自己的本地会话(如 JWT),登录完成。

✅ 关键点:SSO Server 负责认证,应用系统负责自己的授权。


🎤 面试官:那用户再去访问 CRM,为什么不用再登录?

面试者: 因为用户浏览器里已经有 SSO Cookie 了!

流程如下:

  1. 用户访问 crm.company.com

  2. CRM 发现本地未登录,重定向到 SSO Server:

    https://sso.company.com/login?redirect_uri=https://crm.company.com/callback
  3. SSO Server 收到请求,检查浏览器是否带有 .company.com 域的 SSO Cookie;

  4. 如果有,说明用户已在其他系统登录过

  5. SSO Server 不再要求输密码,直接生成一个新的 ST(如 ST-789),重定向回 CRM;

  6. CRM 拿 ST 去验证,拿到用户信息,创建本地会话,登录成功。

🔑 核心:SSO Cookie 是跨应用的身份凭证,只要它有效,用户就在“已登录状态”。


🎤 面试官:如果用户退出 OA,其他系统也退出吗?

面试者: 这要看我们实现的是 单点退出(Single Logout) 还是 局部退出

✅ 理想情况:支持单点退出

  1. 用户在 OA 点“退出登录”;

  2. OA 删除自己的本地会话;

  3. 同时重定向到 SSO Server 的 logout 接口:

    https://sso.company.com/logout?redirect_uri=https://oa.company.com
  4. SSO Server 删除全局会话(TGT-123),并清除 SSO Cookie;

  5. SSO Server 返回一个“退出通知页面”,页面中包含对其他已登录应用的隐藏 iframe 注销请求;

    html
    <iframe src="https://crm.company.com/logout-sso"></iframe>
    <iframe src="https://mail.company.com/logout-sso"></iframe>
  6. 每个应用收到请求后,删除自己的本地会话。

⚠️ 注意:单点退出实现复杂,因为要通知所有应用,可能有失败情况。


🎤 面试官:SSO 有哪些常见的实现方案?

面试者: 常见的有三种:

方案说明适用场景
自研 SSO基于 Cookie + Token,自己实现登录中心中小公司、内部系统
OAuth 2.0 / OIDC使用标准协议,如 Keycloak、Auth0多租户、开放平台
SAML企业级标准,XML 格式,较重传统企业、政府系统

我们项目中用的是 自研 + JWT 的方式,轻量、可控。


🎤 面试官:SSO 有什么安全风险?

面试者: 有,主要风险包括:

🔴 1. CSRF 攻击

  • 黑客诱导用户访问恶意页面,自动发起 SSO 登录或退出
  • ✅ 防范:使用 state 参数 + 同源检测

🔴 2. Ticket 被盗用

  • ST 是一次性 token,但如果被截获,在有效期内可能被重放
  • ✅ 防范:HTTPS + 短有效期 + 服务端验证 referer
  • 如果 .company.com 域被 XSS 攻击,Cookie 可能泄露
  • ✅ 防范:设置 HttpOnlySecureSameSite=Strict

🔴 4. 跨域问题

  • 如果应用不在同一个主域下(如 a.comb.com),Cookie 无法共享
  • ✅ 解决方案:
    • 使用 OAuth 2.0(基于重定向传 token)
    • 或通过 前端中转(登录页嵌 iframe + postMessage)

🎤 面试官:总结一下,什么时候该用 SSO?

面试者: 我总结为以下几种场景适合上 SSO:

适合上 SSO 的场景

  • 公司有多个内部系统(OA、CRM、ERP)
  • 用户是同一群体(如员工、合作伙伴)
  • 希望统一账号管理、统一安全策略
  • 有权限中心、审计中心需求

不需要 SSO 的场景

  • 只有一个应用
  • 系统之间完全独立,用户群体不同
  • 安全要求不高,或不想引入复杂性