在SpringBoot中使用jwt实现token身份认证的实例代码

 更新时间:2023年09月02日 10:02:24   作者:金金金__  
你还不会在SpringBoot中使用jwt实现token身份认证吗,本文小编就给大家详细的介绍一下在SpringBoot中使用jwt实现token身份认证的实例代码,感兴趣的同学可以自己动手试一试
  • jwt (JSON Web Tokens)

jwt结构

  • 用(.)分割的三个部位组成
    • 标头
    • 有效载荷
    • 签名

xxx.xxx.xxx

标头

  • 标头通常由两部分组成:令牌的类型(JWT)和所使用的签名算法(例如 HMAC SHA256 或 RSA
{ "alg": "HS256", "typ": "JWT" }
  • 然后,对该 JSON 进行Base64Url编码以形成 JWT 的第一部分。

有效载荷

  • 令牌的第二部分是有效负载,其中包含声明。声明是关于实体(通常是用户)和附加数据的声明。索赔分为三种类型:注册索赔、公开索赔和私人索赔。
  1. 注册声明
    • 这些是一组预定义的声明,不是强制性的,而是推荐的,以提供一组有用的、可互操作的声明。其中一些是: iss(发行者)、 exp(到期时间)、 sub(主题)、 aud(受众)等。
  2. 公共声明
    • 这些可以由使用 JWT 的人随意定义。但为了避免冲突,它们应该在[IANA JSON Web Token Registry]中定义,或者定义为包含防冲突命名空间的 URI
  3. 私人声明
    • 这些是为在同意使用它们的各方之间共享信息而创建的自定义声明,既不是注册声明也不是公开声明。

有效负载示例

{ "sub": "1234567890", "name": "John Doe", "admin": true }
  • 然后对有效负载进行Base64Url编码以形成 JSON Web 令牌的第二部分。

请注意,对于签名令牌,此信息虽然受到防止篡改的保护,但任何人都可以读取。除非加密,否则请勿将秘密信息放入 JWT 的有效负载或标头元素中。

签名

  • 要创建签名部分,您必须获取编码的标头、编码的有效负载、秘密、标头中指定的算法,然后对其进行签名。

使用HMAC SHA256算法,则将通过以下方式创建签名

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

签名用于验证消息在传输过程中没有发生更改,并且在使用私钥签名的令牌的情况下,它还可以验证 JWT 的发送者是否是其所说的人。

将所有内容放在一起

  • 输出是三个由点分隔的 Base64-URL 字符串,可以在 HTML 和 HTTP 环境中轻松传递,同时与基于 XML 的标准(例如 SAML)相比更加紧凑。
  • 下面显示了一个 JWT,它具有先前的标头和有效负载编码,并且使用密钥进行签名。

image.png

  • 官网翻译的,先了解一点前置知识,开整!

使用

依赖

  • pom.xml文件咱们先加入jwt的依赖

image.png

JwtTokenUtils工具类

public class JwtTokenUtils {
  // token私钥
  private static final String TOKEN_SECRET = "jwtSECRET";
  // token过期时长30分钟
  private static final long EXPIRE_TIME = 1800L; // 单位为秒
  /**
   * 生成用户token,设置token超时时间
   *
   * @return 生成的token
   */
   public static String createToken(LoginVo loginVo) {
    Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME * 1000);
    // 创建 Token 的 payload
    Map<String, Object> map = new HashMap<>();
    map.put("alg", "HS256");
    map.put("typ", "JWT");
    // 生成 JWT Token
    String token = JWT.create()
      .withHeader(map)// 添加头部
      .withClaim("account", loginVo.getAccount())
      .withExpiresAt(expireDate) //超时设置,设置过期的日期
      .withIssuedAt(new Date()) //签发时间
      .sign(Algorithm.HMAC256(TOKEN_SECRET)); //SECRET加密
    return token;
  }
  /**
   * 检验token是否正确
   * @param token 需要校验的token
   * @return 校验是否成功
   */
  public static Map<String, Claim> verifyToken(String token) {
    DecodedJWT jwt = null;
    token = token.replace("Bearer ", ""); // 这里要去掉Bearer ,postman默认会加这个,导致匹配不上
    try {
      //设置签名的加密算法:HMAC256
      Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 私钥需要一致
      JWTVerifier verifier = JWT.require(algorithm).build();
      jwt = verifier.verify(token);
    } catch (Exception e) {
//      logger.error(e.getMessage()); // 还没安装log4j等日志依赖,先注释
//      logger.error("token解码异常");
      //解码异常则抛出异常
      return null;
    }
    return jwt.getClaims();
  }
}
  1. createToken方法主要就是创建token,并把账号密码作为自定义声明添加到 JWT 的 payload 中,最终返回
  2. verifyToken方法其实就是解码 token 并将其转换成 DecodedJWT 对象,通过 getClaims() 方法获取 payload 中的所有声明信息,并以 Map 的形式返回,如果 token 不合法或者解码过程中出现异常,则返回 null,校验的效果。
  • 注意Bearer 这个玩意

JwtFilter.java

@Component
@WebFilter(filterName = "JwtFilter", urlPatterns = "/*")
public class JwtFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    System.out.println("jwt初始化方法");
  }
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    final HttpServletRequest request = (HttpServletRequest) req;
    final HttpServletResponse response = (HttpServletResponse) res;
    response.setCharacterEncoding("UTF-8");
    final String token = request.getHeader("Authorization");
    String requestPath = request.getRequestURI();
    // 登录接口,直接放行
    if (requestPath.contains("/user/login")) {
      chain.doFilter(request, response);
      return;
    }
     if ("OPTIONS".equals(request.getMethod())) { // 是否是 OPTIONS 请求
      response.setStatus(HttpServletResponse.SC_OK);
      chain.doFilter(request, response);
    } else {
      if (token == null) {
        response.getWriter().write("没有令牌");
        return;
      }
      Map<String, Claim> userData = JwtTokenUtils.verifyToken(token); // 检验 token
      if (userData == null) {
        response.getWriter().write("令牌非法");
        return;
      }
      String account = userData.get("account").asString(); // 获取用户名
      request.setAttribute("account", account); // 设置用户名
      chain.doFilter(req, res); // 过滤成功
    }
  }
  @Override
  public void destroy() {
    System.out.println("jwt销毁方法执行的逻辑");
  }
}
  • 这其实就是一个过滤类,你请求接口前就会先到这里,执行相对应的逻辑。
  1. init: 初始化方法
  2. doFilter:过滤拦截方法(主要逻辑就写在这)
  3. destroy:销毁方法

注意

  1. urlPatterns = "/*" : 这个代表拦截所有的请求,我在 doFilter 里面判断了包含 /login 直接放行。意思就是登录接口直接放行,返回 token 给前端,如果是别的请求则走对应逻辑。
  2. @Component :这个需要加上,这样才能扫描到你这个类不然不生效的。或者你在启动类加上 @ServletComponentScan(basePackages = "com.yjx.boot.config") ,我的过滤器是写在 config 包下的。

LoginController

@Controller
@RestController  // 可处理 HTTP 请求的控制器,并且能够返回数据给客户端
@RequestMapping("/user")
@RequiredArgsConstructor
public class LoginController {
  private final AdminService adminService;
  // 用户登陆
  @GetMapping("/login")
  ResultVO<Object> login(LoginVo loginVo) {
    Admin admin = new Admin();
    admin.setAccount(loginVo.getAccount());
    admin.setAdminPassword(loginVo.getAdminPassword());
    // 查找是否有该管理员
    Admin an = adminService.queryByAdmin(admin);
    if (an == null) {
      return ResultResponse.failure(ResultCodeEnum.INTERNAL_NOT_FOUND);
    } else {
      String token = JwtTokenUtils.createToken(loginVo);
      loginVo.setToken(token);
      an.setToken(token);
      return ResultResponse.success(an);
    }
  }
  @GetMapping("/secure/current_registrant")
  public String currentRegistrant(HttpServletRequest request) {
    System.out.println("request" + request);
    String id = request.getParameter("id");
    String account = request.getParameter("account");
    String user_password = request.getParameter("adminPassword");
    return "当前用户信息: id=" + id + " ,account=" + account + " ,user_password=" + user_password;
  }
}
  • 代码部分就完事了,测试一下。

测试

  • 我们请求下/secure/current_registrant接口,此时是没有token的。

image.png

正常,输出了我们想要的:没有令牌

  • 我们先去请求/login,看看token以及用户信息会不会如我们所想 返回给我们呢?

image.png

很好,也拿到了我们想要的,token也返回了,我们复制一下这个token,我们再次请求/secure/current_registrant,并把token放入请求头,试试能不能成功

image.png

  • 成功

我们来试试把token写错一个字母会输出什么。

image.png

我们在请求头不传入token呢

image.png

完事!测试成功!写的并没有很好 甚至可能有些地方都写错了,慢慢完善吧

以上就是在SpringBoot中使用jwt实现token身份认证的实例代码的详细内容,更多关于SpringBoot jwt实现token认证的资料请关注脚本之家其它相关文章!

相关文章

  • ssm项目改造spring boot项目完整步骤

    ssm项目改造spring boot项目完整步骤

    Spring Boot现在已经成为Java开发领域的一颗璀璨明珠,它本身是包容万象的,可以跟各种技术集成,下面这篇文章主要给大家介绍了关于ssm项目改造spring boot项目的相关资料,需要的朋友可以参考下
    2023-04-04
  • 在Spring Boot中如何使用数据缓存

    在Spring Boot中如何使用数据缓存

    本篇文章主要介绍了在Spring Boot中如何使用数据缓存,具有一定的参考价值,有兴趣的可以了解一下。
    2017-04-04
  • Java8函数式编程应用小结

    Java8函数式编程应用小结

    Java8非常重要的就是引入了函数式编程的思想,使得这门经典的面向对象语言有了函数式的编程方式,弥补了很大程度上的不足,函数式思想在处理复杂问题上有着更为令人称赞的特性,本文给大家介绍Java8函数式编程应用小结,感兴趣的朋友一起看看吧
    2023-12-12
  • Spring打包jar包时jsp页面无法访问问题解决

    Spring打包jar包时jsp页面无法访问问题解决

    这篇文章主要介绍了Spring打包jar包时jsp页面无法访问问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • idea使用spring Initializr 快速搭建springboot项目遇到的坑

    idea使用spring Initializr 快速搭建springboot项目遇到的坑

    这篇文章主要介绍了idea使用spring Initializr 快速搭建springboot项目遇到的坑,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • 关于Seata基本使用及二阶提交流程

    关于Seata基本使用及二阶提交流程

    这篇文章主要介绍了关于Seata基本使用及二阶提交流程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • SpringBoot整合Minio的过程(支持公有及私有bucket)

    SpringBoot整合Minio的过程(支持公有及私有bucket)

    Bucket 是存储Object的逻辑空间,每个Bucket之间的数据是相互隔离的,对用户而言,相当于存放文件的顶层文件夹,这篇文章主要介绍了SpringBoot整合Minio的过程(支持公有及私有bucket),需要的朋友可以参考下
    2025-03-03
  • IntelliJ IDEA 2020.1 EAP4 发布,重命名/更改签名新功能一览

    IntelliJ IDEA 2020.1 EAP4 发布,重命名/更改签名新功能一览

    这篇文章主要介绍了IntelliJ IDEA 2020.1 EAP4 发布,重命名/更改签名新功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • 图文详解java反射机制及常用应用场景

    图文详解java反射机制及常用应用场景

    这篇文章主要为大家介绍了图文详解java反射机制及常用应用场景,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03
  • JSONObject toJSONString错误的解决

    JSONObject toJSONString错误的解决

    这篇文章主要介绍了JSONObject toJSONString错误的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02

最新评论