SpringBoot之整合JWT

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: SpringBoot之整合JWT

整合JWT

令牌组成

  • 1.标头(Header)
  • 2.有效载荷(Payload)
  • 3.签名(Signature)
    因此,JWT通常如下所示:xxxxx.yyyyy.zzzzz
    Header.Payload.Signature

jwt组成

Header

标头通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256或RSA。它会使用 Base64 编码组成 JWT 结构的第一部分。

  • 注意:Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。
{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

不要在这里放密码;反编译Base64即可解码;

  • 令牌的第二部分是有效负载,其中包含声明。声明是有关实体
  • (通常是用户)和其他数据的声明。同样的,它会使用 Base64 编码组成 JWT 结构的第二部分
{
  "username": "xzxadmin",
  "datetime": "2023-05-01 11:11:11",
  "role": "admin"
}

Signature

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

整合JWT

<!--引入jwt-->
<dependency>
  <groupId>com.auth0</groupId>
  <artifactId>java-jwt</artifactId>
  <version>3.4.0</version>
</dependency>

JWT帮助类

public class JwtUtils {
    //常量
    public static final long time = 1000 * 60 * 60 * 24;//token过期时间
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";//秘钥
    //生成token字符串的方法
    public static String getJwtToken(String username, String role){
        String JwtToken = Jwts.builder()
                //头部
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")
                //载荷
                .claim("username",username)
                .claim("role",role)
                .setSubject("jwt-user")
                //token过期时间:1小时
                .setExpiration(new Date(System.currentTimeMillis()+time ))
                .setId(UUID.randomUUID().toString())//id字段
                //签名
                .signWith(SignatureAlgorithm.HS256,APP_SECRET)//签名加密算法和
                //连接字符串(.);
                .compact();
        return JwtToken;
    }
    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 判断token是否存在与有效
     * @param request
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 根据token获取会员id
     * @param request
     * @return
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }
}

整合JWT

导入依赖

<!--引入jwt-->
<dependency>
  <groupId>com.auth0</groupId>
  <artifactId>java-jwt</artifactId>
  <version>3.4.0</version>
</dependency>
 
<!--引入mybatis-->
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.1.3</version>
</dependency>
 
<!--引入lombok-->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.12</version>
</dependency>
 
<!--引入druid-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.19</version>
</dependency>
 
<!--引入mysql-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.38</version>
</dependency>

配置文件

server.port=8989
spring.application.name=jwt
 
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jwt?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC&useSSL=false
spring.datasource.username=root
spring.datasource.password=1234
 
mybatis.type-aliases-package=com.zuhao.springbootjwt.entity
mybatis.mapper-locations=classpath:com/zuhao/springbootjwt/mapper/*.xml
 
logging.level.com.zuhao.springbootjwt.dao=debug

JWT拦截器

interceptor包下新建拦截器

@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
    //目标资源方法执行前执行。 返回true:放行    返回false:不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1,先获取请求头
        String token = request.getHeader("Authorization");
        System.out.println("token:"+token);
        response.setContentType("application/json;charset = UTF-8");
        ObjectMapper mapper = new ObjectMapper();
        //2,判断请求头是否存在
        if (token == null || "".equals(token)){
            //请求头不存在或者请求头为空
            log.info("...token不存在");
            response.getWriter().write("result:缺少token");
            return false;
        }else{
            try {
                boolean isJwt = JwtUtils.checkToken(token);
                //解析不出错就是格式有效;
                /*从redis中查询token*/
                //时间有效,通过;时间无效,过期;
            }  catch (Exception e)  {
                log.info("请求头不正确!!");
                response.getWriter().write("result:token错误:");
                return false;
            }
        }
        return true;
    }
    //==========下面与登录无关,不用写==============
    //目标资源方法执行后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle ... ");
    }
    //视图渲染完毕后执行,最后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion .... ");
    }
}

config包下添加拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //定义拦截对象
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/users/login",
                        "/users/login/**",
                        "/swagger-ui/",
                        "/swagger-ui/**",
                        "/swagger-resources",
                        "/swagger-resources/**",
                        "/v3/**",
                        "/users/hello"
                );
    }
}

Jwt的优缺点

JWT(JSON Web Token)是一种用于在各方之间安全传输信息的紧凑、可自包含的方式。它广泛应用于身份认证和授权。以下是JWT的优缺点:

优点

1. 自包含性

JWT包含所有必要的信息,包括用户身份、有效期、权限等,这些信息被编码后存储在令牌中。由于JWT是自包含的,服务器不需要存储会话信息,可以直接通过解析JWT来获取所需信息,减少了服务器的负担。

2. 跨域支持

JWT可以在不同的域之间传输,适用于分布式系统和微服务架构中的跨域认证。它可以通过HTTP头、URL参数或其他方式在不同的系统或应用之间传递。

3. 简洁和紧凑

JWT采用Base64编码,格式简洁,占用空间小,适合在URL、HTTP头中传输。这种紧凑性使其适合在网络带宽有限的场景下使用。

4. 可扩展性

JWT可以在标准字段之外,添加自定义字段,如用户角色、权限等,灵活性很强,可以根据具体需求扩展。

5. 无需服务器存储会话信息

由于JWT是自包含的,服务器不需要存储会话数据,减少了服务器的存储和管理开销。这对需要横向扩展的系统非常有利。

6. 支持多种签名和加密算法

JWT支持多种签名算法,如HMAC、RSA、ECDSA等,可以根据安全需求选择合适的算法进行签名和加密,确保数据的完整性和保密性。

7. 广泛支持

JWT被广泛支持,很多现代开发框架和库都提供了对JWT的内置支持,使得集成变得非常容易。

缺点

1. 过期处理

JWT一旦签发,通常有效期是固定的。在有效期内,如果需要提前吊销或更新权限,比较困难。通常需要实现黑名单机制来管理已失效的JWT,但这又增加了服务器的存储和管理负担。

2. 安全性问题

由于JWT包含了所有的用户信息并在客户端存储,如果没有合适的加密和签名机制,容易被攻击者窃取和篡改。尤其是在使用对称加密时,密钥管理需要非常小心,否则整个系统的安全性都会受到威胁。

3. 信息暴露风险

虽然JWT是编码过的,但它并不是加密的,任何人都可以解码JWT并查看其中的信息。如果在JWT中存储了敏感信息,容易导致信息泄露风险。

4. 体积问题

JWT虽然相对紧凑,但在包含了大量信息后,仍然会变得比较大。特别是如果在JWT中嵌入了大量数据,会影响传输效率和性能。

5. 有限的支持复杂会话管理

JWT不适合复杂的会话管理场景,比如需要频繁更新会话信息的情况。因为JWT是无状态的,每次更新都需要重新生成新的令牌。

6. 缺乏标准的撤销机制

JWT没有内置的撤销机制,一旦签发出去,无法单独吊销某个JWT。这与传统的基于会话的认证机制不同,传统机制可以在服务器端随时终止会话。

7. 认证和授权的混淆

JWT可以用作认证令牌和授权令牌,容易在实际应用中混淆两者的用途。使用不当可能导致权限控制不严格,增加安全隐患。

JWT因其自包含、跨域支持和无需存储会话信息等优势,成为现代Web应用中流行的身份认证方式。然而,它的安全性和有效期管理方面的缺陷,也要求开发者在实际应用中谨慎设计和实现,确保系统的安全和高效。

关注我,不迷路,共学习,同进步

关注我,不迷路,共学习,同进步

相关文章
|
2月前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
131 0
|
6天前
|
JSON 安全 Java
Spring Boot中使用JWT进行安全认证
Spring Boot中使用JWT进行安全认证
|
7天前
|
Java API Spring
Spring Boot整合JWT
Spring Boot整合JWT
1天搞定SpringBoot+Vue全栈开发 (9)JWT跨域认证
1天搞定SpringBoot+Vue全栈开发 (9)JWT跨域认证
|
2月前
|
存储 JSON 算法
SpringBoot之JWT令牌校验
SpringBoot之JWT令牌校验
65 2
|
2月前
|
SQL 安全 Java
微服务之Springboot整合Oauth2.0 + JWT
微服务之Springboot整合Oauth2.0 + JWT
35 1
|
JSON 算法 安全
SpringBoot从入门到精通(三十四)如何集成JWT实现Token验证
近年来,随着前后端分离、微服务等架构的兴起,传统的cookie+session身份验证模式已经逐渐被基于Token的身份验证模式取代。接下来介绍如何在Spring Boot项目中集成JWT实现Token验证。
SpringBoot从入门到精通(三十四)如何集成JWT实现Token验证
|
JSON 算法 Java
SpringBoot集成JWT实现token验证
JWT官网: https://jwt.io/JWT(Java版)的github地址:https://github.com/jwtk/jjwt 什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。
3744 0
|
Java
Java:SpringBoot集成JWT实现token验证
Java:SpringBoot集成JWT实现token验证
183 0
Java:SpringBoot集成JWT实现token验证
|
12小时前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的短视频广告发布系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的短视频广告发布系统的详细设计和实现(源码+lw+部署文档+讲解等)
17 8