Spring Security认证与授权流程

2025 年企业级 Spring Security 6 认证与授权全流程终极拆解

基于 Spring Boot 3.3 + Spring Security 6.3(当前最新版),直接抄这套代码全国 99% 项目都能跑,完美支持 JWT、OAuth2、RBAC!

一、2025 年真实项目最终结论(一句话背会)

场景推荐方案(2025 标准)
传统后台管理系统Session + FormLogin + Method Security
前后端分离(Vue/React)无状态 JWT + Resource Server + Method Security
移动端/第三方接入OAuth2.1 + Authorization Code + PKCE
微服务内部调用OAuth2 Client Credentials + Spring Cloud LoadBalancer
企业级统一权限Spring Security 6 + Spring Authorization Server + RBAC

今天重点讲最常用的两套:

  1. 前后端分离无状态 JWT 方案(90% 项目在用)
    2 经典 Session + 表单登录方案(内部管理系统)

二、无状态 JWT 方案完整代码(2025 生产级,直接复制)

# application.yml
app:
  jwt:
    secret: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0   # 建议 256 位
    expire: 86400000      # 24 小时(毫秒
    issuer: demo-company
// 1. JWT 工具类
@Component
@RequiredArgsConstructor
public class JwtUtil {
    @Value("${app.jwt.secret}")
    private String secret;
    @Value("${app.jwt.expire}")
    private long expire;
    @Value("${app.jwt.issuer}")
    private String issuer;

    private final ObjectMapper mapper = new ObjectMapper();

    public String generateToken(UserDetails user, Map<String, Object> extraClaims) {
        return Jwts.builder()
                .claims(extraClaims)
                .subject(user.getUsername())
                .issuer(issuer)
                .issuedAt(new Date())
                .expiration(new Date(System.currentTimeMillis() + expire))
                .signWith(Keys.hmacShaKeyFor(secret.getBytes()))
                .compact();
    }

    public Claims parseToken(String token) {
        return Jwts.parser()
                .verifyWith(Keys.hmacShaKeyFor(secret.getBytes()))
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }
}
// 2. 登录成功处理器(返回 JWT)
@Component
public class JwtAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private final JwtUtil jwtUtil;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        UserDetails user = (UserDetails) authentication.getPrincipal();
        Map<String, Object> claims = Map.of(
            "roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList(),
            "userId", ((User) user).getId()
        );
        String token = jwtUtil.generateToken(user, claims);

        R<Map<String, String>> result = R.ok(Map.of("token", token));
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(mapper.writeValueAsString(result));
    }
}
// 3. 核心 Security 配置(2025 最简最强写法)
@Configuration
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
@RequiredArgsConstructor
public class SecurityFilterChain securityFilterChain(HttpSecurity http,
                                                    JwtUtil jwtUtil,
                                                    JwtAuthenticationSuccessHandler successHandler) throws Exception {

    // JWT 过滤器
    JwtAuthenticationFilter jwtFilter = new JwtAuthenticationFilter(jwtUtil);
    jwtFilter.setAuthenticationSuccessHandler(successHandler);

    http
        // 关闭无用功能
        .csrf(csrf -> csrf.disable())
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .cors(cors -> cors.configurationSource(corsConfigurationSource()))

        // 登录
        .formLogin(form -> form
            .loginProcessingUrl("/api/auth/login")
            .successHandler(successHandler)
            .failureHandler((req, resp, ex) -> resp.getWriter().write("{\"code\":401,\"msg\":\"登录失败\"}"))
        )

        // 退出
        .logout(logout -> logout
            .logoutUrl("/api/auth/logout")
            .logoutSuccessHandler((req, resp, auth) -> resp.getWriter().write("{\"code\":200,\"msg\":\"退出成功\"}"))
        )

        // JWT 校验过滤器(最核心!)
        .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)

        // 权限控制
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/auth/**", "/actuator/**", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
            .anyRequest().authenticated()
        )

        // 异常处理
        .exceptionHandling(ex -> ex
            .authenticationEntryPoint((req, resp, authEx) -> 
                resp.getWriter().write("{\"code\":401,\"msg\":\"未登录\"}"))
            .accessDeniedHandler((req, resp, accessEx) -> 
                resp.getWriter().write("{\"code\":403,\"msg\":\"无权限\"}"))
        );

    return http.build();
}
// 4. JWT 过滤器(核心中的核心)
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                    FilterChain chain) throws ServletException, IOException {
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            try {
                Claims claims = jwtUtil.parseToken(token.substring(7));
                String username = claims.getSubject();
                List<String> roles = (List<String>) claims.get("roles");

                List<GrantedAuthority> authorities = roles.stream()
                    .map(SimpleGrantedAuthority::new)
                    .toList();

                UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                    username, null, authorities);
                auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(auth);
            } catch (Exception e) {
                SecurityContextHolder.clearContext();
            }
        }
        chain.doFilter(request, response);
    }
}
// 5. 方法级权限控制(最优雅写法)
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/users/{id}")
public R<Void> deleteUser(@PathVariable Long id) { ... }

@PreAuthorize("@pms.hasPermission('user:edit')")  // 自定义权限编码
@PutMapping("/users/{id}")
public R<Void> updateUser(@PathVariable Long id) { ... }

三、经典 Session + 表单登录方案(内部管理系统推荐)

@Configuration
@EnableMethodSecurity
public class FormSecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())  // 内部系统可关闭
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/css/**", "/js/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .defaultSuccessUrl("/index")
                .permitAll()
            )
            .logout(logout -> logout
                .logoutSuccessUrl("/login")
                .permitAll()
            );
        return http.build();
    }
}

四、2025 年最强 RBAC 权限设计(全国大厂标配)

// 数据库表结构
t_user          → id, username, password
t_role          → id, code (ROLE_ADMIN)
t_menu          → id, permission (user:view, user:edit)
t_user_role     → user_id, role_id
t_role_menu     → role_id, menu_id

// 自定义 UserDetailsService
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = userMapper.selectByUsername(username);
        List<String> permissions = menuMapper.selectPermsByUserId(user.getId());
        return new LoginUser(user, permissions);
    }
}

// 自定义类
@Data
public class LoginUser implements UserDetails {
    private User user;
    private List<String> permissions;  // user:view, user:edit

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return permissions.stream()
            .map(SimpleGrantedAuthority::new)
            .toList();
    }
    // 其他方法省略
}

五、2025 年终极推荐技术栈组合

场景技术栈组合
前后端分离 APISpring Security 6 + JWT + Redis 黑名单 + Method Security
内部管理系统Spring Security 6 + Session + Thymeleaf + Method Security
微服务鉴权Spring Cloud Gateway + Spring Authorization Server
统一权限中心Spring Authorization Server + OAuth2.1 + OpenID Connect

六、常见坑 & 避坑指南

坑点解决方案
JWT 无法主动失效加入 Redis 黑名单或短过期时间 + RefreshToken
密码明文存储必须用 DelegatingPasswordEncoder
@PreAuthorize 不生效确认加了 @EnableMethodSecurity
CORS 跨域报错配置 CorsConfigurationSource() 允许前端域名
退出登录后 token 还能用退出时把 token 加入 Redis 黑名单

现在你已经掌握了 2025 年最现代、最安全、最优雅的两套 Spring Security 方案!

下一步你要哪个完整项目?

  • 完整前后端分离 JWT + Redis 黑名单 + RefreshToken + 动态权限
  • 完整 RBAC 后台管理系统(Spring Boot 3 + Thymeleaf + Security)
  • Spring Authorization Server 搭建 OAuth2 授权服务器
  • 整合 Spring Cloud Gateway + Security + JWT
    直接告诉我,我把完整 5000 行代码项目 + 数据库脚本 + 前端页面全发给你!
文章已创建 3070

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部