认证和授权的区别是什么?Spring Security 分别是怎么实现的?
- 标签:Spring Security、认证授权、后端
🏛 业务架构师
一句话区分:认证(Authentication)回答"你是谁",授权(Authorization)回答"你能干什么"。两道关卡先后执行——先验明正身,再核对权限。
Spring Security 的实现分工:
认证:
- 入口:认证过滤器(表单登录、HTTP Basic、Bearer Token 解析等);
- 核心:AuthenticationManager → ProviderManager → AuthenticationProvider(如 DaoAuthenticationProvider 查库+BCrypt 比对密码);
- 产出:Authentication 对象(身份+权限)存 SecurityContextHolder(ThreadLocal);失败 → 401。
授权:
- URL 级:AuthorizationFilter 按配置规则(
/admin/**需要 ADMIN 角色)——粗粒度的入口控制; - 方法级:
@PreAuthorize("hasAuthority('order:delete')")AOP 拦截——细粒度的业务动作控制,支持 SpEL(参数校验:@PreAuthorize("#userId == authentication.name")防水平越权); - 失败 → 403。
业务视角的架构要点:
- 粒度选择:URL 级管"菜单/入口",方法级管"按钮/操作",数据级(行级权限)要在 Service 里显式控制——三层权限模型对应 RBAC 的落地分层;
- 常见事故源:只做了认证没做授权(登录了就能调所有接口)——尤其内部管理端对接外部 API 时忘配规则,默认应该是 deny-all 再按需放开(最小权限原则);
- 越权是授权问题不是认证问题——水平越权(改别人订单)要用方法级+数据级双重防护,这是安全审计的高频项。
安全设计的第一课就是把这两个概念分开建模——混为一谈的系统几乎必然出越权事故。
🏗 系统架构师
认证(Authentication)= 你是谁——验证身份(密码、JWT 签名、OAuth2 授权码、生物特征);授权(Authorization)= 你能干什么——验证权限(角色、权限点、资源归属)。先认证后授权,两阶段有先后依赖。
Spring Security 的实现(两套机制各管一段):
认证侧:
- 入口过滤器:表单(UsernamePasswordAuthenticationFilter)、HTTP Basic、JWT 解析过滤器(自定义 OncePerRequestFilter)、OAuth2 login/resource server;
- 核心:AuthenticationManager → ProviderManager → AuthenticationProvider(DaoAuthenticationProvider 查库验密);
- 产出:Authentication 对象(principal + credentials + authorities)存入 SecurityContextHolder(ThreadLocal)。
授权侧(两个层次):
- URL 级:AuthorizationFilter + authorizeHttpRequests——粗粒度入口控制(
/admin/**要 ADMIN); - 方法级:
@PreAuthorize/@PostAuthorize(AOP 切面)——业务级精细控制(@PreAuthorize("#orderId == authentication.name or hasRole('ADMIN')",SpEL 引用参数做数据级判断)。
架构师视角的分层纪律:
- URL 级做粗防线(挡明显越界),方法级做业务规则(谁能操作哪条数据)——两层都要有:只靠 URL 前缀判断是脆弱的(新端点漏配即裸奔),只靠方法级则攻击面暴露过多;
- 授权判断要基于服务端会话/令牌状态,绝不信任请求参数里的角色字段(客户端可伪造);
- 权限模型演进:URL/方法级硬编码 → RBAC 角色 → 数据级 ABAC/策略引擎(业务复杂后权限外置成策略服务)——权限的复杂度要匹配业务,警惕框架特性诱发的过度设计。
💻 全栈开发专家
认证(Authentication):你是谁——验证身份(用户名密码、JWT、OAuth2、短信码)。 授权(Authorization):你能干什么——验证权限(角色、ACL、资源归属)。
先认证后授权:没有身份就无从谈权限。
Spring Security 的实现分工:
认证侧:
java
http.formLogin(withDefaults()) // 表单
.oauth2ResourceServer(o -> o.jwt(...)) // JWT
// 或自定义 provider:
auth.providerManager(new DaoAuthenticationProvider()...)- 流程:Filter 拦截 → AuthenticationManager/Provider 认证 → 结果放 SecurityContextHolder(ThreadLocal,请求级);
- 密码校验:PasswordEncoder(BCrypt)在 DaoAuthenticationProvider 内完成。
授权侧(三层):
- URL 级(粗粒度):
java
http.authorizeHttpRequests(a -> a
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/order/**").hasAuthority("order:read")
.requestMatchers(HttpMethod.PUT, "/api/**").authenticated()
.anyRequest().permitAll());- 方法级(细粒度,需 @EnableMethodSecurity):
java
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public Order orderOf(Long userId) { ... } // SpEL 可拿参数,防水平越权神器- 领域对象级(ACL/手写):资源属于谁——
@PostAuthorize("returnObject.owner == authentication.name")或业务层判 ownership。
架构上的落点:认证产物 Authentication(含 authorities)是两层的衔接——授权只消费「身份+权限集合」,不关心怎么认证的。实战经验:URL 层做面(拦未登录),方法层做点(业务权限+数据归属),只靠 URL 匹配做授权的系几乎必然出越权漏洞——路径和业务权限对不上太容易了。