• Spring Security:是你 Java 后端里“谁能访问什么接口”的门卫系统(认证 + 授权)。
  • JWT:是一种“证明你是谁”的通行证格式(token),常用于前后端分离、无 session 的登录态。

下面按“前后端怎么调用”的顺序,把整套链路讲清楚,并给一个最小可用例子。


1. 先分清两个概念:认证 vs 授权

  • 认证(Authentication):你是谁?有没有登录?
    例:用户名密码正确、token 是真的 → 认证通过。
  • 授权(Authorization):你能干什么?有没有权限?
    例:你是 ADMIN 才能访问 /admin/**

Spring Security 负责这两件事。JWT 通常只负责携带身份信息,让后端不需要保存 session。


2. “不用 JWT”的传统方式(理解对比)

传统网页登录:

  1. 前端发 POST /login(用户名密码)
  2. 后端验证通过后,在服务器内存/Redis 保存 session
  3. 后端给浏览器一个 JSESSIONID cookie
  4. 之后每次请求浏览器自动带 cookie,后端用 session 查你是谁

缺点:服务器要保存登录状态(有状态),分布式扩容要共享 session。


3. “用 JWT”的方式(前后端分离最常见)

JWT 的核心是:登录态不存服务器,存到 token 里,由前端每次带回来

3.1 调用流程(非常重要)

第一步:登录拿 token

前端:

  • POST /api/login,body 里带用户名密码

后端:

  • 验证账号密码 OK
  • 生成 JWT(里面写入 userId、username、roles、过期时间 exp)
  • 返回给前端:{ "token": "xxxxx.yyyyy.zzzzz" }

前端:

  • 把 token 存在内存/本地存储(localStorage)或 cookie(看安全策略)
第二步:访问业务接口时带 token

前端请求业务接口时在请求头加:

Authorization: Bearer <token>

后端(Spring Security)每次请求都会经过一堆 Filter:

  • 其中你写的 JWT Filter 会:
    1. 从请求头取 token
    2. 验证签名是否正确、是否过期
    3. 解析出用户信息和角色
    4. 把“当前用户是谁”塞进 Spring Security 的上下文(SecurityContext)

接着 Spring Security 才能做授权判断:

  • 例如访问 /admin/** 需要 ROLE_ADMIN
  • 如果 token 里没有这个角色 → 403 Access Denied
  • 如果压根没 token 或 token 无效 → 401 Unauthorized

4. Spring Security 在 JWT 模式下各组件做什么

你可以把它想成一个“安检通道”:

  1. JwtAuthenticationFilter(你们自己写的)
    负责“验票(token)”:

    • 没票/假票/过期票:不要把用户放进上下文
    • 真票:创建 Authentication(里面含用户名、角色)并放入上下文
  2. AuthenticationEntryPoint(没登录/没带 token 时触发)
    返回 401,例如:

    {"code":401,"msg":"Unauthorized"}

  3. AccessDeniedHandler(登录了但没权限时触发)
    返回 403,例如:

    {"code":403,"msg":"Forbidden"}
  4. Authorization(authorizeHttpRequests)配置
    决定哪些 URL 需要登录、哪些需要某角色。


5. 一个“最小例子”把它串起来(可照着理解)

假设我们有三个接口:

  • POST /api/login:放行(permitAll)
  • GET /api/user/me:登录后可访问(authenticated)
  • GET /api/admin/ping:必须 ADMIN 角色(hasRole("ADMIN"))

5.1 Security 配置(核心思想版)

@Bean
SecurityFilterChain security(HttpSecurity http) throws Exception {
    return http
        .csrf(csrf -> csrf.disable())
        .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // JWT 必配:无 session
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/login", "/error").permitAll()
            .requestMatchers("/api/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated()
        )
        .exceptionHandling(eh -> eh
            .authenticationEntryPoint((req, resp, ex) -> {
                resp.setStatus(401);
            })
            .accessDeniedHandler((req, resp, ex) -> {
                resp.setStatus(403);
            })
        )
        .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
        .build();
}

5.2 JWT Filter 做的事(伪代码)

String auth = request.getHeader("Authorization");
if (auth != null && auth.startsWith("Bearer ")) {
    String token = auth.substring(7);
    // 1) 校验 token(签名、过期)
    // 2) 解析 username/roles
    // 3) new Authentication(...) 放到 SecurityContext
}
filterChain.doFilter(request, response);

5.3 前端调用示例

登录

http

POST /api/login Content-Type: application/json {"username":"alice","password":"123456"}

返回:

json

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...."}

带 token 访问

http

GET /api/user/me Authorization: Bearer eyJhbGciOi...

  • token 有效 → 200
  • token 缺失/无效 → 401
  • token 有效但访问 admin 接口无 admin 角色 → 403
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐