做若依项目时的拓展功能。前期的准备知识——JWT。

在人脸识别系统中使用 JWT(JSON Web Token) 主要是为了高效、安全地管理用户身份验证和权限控制,尤其在涉及多系统交互或第三方服务(如百度 AI 人脸识别接口)时,JWT 能提供简洁、可靠的身份凭证传递方式。

前期准备

1. 注册百度 AI 开放平台账号

  • 访问百度 AI 开放平台
  • 创建应用并获取以下信息:
    • APP_ID
    • API_KEY
    • SECRET_KEY
  • 开通人脸识别服务(免费额度:QPS=2,5000 次 / 日)

2. 技术栈选型

  • 前端:Vue.js + Element UI + 浏览器原生 API
  • 后端:Spring Boot + MyBatis + JWT
  • 人脸服务:百度 AI 人脸识别 API

 JWT

JSON Web Token(JSON Web令牌)

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

什么时候你应该用JSON Web Token
  • Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。
JSON Web Token的结构是什么样的

JSON Web Token由三部分组成,它们之间用(.)连接。这三部分分别是:

  • 标头(Header)
  • 有效载荷(Payload)
  • 签名(Signature)

因此,一个典型的JWT看起来是这个样子的:

xxxxx.yyyyy.zzzzz

Header header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。

{
    'alg': "HS256",
    'typ': "JWT"
}

然后,用Base64对这个JSON编码就得到JWT的第一部分

Payload JWT的第二部分是payload,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。

  • Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
  • Public claims : 可以随意定义。
  • Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。
    {
        "sub": '1234567890',
        "name": 'john',
        "admin":true
    }

 对payload进行Base64编码就得到JWT的第二部分

注意,不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

Signature :前面两部分都使用Base64进行编码,前端可以解开知道里面的信息。Signature需要使用编码后的header和payload
加上我们提供的一个密钥,使用header中指定的签名算法(HS256)进行签名。签名的作用是保证JWT没有被篡改过

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret);

**签名目的:**签名的过程实际上是对头部以及负载内容进行签名,防止内容被窜改。如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。

JWT使用
 引入依赖
<!--引入JWT-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.0</version>
</dependency>
生成token
HashMap<String,Object> map = new HashMap<>();
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,20);
        String token = JWT.create()
                .withHeader(map) //可以不设定,就是使用默认的
                .withClaim("userId",20)//payload  //自定义用户名
                .withClaim("username","zhangsan")
                .withExpiresAt(instance.getTime()) //指定令牌过期时间
                .sign(Algorithm.HMAC256("fdahuifeuw78921"));//签名
根据令牌和签名解析数据
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("fdahuif921")).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        decodedJWT.getClaim("userId").asInt();//获取负载里面对应的内容
        decodedJWT.getClaim("username").asString();
        decodedJWT.getExpiresAt();//获取过期时间
常见异常信息
SignatureVerificationException //签名不一致异常
TokenExpiredException //令牌过期异常
AlgorithmMismatchException //算法不匹配异常
InvalidClaimException //失效的payload异常(传给客户端后,token被改动,验证不一致)
封装工具类
public class JWTUtils {
    private static String SIGNATURE = "token!@#$%^7890";

    /**
     * 生成token
     * @param map //传入payload
     * @return 返回token
     */
    public static String getToken(Map<String,String> map){
        JWTCreator.Builder builder = JWT.create();
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,7);
        builder.withExpiresAt(instance.getTime());
        return builder.sign(Algorithm.HMAC256(SIGNATURE)).toString();
    }

    /**
     * 验证token
     * @param token
     */
    public static void verify(String token){
        JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
    }

    /**
     * 获取token中payload
     * @param token
     * @return
     */
    public static DecodedJWT getToken(String token){
        return JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
    }
}
SpringBoot整合JWT
 登录时生成token
//controller层接收数据,生成token,并响应
Map<String,Object> map = new HashMap<>();
try{
    User userDB = userService.login(user);
    Map<String,String> payload = new HashMap<>();
    payload.put("id",userDB.getId());
    payload.put("name",userDB.getName());
    //生成JWT令牌
    String token = JWTUtils.getToken(payload);
    map.put("state",true);
    map.put("msg","认证成功");
    map.put("token",token);//响应token
} catch (Exception e) {
    map.put("state","false");
    map.put("msg",e.getMessage());
}
声明一个token拦截器类
package com.liup.interceptor;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.office.utils.JWTUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT验证拦截器
 */
public class JWTInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String,Object> map = new HashMap<>();
        //令牌建议是放在请求头中,获取请求头中令牌
        String token = request.getHeader("token");
        try{
            JWTUtils.verify(token);//验证令牌
            return true;//放行请求
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg","无效签名");
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            map.put("msg","token过期");
        } catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            map.put("msg","token算法不一致");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("msg","token失效");
        }
        map.put("state",false);//设置状态
        //将map转化成json,response使用的是Jackson
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().print(json);
        return false;
    }
}
配置拦截器
package com.liup.config;

import com.office.interceptor.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/**");
    }
}
在人脸登录中的作用
  • 身份验证:用户人脸验证通过后,生成 JWT 作为身份凭证。
  • 权限控制:通过 Payload 中的声明信息(如角色)控制访问权限。
  • 无状态:服务端不存储会话信息,便于分布式部署。

若依系统有token验证处理。通过tokenService.createToken(loginUser)生成 JWT 令牌。将 Token 和用户信息封装到AjaxResult对象中返回给前端。

1. 无状态身份验证,适配分布式架构
  • 场景:人脸识别系统通常是分布式架构(如前后端分离、微服务),用户通过人脸验证后,需要在多个服务 / 节点间保持身份标识。
  • JWT 的作用:
    • 用户通过人脸登录后,服务器生成包含用户信息(如业务 ID、权限等)的 JWT 令牌,返回给客户端(如前端应用、移动端)。
    • 客户端后续每次请求(如调用百度 AI 人脸识别接口)时,只需在请求头中携带 JWT,服务器通过验证签名即可确认用户身份,无需查询数据库或会话存储(无状态),提升系统扩展性和性能。
2. 安全传递用户标识(如业务 ID)
  • 场景:需要将用户的业务 ID(如数据库中的用户表主键)与百度 AI 云端特征库中的用户 ID 绑定,确保人脸数据与用户身份一一对应。
  • JWT 的作用:在 JWT 的 载荷(Payload) 中加入用户的业务 ID
{
  "sub": "123456",       // 业务用户ID(与数据库用户表主键对应)
  "name": "张三",
  "exp": 1685000000      // 过期时间
}

当调用百度 AI 人脸识别接口时,将 JWT 中的 sub(业务 ID)作为参数传递给百度 AI 的 userId 字段,实现业务系统用户与云端特征库的绑定。

// 解析JWT获取业务用户ID
String bizUserId = JwtUtils.parseToken(token).getSubject();

// 调用百度AI接口时传递bizUserId作为userId参数
BaiduAiClient.addFaceFeature(bizUserId, faceImageData);
典型流程:结合人脸识别的 JWT 使用示例
  1. 用户人脸登录:
    • 用户通过摄像头上传人脸数据,服务器调用百度 AI 接口进行比对。
    • 验证通过后,从数据库查询用户的业务 ID(如 userId=123),生成包含该 ID 的 JWT 令牌。
  2. 返回 JWT 给客户端:
    // 生成JWT(包含业务用户ID)
    String token = JwtUtils.generateToken(123, "USER"); 
    // 返回给前端
    return AjaxResult.success().put("token", token).put("user", userInfo);
    
  3. 后续请求携带 JWT:
    • 客户端每次请求(如更新人脸特征)时,在请求头中添加 Authorization: Bearer <JWT>。
    • 服务器解析 JWT 获得 userId=123,调用百度 AI 接口时传递该 ID 作为 userId 参数:
      // 解析JWT获取业务用户ID
      Integer bizUserId = JwtUtils.parseToken(request.getHeader("Authorization")).getSubject();
      
      // 调用百度AI接口绑定特征库
      baiduAiClient.updateFaceFeature(bizUserId.toString(), newFaceImage);
      
  4. 权限验证:

    • 解析 JWT 中的 roles 字段,判断用户是否有权限执行操作(如仅管理员允许删除特征)。
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐