Java 后端实现基于 JWT 的用户认证和权限校验(含代码讲解)

在前后端分离的项目中,JWT(JSON Web Token) 是一种非常流行的用户认证机制。相比传统的 Session,JWT 更加轻量、易扩展,特别适合前后端分离、移动端 API 场景。

今天我们基于 Spring Boot + JWT,完整实现一套 登录生成 Token + 认证拦截器校验 Token 的流程,并附完整示例代码,助你轻松上手。


📌 项目背景

  • 技术栈:Spring Boot + MyBatis + JWT

  • 功能目标:

    • 用户登录,生成 Token
    • 后续请求带 Token 自动鉴权
    • 无需 Session,支持跨服务调用

🧩 一、登录接口:生成 JWT Token

typescript 复制代码
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody Map<String, String> loginRequest) {
    String username = loginRequest.get("username");
    String password = loginRequest.get("password");

    // 1. 数据库查用户
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("username", username);
    User user = userMapper.selectOne(queryWrapper);

    if (user == null) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名不存在");
    }

    // 2. 验证密码
    if (!passwordEncoder.matches(password, user.getPassword())) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("密码错误");
    }

    // 3. 登录成功,生成 JWT
    String token = JwtUtils.generateToken(user); // 👈 自定义工具类

    Map<String, String> response = new HashMap<>();
    response.put("token", token);
    response.put("message", "登录成功");
    return ResponseEntity.ok(response);
}
  • 登录成功后,前端拿到 token,后续请求都通过请求头 Authorization: Bearer <token> 携带。

🔑 二、JWT 工具类(JwtUtils)

typescript 复制代码
public class JwtUtils {

    private static final String SECRET_KEY = "your-secret-key";
    private static final long EXPIRATION_TIME = 3600000; // 1 小时

    // 生成 Token
    public static String generateToken(User user) {
        return Jwts.builder()
                .setSubject(user.getUsername())
                .claim("role", user.getRole()) // 可扩展更多字段
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    // 解析 Token
    public static Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
    }
}

🧱 三、JWT 认证过滤器:拦截每个请求校验 Token

java 复制代码
public class JwtAuthenticationFilter implements Filter {

    private static final String SECRET_KEY = "your-secret-key";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String token = getTokenFromRequest(httpRequest);

        System.out.println("JWT token: " + token); // ✅ 打印调试

        if (StringUtils.hasText(token)) {
            try {
                Claims claims = Jwts.parser()
                        .setSigningKey(SECRET_KEY)
                        .parseClaimsJws(token)
                        .getBody();

                String username = claims.getSubject();
                String role = claims.get("role", String.class);

                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(
                                username,
                                null,
                                Collections.emptyList() // 可以配置权限
                        );

                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
                SecurityContextHolder.getContext().setAuthentication(authentication);

            } catch (Exception e) {
                System.out.println("JWT解析失败: " + e.getMessage());
            }
        }

        chain.doFilter(request, response);
    }

    private String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7); // 去掉 Bearer 前缀
        }
        return null;
    }
}

🧠 拦截器作用:

  • 自动从请求头解析 Token
  • 校验签名、过期时间
  • 如果合法,向 Spring Security 注入 Authentication 对象,代表当前用户已认证

⚙️ 四、注册过滤器(Spring Security 环境)

如果使用 Spring Security,可以将 JwtAuthenticationFilter 注册到过滤链中:

scss 复制代码
@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                    .antMatchers("/login").permitAll()
                    .anyRequest().authenticated()
                )
                .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}

📋 五、前端如何携带 Token 请求

前端登录后,拿到后端返回的 token,之后的所有请求都加上请求头:

php 复制代码
axios.get('/api/user/info', {
  headers: {
    Authorization: 'Bearer ' + token
  }
})

如果你用的是 Fetch:

php 复制代码
fetch('/api/user/info', {
  headers: {
    'Authorization': 'Bearer ' + token
  }
});

✅ 最终效果演示

  1. 调用 /login,返回:
json 复制代码
{
  "token": "eyJhbGciOiJIUzI1NiIsInR...",
  "message": "登录成功"
}
  1. 访问需要认证的接口,如 /user/info,带上 token,返回成功数据。
  2. 如果 token 错误或过期,会被过滤器拦截,返回未认证提示。

✍️ 总结

步骤 内容
1️⃣ 登录成功生成 JWT
2️⃣ 前端保存 token 并携带请求
3️⃣ 后端过滤器拦截校验 JWT
4️⃣ 注入认证信息,实现无状态登录

🎁 附加建议:

  • 可以使用 Redis 存储 Token 黑名单,实现退出登录/踢人下线。
  • 生产环境务必设置合适的 Token 过期时间、密钥强度。
  • 登录成功返回 token 的同时,也可以返回用户信息。

📌 如果你觉得这篇文章对你有帮助,欢迎点赞 + 收藏 + 关注!我会持续更新更多 Java 项目实战和技术干货。

相关推荐
RoyLin2 小时前
TypeScript设计模式:策略模式
前端·后端·typescript
brzhang2 小时前
为什么说低代码谎言的破灭,是AI原生开发的起点?
前端·后端·架构
得物技术2 小时前
破解gh-ost变更导致MySQL表膨胀之谜|得物技术
数据库·后端·mysql
小桥风满袖3 小时前
极简三分钟ES6 - ES9中字符串扩展
前端·javascript
小码编匠3 小时前
WPF 中的高级交互通过右键拖动实现图像灵活缩放
后端·c#·.net
小Wang3 小时前
npm私有库创建(docker+verdaccio)
前端·docker·npm
用户73087011793083 小时前
Vue中集成文字转语音:使用Web Speech API实现功能
前端
李重楼3 小时前
前端性能优化之 HTTP/2 多路复用
前端·面试
Java水解3 小时前
【MySQL】从零开始学习MySQL:基础与安装指南
后端·mysql