[springboot+jwt]实现较为复杂的token校验(赋源码)

本文介绍了JWT在SpringBoot中的使用,包括其工作原理、应用场景、优点,以及如何在用户登录时处理token生成与验证,强调了在请求头中添加secretKey、clientid和timestamp的安全措施。
摘要由CSDN通过智能技术生成

JWT官网: jwt官网链接
源码地址: springboot-token

1.jwt是什么

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名

流程

在这里插入图片描述

  1. 用户使用账号和面发出post请求
  2. 服务器使用私钥创建一个jwt
  3. 服务器返回这个jwt给浏览器;
  4. 浏览器将该jwt串在请求头中像服务器发送请求
  5. 服务器验证该jwt
  6. 返回响应的资源给浏览器

应用场景

身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的

优点

  • 简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
  • 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
  • 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
  • 不需要在服务端保存会话信息,特别适用于分布式微服务

2.明确需求

当前存在的问题

用户登录后返回token信息,前端将token存在cookie中,每次访问后端api在请求头中携带token信息;这样的方式过于简单;需要添加额外的校验参数

解决思路

  • 在登录生成token时,前端除了要提交用户名和密码;还需要提交secretKey(后端分配)、clientid(后端分配)、timestamp(当前系统时间戳);在生成token时加入上述参数返回给前端
  • 在访问后端api接口校验token时,除了校验token是否正确,还需要对比token中的secretKey、clientid、timestamp与请求头中的是否一致

3.代码实战

导入jwt依赖

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

添加拦截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    AuthenticationInterceptor authenticationInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludeApi = Arrays.asList("/api/login");
        registry.addInterceptor(authenticationInterceptor)
                .addPathPatterns("/**").excludePathPatterns(excludeApi);    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
    }

}

拦截器类

负责校验请求头中的token和其他参数(secretKey、clientid、timestamp)

@Component
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    UserService userService;
    @Autowired
    JwtUtils jwtUtils;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
................................省略

jwt工具类

token生成和token校验

/**
 * jwt工具类
 */
@ConfigurationProperties(prefix = "jwt.config")
@Component
public class JwtUtils {
    private Logger logger = LoggerFactory.getLogger(getClass());

    private long expire;
    private String header;
    private List<ClientidSecretModel> secret;
    @Value("${jwt.config.signkey}")
    private String signkey;


    /**
     * 生成jwt token
     */
    public String generateToken(User user, String clientId, String secretKey, String timestamp) throws Exception {
        // 校验clientId,secretKey,timestamp
        checkClientIdWithSecretKey(clientId, secretKey, timestamp);
        Date nowDate = new Date();
        Long v = expire;
        //过期时间
        Date expireDate = new Date(nowDate.getTime() + v * 1000);
        HashMap<String, Object> extraParams = new HashMap<>();
        extraParams.put("secretKey", secretKey);
        extraParams.put("clientid", clientId);
        extraParams.put("timestamp", timestamp);
        extraParams.put("userid", user.getId());
        HashMap<String, Object> header = new HashMap<>();
        header.put("typ", "JWT");
        return JWT.create()
                .withHeader(header)
                .withSubject(user.getUsername())
                .withAudience(JSON.toJSONString(extraParams))
                .withIssuedAt(nowDate)
                .withExpiresAt(expireDate)
                .sign(Algorithm.HMAC512(signkey.getBytes(StandardCharsets.UTF_8)));

    }

jwt服务端配置参数(yaml)

jwt:
  config:
    secret:
     - clientid: pc         #pc端的clientid
       secretKey: secretKey1 # pc端的secretKey
     - clientid: app     #app端的clientid
       secretKey: secretKey2  #app端secretKey
    expire: 3600
    signkey: sd1234567890123456789012345678901  #服务端签名密钥

4.测试

4.1.1登录时提交的参数不匹配

在这里插入图片描述

4.1.2登录参数正确

在这里插入图片描述

4.2.1请求头中带token和其他参数请求api

在这里插入图片描述

4.2.2请求头中token或者其他参数篡改时

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值