Spring Security 鉴权流程与过滤器链深度剖析

一、login接口鉴权流程

1.1 流程概述

login接口是用户认证入口,核心是验证用户名密码并生成JWT Token。流程涉及控制器认证管理器用户服务密码编码器JWT工具过滤器协同工作。

1.2 详细步骤与代码示例

1.2.1 请求接收(Controller层接口)

组件标注:表现层接口(AuthController.login())

java 复制代码
@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层)

自定义用户服务实现

java 复制代码
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
    private final UserMapper userMapper;
    private final RoleMapper roleMapper;

    @Override
    public 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):

java 复制代码
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层)

配置类代码

java 复制代码
@Configuration
public class SecurityConfig {
    @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
}

密码对比源码核心逻辑(DaoAuthenticationProvider):

java 复制代码
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源码核心逻辑

java 复制代码
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工具类代码

java 复制代码
@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)

自定义过滤器代码

java 复制代码
@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接口执行流程图

graph TD A[前端发起登录请求\nPOST /api/auth/login] --> B[AuthController.login] B --> C[AuthenticationManager.authenticate] C --> D[DaoAuthenticationProvider.authenticate] D --> E[UserDetailsServiceImpl.loadUserByUsername] E --> F[UserMapper.selectOne\n查询用户基础信息] E --> G[RoleMapper.findRolesByUserId\n加载角色权限] D --> H[additionalAuthenticationChecks\n密码校验] H --> I[BCryptPasswordEncoder.matches\n比对密码] D --> J[生成已认证凭证\nUsernamePasswordAuthenticationToken] J --> K[JwtUtils.generateToken\n生成JWT Token] K --> L[返回Token给前端]

二、@PreAuthorize接口鉴权流程

2.1 流程概述

@PreAuthorize是方法级权限控制注解,核心是在方法执行前校验用户权限。流程涉及AOP拦截权限解析授权决策三个阶段。

2.2 详细步骤与代码示例

2.2.1 控制器接口标注@PreAuthorize(表现层)

java 复制代码
@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拦截与权限表达式解析(插件层)

配置类代码

java 复制代码
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override protected MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler h = new DefaultMethodSecurityExpressionHandler();
        h.setPermissionEvaluator(new CustomPermissionEvaluator());
        return h;
    }
}

MethodSecurityInterceptor源码核心逻辑

java 复制代码
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层)

自定义权限检查器

java 复制代码
@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):

java 复制代码
public class SecurityExpressionRoot {
    public boolean hasAuthority(String auth) {
        return authentication.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals(auth));
    }
}

授权决策管理器源码核心逻辑(AffirmativeBased):

java 复制代码
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层)

java 复制代码
@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接口执行流程图

graph TD A[前端携带Token请求GET /api/order] --> B[JwtAuthFilter.doFilterInternal] B --> C[提取Token并验证] C --> D[设置SecurityContextUsernamePasswordAuthenticationToken] D --> E[DispatcherServlet分发请求] E --> F[OrderController.listOrders@PreAuthorize标注方法] F --> G[MethodSecurityInterceptor.invokeAOP拦截] G --> H[attributeSource.getAttributes获取权限表达式] H --> I[accessDecisionManager.decide授权决策] I --> J[WebExpressionVoter.vote表达式投票] J --> K[SecurityExpressionRoot.hasAuthority解析权限逻辑] K --> L[PermissionChecker.hasPermission校验权限] L --> M[OrderServiceImpl.queryOrders执行业务逻辑] M --> N[返回数据给前端]

三、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在认证与授权中的底层逻辑,以及自定义组件与源码组件的协作方式。

相关推荐
佛祖让我来巡山7 小时前
大型项目基于Spring Security的登录鉴权与数据权限控制完整方案
springsecurity·保姆级鉴权·大型项目登录认证
佛祖让我来巡山8 小时前
Spring Security前后端分离接入流程保姆级教程
权限校验·springsecurity·登录认证
佛祖让我来巡山1 天前
Spring Security 认证流程闭环与调用链路详解
springsecurity·authenticationmanager
佛祖让我来巡山1 天前
小明的Spring Security入门到深入实战
springsecurity
佛祖让我来巡山2 天前
⚠️登录认证功能的成长过程:整体概述
安全·登录·springsecurity·登录认证·认证授权
码熔burning3 个月前
Spring Security 深度学习(六): RESTful API 安全与 JWT
安全·spring·restful·springsecurity
A尘埃3 个月前
SpringSecurity版本的不同配置
认证·springsecurity·安全配置·不同版本
世纪摆渡人4 个月前
SpringSecurity-SpringSecurity入门介绍
springsecurity
1.01^10004 个月前
[7-01-01].第01章:安全与权限 - 核心概念
springsecurity