← 返回题目列表

Spring Security 中认证和授权有什么区别?

高频 简单 第 1 / 23 题 更新于 2026/07/25
Spring Security认证授权

简化版

认证(Authentication)解决「你是谁」——确认请求来自哪个用户(校验用户名密码、Token 等)。授权(Authorization)解决「你能做什么」——已确认身份的用户能不能访问某个资源。顺序上先认证后授权:Spring Security 先通过认证得到一个 Authentication 对象(存进 SecurityContext),再基于其中的权限(authorities)+ 角色 + 表达式规则做访问决策。HTTP 语义:未认证返回 401,已认证但权限不足返回 403。核心易错点:「已经登录」不等于「允许操作」——很多越权漏洞就是把认证当成了授权。

详细版

认证 vs 授权对比:

维度认证(Authentication)授权(Authorization)
回答你是谁你能做什么
时机后(依赖认证结果)
手段用户名密码、短信、OAuth2、JWT角色/权限、@PreAuthorize、URL 规则
结果得到 Authentication允许/拒绝访问
HTTP失败 → 401不足 → 403

Authentication 对象的核心字段:

public interface Authentication {
    Object getPrincipal();       // 用户主体(如 UserDetails)
    Object getCredentials();     // 凭证(密码等,认证后常被擦除)
    Collection<? extends GrantedAuthority> getAuthorities();  // 权限集合
    boolean isAuthenticated();   // 是否已认证
}

授权可发生在多层:

// ① URL 授权(接口入口)
http.authorizeHttpRequests(a -> a
    .requestMatchers("/admin/**").hasRole("ADMIN")
    .anyRequest().authenticated());

// ② 方法授权(服务层)
@PreAuthorize("hasAuthority('order:refund')")
public void refund(Long orderId) { ... }

// ③ 数据授权(只能看自己的数据)——需业务查询条件配合

完整版教学

一、先分清两个问题

  • 认证是「身份确认」:证明这个请求属于某个已知用户(张三登录了)。
  • 授权是「权限判断」:这个已知用户能不能做某件事(张三能不能退款)。

关键认知登录成功只证明「请求属于某个主体」,不代表「它能访问所有接口」。很多安全漏洞的根源就是把「已经登录」误当成「允许操作」——比如普通用户登录后,直接调用管理员接口,如果只检查了「是否登录」而没检查「是否有权限」,就发生越权认证是门槛,授权是分权,两者缺一不可。

问题Spring Security 产物失败时 HTTP 语义典型风险
你是谁Authentication / SecurityContext401未登录、Token 无效
你能做什么GrantedAuthority / 授权规则403已登录但越权

这题的记忆钩子是“先认人,再分钱”:认证确认主体,授权决定这个主体能操作哪些资源。

二、Authentication 里有什么

认证成功后,Spring Security 用一个 Authentication 对象表示当前用户,存在 SecurityContext 里,核心字段:

  • principal用户主体(通常是 UserDetails,含用户名、账号状态等)。
  • credentials凭证(如密码)。认证成功后通常被擦除(置空)——避免敏感凭证长期留在内存里被窃取。
  • authorities授予的权限集合GrantedAuthority),授权就靠它。
  • authenticated:是否已认证。

后续任何地方都能通过 SecurityContextHolder.getContext().getAuthentication() 拿到当前用户。

三、认证流程的位置

认证发生在过滤器链里(见「过滤器链」专题):

  1. 请求进入过滤器链,认证过滤器提取凭证(表单的用户名密码、请求头的 Token)。
  2. 交给 AuthenticationManager,它委托给合适的 AuthenticationProvider 校验(如 DaoAuthenticationProvider 查数据库比对密码)。
  3. 校验成功 → 构造已认证的 Authentication写入 SecurityContext
  4. 之后授权组件和业务代码才能知道「当前用户是谁、有什么权限」。

必须先完成认证、写入 SecurityContext,授权才有依据。

请求
  -> 认证过滤器提取凭证
  -> AuthenticationManager 校验
  -> SecurityContext 保存 Authentication
  -> 授权规则读取 authorities 决策

四、授权可以发生在多层

授权不是只在一个地方做,可以分层防御

  • URL 授权(接口入口层):requestMatchers("/admin/**").hasRole("ADMIN")——控制哪些路径需要什么权限。适合粗粒度的入口控制。
  • 方法授权(服务层):@PreAuthorize("hasAuthority('order:refund')")——保护具体的服务方法。即使 URL 层漏了,方法层还能兜住。
  • 数据授权(最细):「只能查看自己部门/自己的数据」——这种不能只靠 URL/方法规则,必须在业务查询里加条件where user_id = 当前用户),因为它依赖具体数据的归属。

多层授权能形成纵深防御,尤其数据级授权是很多越权漏洞的重灾区。

五、401 和 403 的区别(要区分清楚)

  • 401 Unauthorized没有有效认证——没登录、Token 过期/无效。语义是「你需要先登录/重新提交凭证」。
  • 403 Forbidden身份已确认,但权限不足——你登录了,但这个操作你没权限。语义是「你是谁我知道,但你不能做这个」。

API 设计中区分二者很重要:前端拿到 401 应该跳转登录(重新认证),拿到 403 应该提示「无权限」(不用重新登录)。混淆会导致「权限不足却一直弹登录框」之类的糟糕体验,也不利于排查。

六、角色和权限的建模

  • 角色(Role) 通常是权限的集合,粗粒度:ADMINUSERMANAGER
  • 权限(Authority/Permission)具体的动作,细粒度:order:readorder:refunduser:delete

系统越复杂,越应该让授权逻辑面向「权限点」而非「角色名」。原因:如果代码里到处硬编码 hasRole("ADMIN"),将来新增一个「财务」角色也需要退款权限时,要改所有相关代码。而如果代码写 hasAuthority('order:refund'),只要把这个权限点分配给财务角色即可,代码不用动。角色管「分给谁」,权限点管「能做什么」,代码依赖权限点更灵活(这是 RBAC 的核心,见相关专题)。

七、测试授权规则(要测反向场景)

安全测试不能只测「有权限的能访问」,更要测反向场景

  • 未登录访问 → 应 401。
  • 登录但权限不足访问 → 应 403。
  • 权限正确访问 → 成功。
  • 跨租户/跨用户访问(张三尝试访问李四的数据)→ 应被拒绝。

「管理员能访问」测了没用——关键是**「普通用户不能越权」。越权漏洞恰恰藏在「没测反向场景」里。安全测试的重点是证明该拒绝的确实被拒绝了**。

八、常见误区与追问

  • 误区:用户已经登录就可以访问所有接口。 登录只说明身份被确认,是否能访问还要看角色、权限点和数据范围。
  • 误区:认证失败和授权失败都返回同一个状态码即可。 未认证应返回 401,已认证但权限不足应返回 403,前端和排障语义都不同。
  • 误区:只做 URL 授权就能覆盖所有越权。 服务方法可能被多个入口调用,数据级权限还要在业务查询条件里落实。
  • 追问:Authentication 中哪些字段和授权最相关? principal 表示主体,authorities 表示权限集合,授权决策主要依赖这些信息。
  • 追问:为什么复杂系统更推荐判断权限点而不是角色名? 权限点表达具体动作,新增角色时只改角色-权限关系,不需要到处改代码里的 hasRole
  • 追问:数据授权为什么不能只靠 @PreAuthorize 数据归属依赖具体记录,如租户、部门、创建人,必须进入查询条件或业务校验。

九、加强记忆

认证(Authentication)= 确认「你是谁」(用户名密码/短信/OAuth2/JWT,成功得到 Authentication 存入 SecurityContext,凭证认证后擦除);授权(Authorization)= 判断「你能做什么」(基于 authorities/角色/表达式)。先认证后授权——「已登录」≠「允许操作」,混淆导致越权。授权分层:URL 授权(入口)、方法授权(@PreAuthorize)、数据授权(业务查询加条件,最细)401(未认证,需登录)vs 403(已认证但权限不足) 要区分(前端处理不同)。建模:角色是权限集合、权限点是具体动作,复杂系统代码依赖权限点(order:refund)而非角色名,更灵活安全测试必须覆盖反向场景(未登录 401、越权 403、跨用户拒绝)——重点是「该拒绝的被拒绝」。