JWT详解

简介: JWT详解

 JWT详解

传统的session认证

安全性

CSRF攻击因为基于cookie来进行用户识别, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

扩展性

对于分布式应用,需要实现 session 数据共享

性能

每一个用户经过后端应用认证之后,后端应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大,与REST风格不匹配。因为它在一个无状态协议里注入了状态

JWT

官网

https://jwt.io/

JWT

JWT是一种用于双方之间传递安全信息的简洁的、URL安全的声明规范。定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。特别适用于分布式站点的单点登录(SSO)场景

优点:

无状态

适合移动端应用

单点登录友好

原理

服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样

{

"姓名": "张三",

"角色": "管理员",

"到期时间": "2030年7月1日0点0分"

}

注意

用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候会加上签名,服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展

JWT的结构

头部(header)/载荷(payload)/签证(signature)

它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行

JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面

JWT项目搭建

创建maven项目,名字为cloud-jkw

pom.xml

<dependencies>
        <!-- JWT -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.79</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.7.0</version>
        </dependency>
        <!-- redis -->
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-data-redis</artifactId>-->
<!--        </dependency>-->
        <!-- eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- Actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- SpringMVC -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
    </dependencies>

image.gif

application.yml

server:
  port: 6500
spring:
  application:
    name: cloud-jwt
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/

image.gif

启动类

@SpringBootApplication
@EnableEurekaClient
public class Jwt6500 {
    public static void main(String[] args) {
        SpringApplication.run(Jwt6500.class,args);
    }
}

image.gif

Jwt工具类

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import java.util.Date;
/**
 * JWT工具类
 */
public class JWTUtil {
    //签发人
    private static final String ISS_USER="jkw";
    //令牌过期时间(五分钟)
    private static final long TOKEN_EXPIRE_TIME=5*60*1000;
    //签名秘钥(最好写的复杂一点)
    private static final String KEY="jkw-123456";
    /**
     * 生成令牌(签名)
     * @return
     */
    public static String token(){
        Date now=new Date();//时间
        Algorithm algorithm_key=Algorithm.HMAC256(KEY);//加密秘钥
        //创建JWT
        String token = JWT.create()
                //签发人
                .withIssuer(ISS_USER)
                //签发时间(当前时间)
                .withIssuedAt(now)
                //过期时间(当前时间+过期时间)
                .withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME))
                //加密过的签名秘钥
                .sign(algorithm_key);
        return token;
    }
    /**
     * 校验令牌(签名)
     * @param token 传来的令牌
     * @return
     */
    public static boolean verify(String token){
        try {
            Algorithm algorithm_key=Algorithm.HMAC256(KEY);//加密秘钥
            //校验令牌(校验 签发人/签名秘钥)
            JWTVerifier verifier = JWT.require(algorithm_key)
                    //签发人
                    .withIssuer(ISS_USER)
                    .build();
            verifier.verify(token);
            return true;
            //如果校验有问题,就会抛出异常
        }catch (IllegalArgumentException e){
            e.printStackTrace();
        }catch (JWTDecodeException e){
            e.printStackTrace();
        }
        return false;
    }
    //测试
    public static void main(String[] args) {
        //1.生成令牌
        //String token = token();
        //System.out.println(token);
        //eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ5dWx1IiwiZXhwIjoxNjc4MjAwMDM1LCJpYXQiOjE2NzgxOTk3MzV9.al7qL_RHDvLHLx38J-5WGcsrK9UnDEpSWM1L0I3iWT4
        boolean verify = verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ5dWx1IiwiZXhwIjoxNjc4MjAwMDM1LCJpYXQiOjE2NzgxOTk3MzV9.al7qL_RHDvLHLx38J-5WGcsrK9UnDEpSWM1L0I3iWT4");
        System.out.println(verify);
    }
}

image.gif

统一返回结果集

/**
 * 统一返回结果实体类
 */
@Data
@AllArgsConstructor
public class BaseResult<T> implements Serializable {
    //状态码(成功:200,失败:其他)
    private Integer code;
    //提示信息
    private String message;
    //返回数据
    private T data;
    //构建成功结果
    public static <T> BaseResult<T> ok(){
        return new BaseResult(CodeEnum.SUCCESS.getCode(),CodeEnum.SUCCESS.getMessage(),null);
    }
    //构建带有数据的成功结果
    public static <T> BaseResult<T> ok(T data){
        return new BaseResult(CodeEnum.SUCCESS.getCode(),CodeEnum.SUCCESS.getMessage(),data);
    }
}

image.gif

@Getter
@AllArgsConstructor
public enum CodeEnum {
    // 正常
    SUCCESS(200, "OK"),
    // 系统异常
    SYSTEM_ERROR(500, "系统异常"),
    // 业务异常
    PARAMETER_ERROR(601, "参数异常"),
    INSERT_PRODUCT_TYPE_ERROR(602, "该商品类型不能添加子类型"),
    DELETE_PRODUCT_TYPE_ERROR(603, "该商品类型有子类型,无法删除"),
    UPLOAD_FILE_ERROR(604, "文件上传失败"),
    /**
     * user
     */
    REGISTER_CODE_ERROR(605,"验证码不正确"),
    REGISTER_REPEAT_PHONE_ERROR(606,"手机号已存在"),
    REGISTER_REPEAT_NAME_ERROR(607,"用户名已存在"),
    LOGIN_NAME_PASSWORD_ERROR(608,"用户名或密码错误"),
    LOGIN_CODE_ERROR(609,"验证码不正确"),
    VERIFY_TOKEN_ERROR(610,"签名解析失败"),
    QR_CODE_ERROR(611,"二维码错误"),
    CHECK_SIGN_ERROR(612,"验签失败"),
    NO_STOCK_ERROR(613,"库存不足"),
    ORDER_EXPIRED_ERROR(614,"订单过期")
    ;
    private final Integer code;
    private final String message;
}

image.gif

控制器接口

@RestController
@RequestMapping("/user")
public class UserController {
    /**
     * 登陆
     * @param username 用户名
     * @param password 密码
     */
    @PostMapping("/login")
    public BaseResult login(String username, String password){
        //1.验证用户名和密码
        if("user".equals(username)&&"user".equals(password)){
            //2.生成令牌
            String token = JWTUtil.token();
            return BaseResult.ok(token);
        }else {
            return new BaseResult(CodeEnum.Login_ERROR.getCode(), CodeEnum.Login_ERROR.getMessage(), null);
        }
    }
}

image.gif

访问post【postman测试】

localhost:6500/user/login?username=jkw&password=jkw

image.gif

配置路由

#cloud-jwt
- id: cloud-jwt
  uri: lb://cloud-jwt
  predicates:
    - Path=/user/*

image.gif

访问post

localhost:9527/user/login?username=jkw&password=jkw

image.gif


相关文章
|
6天前
|
存储 JSON 安全
JWT 还能这样的去理解嘛??
JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的。JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。可以看出,。并且, 使用 JWT 认证可以攻击,因为 JWT 一般是存在在中,使用 JWT 进行身份验证的过程中是的。
185 1
|
6天前
|
JSON 算法 数据库
|
6月前
|
前端开发
什么是JWT?深入理解JWT从原理到应用(下)
什么是JWT?深入理解JWT从原理到应用(下)
43 0
|
6天前
|
JSON 算法 数据库
JWT
JWT
48 0
|
6天前
|
存储 JSON 算法
什么是JWT?
什么是JWT?
39 0
|
6天前
|
存储 JSON 算法
快速了解什么是jwt及如何使用jwt
快速了解什么是jwt及如何使用jwt
83 0
|
5月前
|
存储 JSON 安全
了解什么是JWT
了解什么是JWT
28 0
|
5月前
|
JSON 数据格式
jwt->jwt简介,jwt工具类,jwt集进成spa项目
jwt->jwt简介,jwt工具类,jwt集进成spa项目
46 0
|
6月前
|
JSON 算法 前端开发
什么是JWT?深入理解JWT从原理到应用(上)
什么是JWT?深入理解JWT从原理到应用(上)
395 0
|
7月前
|
JSON 算法 数据库
98 # jwt
98 # jwt
31 0