java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java JWT令牌

Java实现JWT令牌的示例代码

作者:一只游鱼

JWT是一种无状态认证方案,用于前后端分离项目,后端生成包含用户信息的token,前端保存并随请求头携带,后端通过验证token签名、有效期等确保安全,实现跨服务身份认证,感兴趣的可以了解一下

一、简介

JWT (JSON Web Token) 是一种 跨服务、跨语言的认证解决方案,常用于前后端分离项目(比如 Spring Boot + Vue)。
它的主要作用是:

  1. 身份认证
    • 用户登录成功后,后端生成一个 JWT 返回给前端。
    • 前端每次请求时带上这个 JWT,后端根据它来确认“你是谁”。
  2. 无状态认证(不依赖 session
    • 传统的 session 登录需要服务器保存状态,用户多了容易占用大量内存。
    • JWT 是 自包含的,不需要服务器保存用户状态,后端只要验证 token 就行。
  3. 安全传递信息
    • JWT 由三部分组成:Header.Payload.Signature。
    • Payload 里可以存储用户 ID、角色、过期时间等信息。
    • Signature 签名部分保证 token 不会被篡改。

二、实现流程

  1. 用户在 Vue 前端输入用户名、密码 → 发送到 Spring Boot 登录接口。
  2. Spring Boot 验证账号密码成功后 → 生成 JWT → 返回给前端。
  3. Vue 将 JWT 保存(通常存在 localStorage 或 sessionStorage)。
  4. 前端每次请求后端 API 时,在请求头 Authorization 里带上:Authorization: Bearer <token>
  5. Spring Boot 拦截请求,验证 JWT 是否有效:
    • 签名是否正确?
    • 是否过期?
    • 是否被篡改?
    • 验证通过才放行,否则返回 401(未认证)。

如图:

三、实战

这里我们用spring boot + vue 来演示,

在配置中:

# JWT
#密钥 注意密钥要够长58位
jwt.secret=abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+
#过期时间
jwt.expiration= 3600000

创建工具类:

@Value("${jwt.secret}")
private String secret;
 
@Value("${jwt.expiration}")
private long expiration; // 例如 3600000 = 1小时
 
// 生成 token
public String generateToken(String username) {
    Date now = new Date();
    Date expiryDate = new Date(now.getTime() + expiration);
 
    return Jwts.builder()
            .setSubject(username)
            .setIssuedAt(now)
            .setExpiration(expiryDate)
            .signWith(Keys.hmacShaKeyFor(secret.getBytes()))
            .compact();
}
 
// 获取用户名
public String getUsernameFromToken(String token) {
    return Jwts.parserBuilder()
            .setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
            .build()
            .parseClaimsJws(token)
            .getBody()
            .getSubject();
}
 
// 验证 token
public boolean validateToken(String token) {
    try {
        Jwts.parserBuilder()
                .setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
                .build()
                .parseClaimsJws(token);
        return true;
    } catch (JwtException | IllegalArgumentException e) {
        return false;
    }
}

创建SecurityConfig文件

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    String path = exchange.getRequest().getURI().getPath();
 
 
    // 1. 放行登录注册
    if (path.startsWith("/user/login") || path.startsWith("/user/register") || path.startsWith("/chat-stream")) {
        return chain.filter(exchange);
    }
    //如果没有Authorization头,返回401
   if (!exchange.getRequest().getHeaders().containsKey("Authorization")) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }
 
    // 2. 处理Authorization头
    String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
    if (!authHeader.startsWith("Bearer ")) {
        // 无有效Token,返回401
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }
 
    // 3. 解析Token
    String token = authHeader.substring(7);
    if (!jwtUtil.validateToken(token)) {
        // Token验证失败(过期、签名错误等),返回401并打印日志
        System.out.println("JWT验证失败:Token无效或过期 - " + token);
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }
 
    // 4. Token有效,设置认证信息到安全上下文
    String username = jwtUtil.getUsernameFromToken(token);
    Authentication auth = new UsernamePasswordAuthenticationToken(
            username, null, Collections.emptyList() // 无权限,可根据需求添加
    );
 
    // 5. 传递认证信息并继续执行过滤链
    return chain.filter(exchange)
            .contextWrite(ReactiveSecurityContextHolder.withAuthentication(auth));
}

接着创建JwtFilter

@Autowired
private JwtUtil jwtUtil;
 
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    String path = exchange.getRequest().getURI().getPath();
 
 
    // 1. 放行登录注册
    if (path.startsWith("/user/login") || path.startsWith("/user/register") || path.startsWith("/chat-stream")) {
        return chain.filter(exchange);
    }
 
    // 2. 处理Authorization头
    String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
    if (!authHeader.startsWith("Bearer ")) {
        // 无有效Token,返回401
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }
 
    // 3. 解析Token
    String token = authHeader.substring(7);
    if (!jwtUtil.validateToken(token)) {
        // Token验证失败(过期、签名错误等),返回401并打印日志
        System.out.println("JWT验证失败:Token无效或过期 - " + token);
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }
 
    // 4. Token有效,设置认证信息到安全上下文
    String username = jwtUtil.getUsernameFromToken(token);
    Authentication auth = new UsernamePasswordAuthenticationToken(
            username, null, Collections.emptyList() // 无权限,可根据需求添加
    );
 
    // 5. 传递认证信息并继续执行过滤链
    return chain.filter(exchange)
            .contextWrite(ReactiveSecurityContextHolder.withAuthentication(auth));
}

在控制层中,我们就可以在登录逻辑中创建、返回给密钥了:

//生成token
String token = jwtUtil.generateToken(user.getUserName());
//封装返回数据
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("token", token);

在前端中,处理登录请求,将登录成功后后端返回的token保存到浏览器:

const onFinish = async (values) => {
  loading.value = true
  try {
    if (isLogin.value) {
 
      //加密数据
      const encrypted = Encrypt(JSON.stringify(values))
      // 实现登录逻辑
      const response = await request.post('/user/login', { data: encrypted })
 
      //保存token
      localStorage.setItem("token", JSON.parse(response.data).data.token)
 
     
      alert("登录成功")
 
 
      // 刷新页面
      // window.location.reload()
    } else {
      //加密数据
      const encrypted = Encrypt(JSON.stringify(values))
      // 实现注册逻辑
      const response = await request.post('/user/register', { data: encrypted })
      console.log('注册:', values)
    }
  } catch (error) {
    console.error(error)
    alert("登录失败")
  } finally {
    loading.value = false
  }
}

创建request.Js,将保存到本地浏览器的token放到请求头中:

import axios from "axios";
const request = axios.create({
  baseURL: "/api",
  timeout: 30000, // 修改为30秒
  responseType: "stream" // 流式响应
});
// 请求拦截器:自动携带 token
request.interceptors.request.use(
  (config) => {
  const token = localStorage.getItem("token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);
export default request;

发送请求:

// 删除
const deleteChat = (id) => {
  Modal.confirm({
    title: '确认删除',
    content: '确定要删除此对话吗?(对话历史也将会全部删除)',
    okText: '确认',
    cancelText: '取消',
    onOk() {
      // 调用后端接口删除对话
      eventSource?.close();
      request.delete(`/chat/delete/${id}`)
        .then((response) => {
          if (response.data.code === "200") {  
            //显示更新成功
            message.success('删除成功');
            //刷新对话列表
            getChatList();
          } else {
            //显示更新失败
            message.error('删除失败');
          }
        })
        .catch(err => {
          message.error('删除失败');
          // console.error('删除失败:', err);
        });
    },
    onCancel() {
      console.log('取消删除');
    },
  });
};

运行可以看到,登录成功后后端成功返回token:

Token成功保存:

如果token国企或者错误:

再次请求会显示401无权限报错

到此这篇关于Java实现JWT令牌的示例代码的文章就介绍到这了,更多相关Java JWT令牌内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文