Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Springboot+JWT+Vue实现登录功能

Springboot+JWT+Vue实现登录功能

原创
作者头像
CodeGoat24
修改于 2022-04-07 02:26:30
修改于 2022-04-07 02:26:30
2.6K20
代码可运行
举报
运行总次数:0
代码可运行

一、前言

最近在写一个Springboot+Vue的前后端分离项目,并且刚学了JWT的功能和原理,正好拿来练练手,在开发过程中也遇到了很多坑点,主要是对vue和springboot不够熟练导致的,因此写篇文章来记录分享。

二、JWT

1.介绍

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

2.请求流程

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

3.JWT的主要应用场景

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

4.JWT的结构

JWT包含了三部分

Header 头部(标题包含了令牌的元数据,并且包含签名和/或加密算法的类型)

Payload 负载 (类似于飞机上承载的物品,存放我们指定的信息)

Signature 签名/签证

将这三段信息文本用.连接一起就构成了JWT字符串

就像这样: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

三、Springboot集成JWT

1. Maven添加JWT依赖项

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        <!--token-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

2. 封装Token的生成函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  public String getToken(User user, long time) {
    Date start = new Date();//token起始时间
    long currentTime = System.currentTimeMillis() + time;
    Date end = new Date(currentTime);//token结束时间
    String token = "";
    token = JWT.create()
            .withAudience(user.getLevel().toString()+user.getId().toString()) //存放接收方的信息
            .withIssuedAt(start)//token开始时间
            .withExpiresAt(end)//token存活截止时间
            .sign(Algorithm.HMAC256(user.getPassword()));//加密
    return token;
  }

3. 编写注解类

1.UserLoginToken 需要登录才能进行操作的注解

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
  boolean required() default true;
}

2.PassToken 用来跳过验证的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
  boolean required() default true;
}

4. 编写权限拦截器(AuthenticationInterceptor)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {

  @Autowired
  UserService userService;

  @Override
  public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
    //通过所有OPTION请求
    if(httpServletRequest.getMethod().toUpperCase().equals("OPTIONS")){
      return true;
    }

    String token = httpServletRequest.getHeader("Authorization");// 从 http 请求头中取出 token
    String refreshToken = httpServletRequest.getHeader("freshToken");// 从 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;
      }
    }

    // 获取 token 中的 用户信息
    String userValue = null;
    try {
      userValue = JWT.decode(token).getAudience().get(0);
     
    } catch (JWTDecodeException j) {
      throw new RuntimeException("401");
    }
    Map<String, Object> map = new HashMap<>();
    map.put("level", (userValue).substring(0,1));
    map.put("id", (userValue).substring(1));

    User user = userService.findUser(map);
    if (user == null) {
      throw new RuntimeException("用户不存在,请重新登录");
    }

    Date oldTime = JWT.decode(token).getExpiresAt();
    Date refreshTime = JWT.decode(refreshToken).getExpiresAt();
    long oldDiff = oldTime.getTime() - new Date().getTime();//这样得到的差值是毫秒级别
    long refreshDiff = refreshTime.getTime() - new Date().getTime();//这样得到的差值是毫秒级别
    if (oldDiff <= 0) {
      if (refreshDiff <= 0) {
        httpServletResponse.sendError(401);
        throw new RuntimeException("401");
      }
    }
    String newToken = userService.getToken(user, 60* 60 * 1000);
    String newRefToken = userService.getToken(user, 24*60*60*1000);
    // 更新token
    httpServletResponse.setHeader("Authorization", newToken);
    httpServletResponse.setHeader("freshToken", newRefToken);

    //检查有没有需要用户权限的注解
    if (method.isAnnotationPresent(UserLoginToken.class)) {  // 是否使用@UserLoginToken注解
      UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
      if (userLoginToken.required()) {
        // 执行认证
        if (token == null) {
          throw new RuntimeException("=== 无token,请重新登录 ===");
        }
        // 利用用户密码,解密验证 token
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
        try {
          jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
          System.out.println("=== token验证失败 ===");
          httpServletResponse.sendError(401);
          throw new RuntimeException("401");
        }
        return true;
      }
    }
    return true;
  }

  @Override
  public void postHandle(HttpServletRequest httpServletRequest,
                         HttpServletResponse httpServletResponse,
                         Object o, ModelAndView modelAndView) throws Exception {

  }
  @Override
  public void afterCompletion(HttpServletRequest httpServletRequest,
                              HttpServletResponse httpServletResponse,
                              Object o, Exception e) throws Exception {
  }
}

5. 配置跨域请求和权限拦截器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Description 解决vue+spring boot跨域问题
 **/
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public CorsFilter corsFilter()
    {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        // 是否允许请求带有验证信息
        config.setAllowCredentials(true);

        // 允许访问的客户端域名
        // (springboot2.4以上的加入这一段可解决 allowedOrigins cannot contain the special value "*"问题)
        List<String> allowedOriginPatterns = new ArrayList<>();
        allowedOriginPatterns.add("*");
        config.setAllowedOriginPatterns(allowedOriginPatterns);

        // 设置访问源地址
        // config.addAllowedOrigin("*");
        // 设置访问源请求头
        config.addAllowedHeader("*");
        // 设置访问源请求方法
        config.addAllowedMethod("*");
        // 对接口配置跨域设置
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
  // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
  @Override
  public void addInterceptors(InterceptorRegistry registry) {

    //添加自定义拦截器和拦截路径,此处对所有请求进行拦截,除了登录界面和登录接口
    registry.addInterceptor(appInterceptor())
        .addPathPatterns("/api/sms/**")//添加拦截路径,拦截所有
        .excludePathPatterns("/login"); // 排除的拦截路径
    WebMvcConfigurer.super.addInterceptors(registry);
  }

  @Bean
  public HandlerInterceptor appInterceptor(){
    return new AuthenticationInterceptor();
  }
}

四、Vue配置JWT

1. 配置axios拦截器

axiosHelper.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import axios from 'axios';
import {Message} from 'element-ui';

// axios.defaults.timeout = 10000; //超时终止请求
axios.defaults.baseURL = 'http://localhost:8443/'; //配置请求地址


axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.withCredentials = true;

// var loadingInstance
axios.interceptors.request.use(config => {
    //Ajax请求执行该方法,请求带上token
    var token = localStorage.getItem('userToken');
    const refreshToken = localStorage.getItem('refreshToken');
    if (token !== null && token !== undefined && token !== '') {
        config.headers.Authorization = token;
        var host = window.location.host;
        config.headers['appHost'] = host;
    }
    if (refreshToken !== null && refreshToken !== undefined && refreshToken !== '') {
        config.headers.freshToken = refreshToken;
    }
    //全局配置,get请求加时间戳
    if (config.method.toLowerCase() === "get") {
        config.url += config.url.match(/\?/) ? "&" : "?";
        config.url += "_dc=" + new Date().getTime();
    }
    return config;
}, error => {  //请求错误处理
    // loadingInstance.close()
    Message.error({
        message: '加载超时'
    });
    return Promise.reject(error);
});
//
var count = 0;
axios.interceptors.response.use(response => {
        return response;
    },
    error => {
        if (error.response.status === 401) {
            if (count === 0) {
                count = count + 1;
            } else if (count > 0) {
                return null;
            }
            // debugger
            Message.error("身份信息超时,请重新登录!", { icon: 1, time: 2000 });
            $cookies.remove('userToken');
            setTimeout(function () {
                window.location.href = '/#/login';
            }, 3000);
            return Promise.reject(error);
        }
    }
    );
export default axios; //暴露axios实例

然后在main.js中配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import axiosHelper from '../src/axios/axiosHelper'
Vue.prototype.axiosHelper = axiosHelper;

2. axios接收Token, 并放入localStorage中

只需在拿到后端数据data后, 添加以下代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let obj = {
      username: data.username,
      level: data.level
}

localStorage.setItem('userToken', data.token)
localStorage.setItem('refreshToken', data.refreshToken)

五、总结

至此,我们就配置好了前后端的JWT使用环境,之后还会继续更新我在学习技术时总结的干货,希望大家多多点赞支持哦!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
2 条评论
热度
最新
感谢分享
感谢分享
回复回复点赞举报
很好
很好
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
实战SpringBoot集成JWT实现token验证【附源码】
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。
用户5224393
2020/02/24
3.2K0
实战SpringBoot集成JWT实现token验证【附源码】
SpringBoot中集成jwt实现前后端分离的token验证机制
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。
你好戴先生
2020/09/02
3.1K0
SpringBoot中集成jwt实现前后端分离的token验证机制
【SpringBoot】45、SpringBoot中整合JWT实现Token验证(注解篇)「建议收藏」
上篇文章,我们已经在 SpringBoot 中整合了 JWT 并实现了 Token 验证,那我们在实际应用中就会发现,如果每个 视图层(controller)都手动验证 token,代码就会显得特别臃肿,本篇文章主要为了解决该问题。
全栈程序员站长
2022/07/30
7080
json-web-token(JWT)
JWT的全称为json web token。不要把它想得多么高深,其实就是一种生成token的方式。一般我们访问一个系统的流程就是:请求登录接口,该接口会返回一个token,请求其他接口都要带上token,token验证通过才能访问成功,而JWT可以理解为就是生成token的一种机制。
贪挽懒月
2020/04/10
5930
拦截器快速实现api鉴权,再也不用裸奔了
此公众号大神太多,每天小编我都瑟瑟发抖,但是初学者更是主力,大神就自动略过了,高大上也会有,别着急,兄弟们多赚钱要紧哪
肉眼品世界
2020/11/17
1.3K0
拦截器快速实现api鉴权,再也不用裸奔了
如何设计一套单点登录系统
昨天介绍了API接口设计token鉴权方案,其实token鉴权最佳的实践场景就是在单点登录系统上。
Java极客技术
2022/12/02
5670
如何设计一套单点登录系统
还不会用JWT?咱们通过单点登录来熟悉下
JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案之一,今天我们一起来揭开它神秘的面纱!
程序员小跃
2020/05/18
9870
还不会用JWT?咱们通过单点登录来熟悉下
SpringBoot+JWT完成token验证
JWT官网: https://jwt.io/ JWT(Java版)的github地址:https://github.com/jwtk/jjwt
用户1212940
2022/04/13
8100
SpringBoot+JWT完成token验证
SpringCloud(八) - 自定义token令牌,鉴权(注解+拦截器),参数解析(注解+解析器)
虽然业务可以完成,但是每次都进行这样的手动鉴权和手动获取用户数据,比较繁琐,而且大量代码冗余;
化羽羽
2022/11/16
1.4K0
SpringCloud(八) - 自定义token令牌,鉴权(注解+拦截器),参数解析(注解+解析器)
基于SpringBoot的JWT单点登录
这样我们的jwt单点登录的业务就完成了,但是还存在一个问题,加入用户在访问的过程中登录密文已经过期,那么是十分影响用户体验。我们如何解决这个问题
全栈程序员站长
2022/08/26
5960
基于SpringBoot的JWT单点登录
SpringBoot整合JWT实现验证登陆
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的, 特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息, 以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
乱敲代码
2019/06/03
1.2K0
SpringBoot整合JWT
如果大家觉得有用的话,可以关注我下面的微信公众号,极客李华,我会在里面更新更多行业资讯,企业面试内容,编程资源,如何写出可以让大厂面试官眼前一亮的简历等内容,让大家更好学习编程,我的抖音,B站也叫极客李华。大家喜欢也可以关注一下
GeekLiHua
2025/01/21
1260
SpringBoot整合JWT
Java基于ssm框架的restful应用开发
Java基于ssm框架的restful应用开发 好几年都没写过java的应用了,这里记录下使用java ssm框架、jwt如何进行rest应用开发,文中会涉及到全局异常拦截处理、jwt校验、token拦截器等内容。 1、jwt工具类 直接贴代码了,主要包括jwt的sign、verify、decode三个方法,具体实现如下: package com.isoft.util; import java.util.Date; import com.auth0.jwt.JWT; import com.auth0.j
用户1141560
2018/05/28
6670
如何保证API接口安全?
在实际的业务开发过程中,我们常常会碰到需要与第三方互联网公司进行技术对接,例如支付宝支付对接、微信支付对接、高德地图查询对接等等服务,如果你是一个创业型互联网,大部分可能都是对接别的公司api接口。
纯洁的微笑
2021/07/01
1.7K0
如何保证API接口安全?
SpringBoot的旅游项目——day02(登录、拦截器)
    我们使用拦截器去配置权限无法做到细粒度的设置,所以我们可以使用自定义注解来控制权限。
上分如喝水
2021/08/16
3660
SpringBoot的旅游项目——day02(登录、拦截器)
Shiro + JWT + Spring Boot Restful 简易教程
GitHub 项目地址:https://github.com/Smith-Cruise/Spring-Boot-Shiro 。
java思维导图
2020/04/22
9110
Spring boot整合JWT
springboot整合jwt pom.xml添加依赖 <dependencies> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.
ruochen
2021/11/25
6790
如何保证API接口安全?
在实际的业务开发过程中,我们常常会碰到需要与第三方互联网公司进行技术对接,例如支付宝支付对接、微信支付对接、高德地图查询对接等等服务,如果你是一个创业型互联网,大部分可能都是对接别的公司api接口。当你的公司体量上来了时候,这个时候可能有一些公司开始找你进行技术对接了,转变成由你来提供api接口,那这个时候,我们应该如何设计并保证API接口安全呢?
老K博客
2024/12/10
1870
如何保证API接口安全?
SpringBoot 开发 -- JWT 认证教程
最开始 我们登陆的时候,是将 对象存到session当中,每次请求的时候,取session中是否存在该对象进而判断是否通过认证。
RAIN7
2022/09/28
1.3K0
SpringBoot 开发 -- JWT 认证教程
Shiro + JWT + Spring Boot Restful 简易教程
github.com/Smith-Cruise/Spring-Boot-Shiro
好好学java
2021/04/30
1.1K0
相关推荐
实战SpringBoot集成JWT实现token验证【附源码】
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验