Spring Security认证授权深度解析

一 Spring Security简介

Spring Security是Spring生态系统中的一个安全框架,主要用于处理认证(Authentication)和授权(Authorization)。它提供了一套完整的安全解决方案,可以轻松集成到Spring应用中。

二 核心概念

1. 认证(Authentication)

验证用户的身份,确认"你是谁"。例如:用户登录过程。

2. 授权(Authorization)

验证用户是否有权限执行某个操作,确认"你能做什么"。例如:检查用户是否有权访问某个API。

3. 主要组件

  • SecurityContextHolder:存储安全上下文信息
  • Authentication:存储当前用户的认证信息
  • UserDetails:用户信息的核心接口
  • UserDetailsService:加载用户信息的核心接口
  • AuthenticationProvider:认证的具体实现者

三 实战案例:基于JWT的认证授权系统

一、核心架构

1. 核心组件关系图

复制代码
请求 → SecurityFilterChain → (多个Security Filter) → 目标资源
                                     ↓
                           SecurityContextHolder
                                     ↓
                              SecurityContext
                                     ↓
                             Authentication
                            /            \
                     Principal     GrantedAuthority

2. 核心组件说明

2.1 SecurityContextHolder
  • 作用:存储当前线程的安全上下文信息
  • 实现:使用ThreadLocal存储SecurityContext
  • 访问方式:
java 复制代码
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
2.2 SecurityContext
  • 作用:持有Authentication对象和其他安全相关信息
  • 生命周期:请求开始到结束
  • 存储位置:ThreadLocal或Session中
2.3 Authentication
  • 作用:存储用户认证信息
  • 主要属性:
    • principal:用户身份信息
    • credentials:凭证信息(如密码)
    • authorities:用户权限集合
    • authenticated:是否已认证
  • 常用实现:UsernamePasswordAuthenticationToken

二、认证流程详解

1. 完整认证流程图

复制代码
用户请求登录
     ↓
UsernamePasswordAuthenticationFilter
     ↓
AuthenticationManager
     ↓
AuthenticationProvider
     ↓
UserDetailsService
     ↓
UserDetails
     ↓
Authentication对象
     ↓
SecurityContext

2. 详细流程说明

2.1 认证入口(以登录为例)
java 复制代码
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
    // 1. 创建未认证的Authentication
    Authentication authentication = new UsernamePasswordAuthenticationToken(
        loginRequest.getUsername(),
        loginRequest.getPassword()
    );

    // 2. 执行认证
    Authentication authenticated = authenticationManager.authenticate(authentication);

    // 3. 认证成功,生成JWT
    SecurityContextHolder.getContext().setAuthentication(authenticated);
    String jwt = tokenProvider.generateToken(authenticated);

    return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
2.2 认证管理器(AuthenticationManager)
java 复制代码
public class ProviderManager implements AuthenticationManager {
    private List<AuthenticationProvider> providers;

    @Override
    public Authentication authenticate(Authentication authentication) {
        // 遍历所有Provider尝试认证
        for (AuthenticationProvider provider : providers) {
            if (!provider.supports(authentication.getClass())) {
                continue;
            }
            
            try {
                return provider.authenticate(authentication);
            } catch (AuthenticationException e) {
                // 处理认证异常
            }
        }
        throw new AuthenticationException("无法认证");
    }
}
2.3 自定义认证提供者
java 复制代码
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    private final CustomUserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) {
        // 1. 获取认证信息
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        // 2. 加载用户信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        // 3. 验证密码
        if (!passwordEncoder.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("密码错误");
        }

        // 4. 创建已认证的Authentication
        return new UsernamePasswordAuthenticationToken(
            userDetails,
            null,
            userDetails.getAuthorities()
        );
    }
}

三、授权流程详解

1. 授权流程图

复制代码
请求 → FilterSecurityInterceptor
         ↓
    SecurityContextHolder获取Authentication
         ↓
    AccessDecisionManager
         ↓
    AccessDecisionVoter
         ↓
    权限判断结果

2. 详细授权步骤

2.1 配置安全规则
java 复制代码
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) {
        http.authorizeRequests()
            // 1. URL级别的权限控制
            .antMatchers("/api/public/**").permitAll()
            .antMatchers("/api/admin/**").hasRole("ADMIN")
            // 2. 自定义权限判断
            .anyRequest().access("@customSecurityService.hasPermission(request,authentication)");
    }
}
2.2 方法级别权限控制
java 复制代码
@Service
public class UserService {
    // 使用Spring EL表达式进行权限控制
    @PreAuthorize("hasRole('ADMIN') or #username == authentication.name")
    public UserDetails getUser(String username) {
        // 方法实现
    }
}

四、JWT集成原理

1. JWT认证流程

复制代码
请求 → JwtAuthenticationFilter
         ↓
    提取JWT令牌
         ↓
    验证JWT有效性
         ↓
    解析用户信息
         ↓
    创建Authentication
         ↓
    存入SecurityContext

2. JWT过滤器实现

java 复制代码
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                  HttpServletResponse response,
                                  FilterChain filterChain) {
        try {
            // 1. 从请求中提取JWT
            String jwt = getJwtFromRequest(request);

            if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
                // 2. 从JWT中获取用户信息
                String username = tokenProvider.getUsernameFromJWT(jwt);
                String roles = tokenProvider.getRolesFromJWT(jwt);

                // 3. 创建Authentication
                List<GrantedAuthority> authorities = Arrays.stream(roles.split(","))
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());

                UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(username, null, authorities);

                // 4. 设置认证信息
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            logger.error("无法设置用户认证", ex);
        }

        filterChain.doFilter(request, response);
    }
}

五、数据校验流程

1. 请求数据校验

java 复制代码
@PostMapping("/login")
public ResponseEntity<?> login(@Valid @RequestBody LoginRequest request) {
    // 1. @Valid触发数据校验
    // 2. 校验失败抛出MethodArgumentNotValidException
}

public class LoginRequest {
    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "密码不能为空")
    private String password;
}

2. 认证数据校验

java 复制代码
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) {
        // 1. 检查用户是否存在
        UserPrincipal user = userMap.get(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        // 2. 检查用户状态
        if (!user.isEnabled()) {
            throw new DisabledException("用户已禁用");
        }

        // 3. 检查账户是否过期
        if (!user.isAccountNonExpired()) {
            throw new AccountExpiredException("账户已过期");
        }

        // 4. 检查账户是否锁定
        if (!user.isAccountNonLocked()) {
            throw new LockedException("账户已锁定");
        }

        return user;
    }
}

3. JWT数据校验

java 复制代码
public class JwtTokenProvider {
    public boolean validateToken(String authToken) {
        try {
            // 1. 验证签名
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
            
            // 2. 验证是否过期
            Claims claims = getClaimsFromJWT(authToken);
            return !claims.getExpiration().before(new Date());
            
        } catch (SignatureException ex) {
            logger.error("无效的JWT签名");
        } catch (MalformedJwtException ex) {
            logger.error("无效的JWT令牌");
        } catch (ExpiredJwtException ex) {
            logger.error("JWT令牌已过期");
        } catch (UnsupportedJwtException ex) {
            logger.error("不支持的JWT令牌");
        } catch (IllegalArgumentException ex) {
            logger.error("JWT声明为空");
        }
        return false;
    }
}

六、异常处理流程

1. 认证异常处理

java 复制代码
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request,
                        HttpServletResponse response,
                        AuthenticationException e) throws IOException {
        // 1. 未认证异常处理
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        
        String message = "请先进行认证";
        if (e instanceof BadCredentialsException) {
            message = "用户名或密码错误";
        } else if (e instanceof JwtExpiredTokenException) {
            message = "token已过期";
        }
        
        response.getWriter().write(
            new ObjectMapper().writeValueAsString(
                new ApiResponse(false, message)
            )
        );
    }
}

2. 授权异常处理

java 复制代码
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request,
                      HttpServletResponse response,
                      AccessDeniedException e) throws IOException {
        // 1. 权限不足异常处理
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        
        response.getWriter().write(
            new ObjectMapper().writeValueAsString(
                new ApiResponse(false, "没有足够的权限")
            )
        );
    }
}

七、安全上下文传递

1. 异步方法中的安全上下文

java 复制代码
@Async
public CompletableFuture<String> asyncMethod() {
    // 1. 获取当前安全上下文
    SecurityContext context = SecurityContextHolder.getContext();
    
    return CompletableFuture.supplyAsync(() -> {
        try {
            // 2. 设置安全上下文到新线程
            SecurityContextHolder.setContext(context);
            // 3. 执行业务逻辑
            return "success";
        } finally {
            // 4. 清理安全上下文
            SecurityContextHolder.clearContext();
        }
    });
}
相关推荐
跟着珅聪学java41 分钟前
spring boot +Elment UI 上传文件教程
java·spring boot·后端·ui·elementui·vue
我命由我123451 小时前
Spring Boot 自定义日志打印(日志级别、logback-spring.xml 文件、自定义日志打印解读)
java·开发语言·jvm·spring boot·spring·java-ee·logback
战族狼魂4 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
杉之6 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
用键盘当武器的秋刀鱼8 小时前
springBoot统一响应类型3.5.1版本
java·spring boot·后端
canonical_entropy8 小时前
Nop入门-如何通过配置扩展服务函数的返回对象
spring·mvc·graphql
小李同学_LHY8 小时前
三.微服务架构中的精妙设计:服务注册/服务发现-Eureka
java·spring boot·spring·springcloud
非ban必选9 小时前
spring-ai-alibaba第四章阿里dashscope集成百度翻译tool
java·人工智能·spring
非ban必选9 小时前
spring-ai-alibaba第五章阿里dashscope集成mcp远程天气查询tools
java·后端·spring
爱喝醋的雷达10 小时前
Spring SpringBoot 细节总结
java·spring boot·spring