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>

application.yml

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

启动类

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

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);
    }
}

统一返回结果集

/**
 * 统一返回结果实体类
 */
@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);
    }

}
@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;
}

控制器接口

@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);
        }
    }
}

访问post【postman测试】

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

配置路由

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

访问post

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月木@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值