一、login接口鉴权流程
1.1 流程概述
login接口是用户认证入口,核心是验证用户名密码并生成JWT Token。流程涉及控制器、认证管理器、用户服务、密码编码器、JWT工具和过滤器协同工作。
1.2 详细步骤与代码示例
1.2.1 请求接收(Controller层接口)
组件标注:表现层接口(AuthController.login())
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {private final AuthenticationManager authenticationManager;private final JwtUtils jwtUtils;@PostMapping("/login")public Result<JwtResponse> login(@RequestBody LoginRequest request) {Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));UserDetails userDetails = (UserDetails) authentication.getPrincipal();String token = jwtUtils.generateToken(userDetails);return Result.success(new JwtResponse(token, userDetails.getUsername()));}
}@Data class LoginRequest { private String username; private String password; }
@Data class JwtResponse { private String token; private String username; public JwtResponse(String t, String u) { token=t; username=u; } }
1.2.2 触发认证与加载用户信息(Service层)
自定义用户服务实现:
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {private final UserMapper userMapper;private final RoleMapper roleMapper;@Overridepublic UserDetails loadUserByUsername(String username) {UserPo user = userMapper.selectOne(new QueryWrapper<UserPo>().eq("username", username));if (user == null) throw new UsernameNotFoundException("用户不存在");Set<RolePo> roles = roleMapper.findRolesByUserId(user.getId());user.setRoles(roles);return user;}
}
Spring Security认证管理器源码核心逻辑(ProviderManager):
public class ProviderManager implements AuthenticationManager {private List<AuthenticationProvider> providers;public Authentication authenticate(Authentication auth) {for (AuthenticationProvider p : providers) {if (p.supports(auth.getClass())) {Authentication result = p.authenticate(auth);if (result != null) return result;}}throw new AuthenticationException("认证失败") {};}
}
1.2.3 密码校验(Util层)
配置类代码:
@Configuration
public class SecurityConfig {@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
}
密码对比源码核心逻辑(DaoAuthenticationProvider):
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {protected void additionalAuthenticationChecks(UserDetails ud, UsernamePasswordAuthenticationToken auth) {String presented = auth.getCredentials().toString();String encoded = ud.getPassword();if (!passwordEncoder.matches(presented, encoded)) throw new BadCredentialsException("密码错误");}
}
BCryptPasswordEncoder源码核心逻辑:
public class BCryptPasswordEncoder implements PasswordEncoder {public boolean matches(CharSequence raw, String encoded) {BCrypt.HashData hashData = decode(encoded);byte[] hashed = BCrypt.hashpw(raw.toString(), hashData);return constantTimeEquals(hashed, hashData.password);}
}
1.2.4 生成JWT Token(Util层)
JWT工具类代码:
@Component
public class JwtUtils {@Value("${app.jwt.secret}") private String secret;@Value("${app.jwt.expiration}") private long expiration;public String generateToken(UserDetails ud) {return Jwts.builder().setSubject(ud.getUsername()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + expiration)).signWith(SignatureAlgorithm.HS256, secret).compact();}
}
1.2.5 后续请求认证(插件层:Filter)
自定义过滤器代码:
@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {private final JwtUtils jwtUtils;private final UserDetailsServiceImpl userDetailsService;@Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {String token = parseJwt(req);if (token != null && jwtUtils.validateToken(token)) {String username = jwtUtils.extractUsername(token);UserDetails ud = userDetailsService.loadUserByUsername(username);UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(ud, null, ud.getAuthorities());SecurityContextHolder.getContext().setAuthentication(auth);}chain.doFilter(req, res);}private String parseJwt(HttpServletRequest req) {String h = req.getHeader("Authorization");return (h != null && h.startsWith("Bearer ")) ? h.substring(7) : null;}
}
1.3 login接口执行流程图
二、@PreAuthorize接口鉴权流程
2.1 流程概述
@PreAuthorize是方法级权限控制注解,核心是在方法执行前校验用户权限。流程涉及AOP拦截、权限解析、授权决策三个阶段。
2.2 详细步骤与代码示例
2.2.1 控制器接口标注@PreAuthorize(表现层)
@RestController
@RequestMapping("/api/order")
@RequiredArgsConstructor
public class OrderController {private final OrderService orderService;@GetMapping@PreAuthorize("hasAuthority('order:view')")public PageResult<OrderVo> listOrders(OrderQuery query) {return orderService.queryOrders(query);}
}
2.2.2 AOP拦截与权限表达式解析(插件层)
配置类代码:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {@Override protected MethodSecurityExpressionHandler createExpressionHandler() {DefaultMethodSecurityExpressionHandler h = new DefaultMethodSecurityExpressionHandler();h.setPermissionEvaluator(new CustomPermissionEvaluator());return h;}
}
MethodSecurityInterceptor源码核心逻辑:
public class MethodSecurityInterceptor implements MethodInterceptor {public Object invoke(MethodInvocation mi) {Collection<ConfigAttribute> attrs = attributeSource.getAttributes(mi);if (attrs == null) return mi.proceed();Authentication auth = SecurityContextHolder.getContext().getAuthentication();accessDecisionManager.decide(auth, mi, attrs);return mi.proceed();}
}
2.2.3 权限校验逻辑(Service层)
自定义权限检查器:
@Component
public class PermissionChecker {public boolean hasPermission(String code) {Authentication auth = SecurityContextHolder.getContext().getAuthentication();return auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals(code));}
}
表达式解析源码核心逻辑(SecurityExpressionRoot):
public class SecurityExpressionRoot {public boolean hasAuthority(String auth) {return authentication.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals(auth));}
}
授权决策管理器源码核心逻辑(AffirmativeBased):
public class AffirmativeBased implements AccessDecisionManager {public void decide(Authentication auth, Object obj, Collection<ConfigAttribute> attrs) {for (AccessDecisionVoter v : decisionVoters) {int r = v.vote(auth, obj, attrs);if (r == ACCESS_GRANTED) return;}throw new AccessDeniedException("权限不足");}
}
2.2.4 业务逻辑执行(Service层)
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {private final OrderMapper orderMapper;private final DataScopeService dataScopeService;public PageResult<OrderVo> queryOrders(OrderQuery q) {DataScopeService.DataScope scope = dataScopeService.getCurUserDataScope();LambdaQueryWrapper<OrderPo> w = new LambdaQueryWrapper<>();if (scope.getScopeType() == 1) w.eq(OrderPo::getCreatorId, scope.getUserId());else if (scope.getScopeType() == 2) w.eq(OrderPo::getDeptId, scope.getDeptIds().get(0));Page<OrderPo> p = orderMapper.selectPage(new Page<>(q.getPageNum(), q.getPageSize()), w);return convertToPageResult(p);}
}
2.3 @PreAuthorize接口执行流程图
三、Spring Security过滤器链详解
3.1 过滤器执行顺序与功能
| 顺序 | 过滤器名称 | 功能描述 | 使用场景 |
|---|---|---|---|
| 1 | SecurityContextPersistenceFilter | 恢复或清理SecurityContext,隔离请求间状态。 | 所有请求必经,前后端分离可简化。 |
| 2 | LogoutFilter | 处理退出请求,清理认证信息。 | 需显式退出功能时启用。 |
| 3 | UsernamePasswordAuthenticationFilter | 处理传统用户名密码登录请求。 | 前后端分离通常替换为自定义登录接口。 |
| 4 | JwtAuthFilter | 自定义过滤器,提取Bearer Token并设置认证信息。 | 前后端分离核心过滤器,手动配置。 |
| 5 | AnonymousAuthenticationFilter | 为未认证用户分配匿名身份。 | 区分未登录与已登录用户。 |
| 6 | ExceptionTranslationFilter | 捕获安全异常并转换为HTTP响应(401/403)。 | 所有异常处理中枢,必配置。 |
| 7 | FilterSecurityInterceptor | URL级权限校验,根据authorizeRequests配置判断访问权限。 | 粗粒度权限控制。 |
3.2 过滤器链配置与自定义
- 配置位置:SecurityConfig中通过HttpSecurity链式调用配置。
- 自定义过滤器插入:使用addFilterBefore/After/At方法,如JwtAuthFilter插入到UsernamePasswordAuthenticationFilter之前。
四、核心流程总结
4.1 login接口核心流程
前端请求→AuthController.login()→AuthenticationManager.authenticate()→
DaoAuthenticationProvider.authenticate()→UserDetailsServiceImpl.loadUserByUsername()→
UserMapper.selectOne()→additionalAuthenticationChecks()→BCryptPasswordEncoder.matches()→
生成UsernamePasswordAuthenticationToken→JwtUtils.generateToken()→返回Token
4.2 @PreAuthorize接口核心流程
前端携带Token请求→JwtAuthFilter.doFilterInternal()→设置SecurityContext→
OrderController.listOrders()→MethodSecurityInterceptor.invoke()→
attributeSource.getAttributes()→accessDecisionManager.decide()→
WebExpressionVoter.vote()→SecurityExpressionRoot.hasAuthority()→
PermissionChecker.hasPermission()→OrderServiceImpl.queryOrders()→返回数据
通过上述流程图与源码剖析,可清晰理解Spring Security在认证与授权中的底层逻辑,以及自定义组件与源码组件的协作方式。