栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

vue中通过请求头发送token到后端接收验证(解决拦截器中获取请求头信息为null,解决跨域)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

vue中通过请求头发送token到后端接收验证(解决拦截器中获取请求头信息为null,解决跨域)

我的项目的springboot+vue前后端分离项目,在做给项目中增加JWT验证时出现了许多的问题,因此写这篇博客来记录遇到的问题以及解决方法

遇到的问题

在给项目中增加jwt的token验证的时候(将token放在请求头中),后端获取不到我在前端请求头中封装好的token信息,为了解决这个问题,查阅了许多资料,在解决问题的过程中也遇到了许多新的问题,接下来就跟大家一一讲述

下面的解决方案会默认是配置了jwt拦截器验证的,jwt拦截器不知道怎么配置的跨域去看看我的jwt学习笔记博客里面有

如果讲解的有不明白的可以私信我

跨域

最初我的项目是不存在跨域问题的,但是我问了下老师我的后端接为什么收不到请求头中的信息,老师帮我看了看,怀疑可能是我的跨域没有配置好产生的问题,然后老师发了一份他的跨域配置给我来进行配置,结果更换跨域配置后就真的是出现了跨域问题

这是报的错误信息:

The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

到了后面就算我把配置修改回来,也会报错。

经过不断的查阅资料和修改后端,我的项目终于又不会出现跨域的错误,以下讲述如何配置

  1. 在控制器中全部加入@CrossOrigin注解

  2. 不知道为什么我的项目就算加入了@CrossOrigin注解也还是会报错,还需要加入WebMvcConfig配置类才没报错

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("
    export function get(url,params={}){
        return new Promise((resolve,reject) => {
            axios.get(url,{params:params})
            .then(response =>{
              console.log(response.data);
              if (response.data.state==false){
                // alert("账号信息已过期,请重新登录");
                // location.href = '/'
                // this.notify("账号信息已过期,请重新登录","false");
              }
                resolve(response.data);
            })
            .catch(err =>{
                reject(err);
            })
        });
    }
    
    
    export function post(url,data={}){
        return new Promise((resolve,reject) => {
            // axios.post(url,data,{headers:{'token': window.localStorage['token']}})
            axios.post(url,data,)
            .then(response =>{
              console.log(response.data);
              if (response.data.state==false){
                // alert("账号信息已过期,请重新登录");
                // location.href = '/'
                // this.notify("账号信息已过期,请重新登录","false");
              }
                resolve(response.data);
            })
            .catch(err =>{
                reject(err);
            })
        });
    }
    

    首先你的token数据是在登录的时候由后端返回的,然后前端接收并存储,我是选择使用localStorage来进行存储

    这个时候你的localStorage中就会存在token了,然后我们封装一个请求拦截器,让前端axios在每次给后端发送请求的时候都会携带上该请求头数据

    **注意:**如果不适用请求拦截器来设置请求头中的token,而是利用全局默认请求头设置,那么你的请求头中token信息不会进行更新,就算重新登录,携带的也会是原来的token,而不是新的token。

    特别注意:请求头中储存token数据的一定是Authorization,不能是其它名字

    //请求拦截器
    axios.interceptors.request.use(
      config => {
        //先从浏览器的localStorage存储中提取token值
        const tokenStr = localStorage.getItem('token')
        if (tokenStr) {
          config.headers.Authorization=tokenStr
        }
        return config
      })
    
    OPTIONS的问题

    首先我讲一下我是怎么发现这个问题的:我在controller中去获取我的请求头中的信息,是可以获取到的,但是在JWT验证的拦截器中无法获取得到。

    原因:

    实际上发送了两次请求,第一次为 OPTIONS 请求,第二次才 GET/POST… 请求在OPTIONS请求中,不会携带请求头的参数,所以在拦截器上获取请求头为空。

    自定义的拦截器拦截成功第一次请求不能通过,就不能获取第二次的请求了 GET/POST…第一次请求不带参数,第二次请求才带参数

    **解决:**在拦截器的最上面加上这段话

    //如果请求为 OPTIONS 请求,则返回 true,否则需要通过jwt验证
     if (HttpMethod.OPTIONS.toString().equals(request.getMethod())){
         System.out.println("OPTIONS请求,放行");
         return true;
     }
    

    到了这里基本问题就解决了,再给大家扩展一个注意点:

    拦截器需要放行静态资源

    如果你是配置了虚拟路径,那么你需要放行它的路径,否则会访问不到它的资源

    最后我就把我相关的所有代码都放上来供大家参考:

    全部代码

    jwt工具类

    public class JWTUtils {
        private static final String SIGN="!qwe@[wef";//后面从数据库中获取
    
        
        public static String getToken(Map map){
            Calendar instance = Calendar.getInstance();
            instance.add(Calendar.DATE,7);//默认7天过期
            //创建jwt builder
            JWTCreator.Builder builder = JWT.create();
            //playload
            map.forEach((k,v)->{builder.withClaim(k,v);});
    
            String token = builder.withExpiresAt(instance.getTime())//令牌过期时间
                    .sign(Algorithm.HMAC256(SIGN));//签名
    
            return token;
        }
    
        
        public static DecodedJWT verify(String token){
            return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
    
        }
    
    //    
    //    public static DecodedJWT getTokenInfo(String token){
    //        DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
    //        return verify;
    //    }
    }
    

    jwt拦截器

    @Component
    public class JWTInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
           //如果请求为 OPTIONS 请求,则返回 true,否则需要通过jwt验证
            if (HttpMethod.OPTIONS.toString().equals(request.getMethod())){
                System.out.println("OPTIONS请求,放行");
                return true;
            }
            HashMap map = new HashMap<>();
            //获取请求头中的令牌
    //        String token = request.getHeader("token");//所以前端需要将token放到请求头中
            String token = request.getHeader("Authorization");//所以前端需要将token放到请求头中
            System.out.println("请求头中的token:"+token);
    
            try {
                JWTUtils.verify(token);//验证令牌
                return true;
            }catch (SignatureVerificationException e){
                e.printStackTrace();
                map.put("msg","无效签名");
            }catch (TokenExpiredException e){
                e.printStackTrace();
                map.put("msg","token过期");
            }catch (AlgorithmMismatchException e){
                e.printStackTrace();
                map.put("msg","token算法不一致");
            }catch (Exception e){
                e.printStackTrace();
                map.put("msg","token无效");
            }
            map.put("state",false);//设置状态
            //将map转为json 使用jackson
            String json = new ObjectMapper().writeValueAsString(map);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().print(json);
            return false;
        }
    }
    

    jwt拦截器配置

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Autowired
        private HttpInterceptor httpInterceptor;
        @Autowired
        private JWTInterceptor jwtInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
    //        registry.addInterceptor(httpInterceptor).addPathPatterns("
    export function get(url,params={}){
        return new Promise((resolve,reject) => {
            axios.get(url,{params:params})
            .then(response =>{
              console.log(response.data);
              if (response.data.state==false){
                // alert("账号信息已过期,请重新登录");
                // location.href = '/'
                // this.notify("账号信息已过期,请重新登录","false");
              }
                resolve(response.data);
            })
            .catch(err =>{
                reject(err);
            })
        });
    }
    
    
    export function post(url,data={}){
        return new Promise((resolve,reject) => {
            // axios.post(url,data,{headers:{'token': window.localStorage['token']}})
            axios.post(url,data,)
            .then(response =>{
              console.log(response.data);
              if (response.data.state==false){
                // alert("账号信息已过期,请重新登录");
                // location.href = '/'
                // this.notify("账号信息已过期,请重新登录","false");
              }
                resolve(response.data);
            })
            .catch(err =>{
                reject(err);
            })
        });
    }
    
    总结

    这次遇到的问题让我对vue和springboot的跨域问题理解的更加深刻,也加深了我对于vue的理解学习。解决遇到的bug的过程就是学习的过程,加油!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/825647.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号