文章目录

  • 前言
  • 一、jwt后端配置
    • 1.导入依赖
    • 2.jwt方法类
    • 3.token生成
    • 4.jwt拦截器
    • 5.jwt拦截器配置
  • 二、jwt前端配置
    • 1.获取token
    • 2.封装axios
  • 三、踩坑记录
    • 1.静态资源被拦截(一、5)
    • 2./error被拦截(一、5)
    • 3.yml项目路径(一、5)

前言

通过jwt实现单点登录,后端项目基于spring boot实现,前端使用vue.js示范。
jwt是什么?原理是什么?诸如此类的问题这里不提,本文记录jwt登录验证的前后端实现过程及踩的坑


一、jwt后端配置

1.导入依赖

        <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.3.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.25</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency>

2.jwt方法类

包含token相关参数,生成token、验证token、解析token方法

package com.ymzhao.website.utils;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** @date 2019/4/25 11:46* @atuther net source*/
public class JwtUtil {//密钥public static final String SECRET = "sgfdsopljkhsl;o437632";//过期时间:秒public static final int EXPIRE = 60 * 1;/*** 生成Token* @param username* @return* @throws Exception*/public static String createToken(String username) throws Exception {Calendar nowTime = Calendar.getInstance();nowTime.add(Calendar.SECOND, EXPIRE);Date expireDate = nowTime.getTime();Map<String, Object> map = new HashMap<>();map.put("alg", "HS256");map.put("typ", "JWT");String token = JWT.create().withHeader(map)//头.withClaim("username", username).withSubject("测试")//.withIssuedAt(new Date())//签名时间.withExpiresAt(expireDate)//过期时间.sign(Algorithm.HMAC256(SECRET));//签名return token;}/*** 验证Token* @param token* @return* @throws Exception*/public static Map<String, Claim> verifyToken(String token)throws Exception{JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();DecodedJWT jwt = null;try {jwt = verifier.verify(token);}catch (Exception e){throw new RuntimeException("凭证已过期,请重新登录");}return jwt.getClaims();}/*** 解析Token* @param token* @return*/public static Map<String, Claim> parseToken(String token){DecodedJWT decodedJWT = JWT.decode(token);return decodedJWT.getClaims();}}

验证方法返回类为Map<String, Claim>,Claim定义如下:

有需要的话,使用它提供的格式转换方法

3.token生成

demo 如下,直接调用上一节中的token生成方法:

@Service
public class LoginServiceImpl implements LoginService {@Overridepublic String logIn(String username, String password) {// 可封装下返回类,避免直接抛异常,结构为{code: 0, data: "xxx", message: "成功"}if("admin".equals(username) && "123abc".equals(password)) {String token;try {token = JwtUtil.createToken(username);} catch (Exception e) {e.printStackTrace();throw new RuntimeException("生成token失败");}return token;}throw new RuntimeException("用户名/密码错误");}
}

返回给前端后,需同前端约定将token置于请求头中,以实现用户登录验证

4.jwt拦截器

拦截http请求,检查token的有效与否

package com.ymzhao.website.interceptor;import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.interfaces.Claim;
import com.ymzhao.website.utils.JwtUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;/*** Create By ymzhao on 2021/3/11*/
@Component
public class JwtInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {String uri = request.getRequestURI();System.out.println("uri: " + uri);String headToken = request.getHeader("Authorization");if(StringUtils.isEmpty(headToken)) {Map<String, Object> map = new HashMap<>();map.put("code", 20001);map.put("message", "Missing or invalid Authorization header");ErrorResponse(response, map);return false;}try {Map<String, Claim> map = JwtUtil.verifyToken(headToken);} catch (Exception e) {e.printStackTrace();Map<String, Object> map = new HashMap<>();map.put("code", 20002);map.put("message", "Invalid Authorization header " + e.getLocalizedMessage());ErrorResponse(response, map);return false;}return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}// 被拦截的请求响应private void ErrorResponse(HttpServletResponse response, Map<String, Object> result){OutputStream out = null;JSONObject object = new JSONObject();object.put("result", result);try{response.setCharacterEncoding("utf-8");response.setContentType("text/json");out = response.getOutputStream();out.write(object.toString().getBytes());out.flush();}catch (Exception e){e.printStackTrace();}finally{try {if (out != null) {out.close();}}catch (Exception e){e.printStackTrace();}}}
}

5.jwt拦截器配置

配置拦截模式
此处踩了三个坑:静态资源也被拦截了(三、1);项目/error路径也会被拦截(三、2);yml中设置了项目路径时,拦截路径中需要加上该路径(三、3)

package com.ymzhao.website.config;import com.ymzhao.website.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;import javax.annotation.Resource;/*** Create By ymzhao on 2021/3/11*/
@Configuration
public class JwtInterceptorConfig extends WebMvcConfigurationSupport {@Resourceprivate JwtInterceptor jwtInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册拦截器,要声明拦截器对象和要拦截的请求registry.addInterceptor(jwtInterceptor).addPathPatterns("/**") //所有路径都被拦截.excludePathPatterns("/login/**") // 排除用户登录请求.excludePathPatterns("/register/**") // 排除用户注册请求.excludePathPatterns("/error");super.addInterceptors(registry);}/*** 用来指定静态资源不被拦截,否则继承WebMvcConfigurationSupport这种方式会导致静态资源无法直接访问* @param registry*/@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");super.addResourceHandlers(registry);}
}

二、jwt前端配置

同后端的约定,登录后,返回的token存放在之后的请求头中,属性名为Authoritization

1.获取token

使用封装的axios方法(见下一节)发送请求

 this.fetchPost("/zymwb/login/log_in", {username: 'admin', password: '123abc'}).then(res => {console.log(res)const result = res.data // 示例中返回的result结构为{code: 0, data: 'xxx', message: '成功'}if(result != null && result.code == 0) { // 登录成功localStorage.setItem('token', result.data)} else { // 登录失败}});

2.封装axios

import Vue from 'vue';
import axios from 'axios';
import qs from 'qs';const host = 'http://192.168.0.179';
const baseURL = host + ':13145';axios.defaults.withCredentials = false;
axios.defaults.timeout = 2500;// 请求拦截
axios.interceptors.request.use(config => {const token = localStorage.getItem("token")config.headers.Authoritization = tokenreturn config;},err => {return Promise.reject(err); }
)
// 响应拦截
axios.interceptors.response.use(res => {return res;},err => {return Promise.reject(err);}
)
const fetch = (url, method, data) => {data = data ? data : {};let httpDefaultOpts = { //http默认配置method: method,url: baseURL + url,params:data,data:qs.stringify(data),headers: method=='get'?{"Accept": "application/json","Content-Type": "application/json; charset=UTF-8"}:{'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}}if(method =='get'){delete httpDefaultOpts.data;}else{delete httpDefaultOpts.params;}let promise = new Promise(function(resolve, reject) {axios(httpDefaultOpts).then(res => {resolve(res);}).catch(res => {reject(res);})})return promise;
}const fetchGet = (url, data) => {return fetch(url, "get", data);
}
const fetchPost = (url, data) => {return fetch(url, "post", data);
}Vue.prototype.fetchGet = fetchGet;
Vue.prototype.fetchPost = fetchPost;

三、踩坑记录

1.静态资源被拦截(一、5)

BUG记录:SpringBoot项目配置拦截器,部署后出现无法调用接口的问题

2./error被拦截(一、5)

springboot的错误处理url为/error也会被拦截,需要添加排除

3.yml项目路径(一、5)

yml中设置了项目路径时,拦截路径中需要加上该路径(三、3)

server:port: 13145servlet:context-path: /zymwb
registry.addInterceptor(jwtInterceptor).addPathPatterns("/zymwb/**") //所有路径都被拦截.excludePathPatterns("/zymwb/login/**") // 排除用户登录请求

实践:前后端分离实现JWT登录验证,包括前、后端配置相关推荐

  1. 前后端分离项目token怎么验证_前后端分离,获取token,验证登陆是否失效

    public classJwtUtils {/*** 密钥*/ private static final String SECRET="xxxx";/*** 默认字段key:exp ...

  2. 前后端分离之JWT用户认证

    前后端分离之JWT用户认证 在前后端分离开发时为什么需要用户认证呢?原因是由于HTTP协定是不储存状态的(stateless),这意味着当我们透过帐号密码验证一个使用者时,当下一个request请求时 ...

  3. 前后端分离使用 Token 登录解决方案

    前后端分离使用 Token 登录解决方案:https://juejin.im/post/5b7ea1366fb9a01a0b319612 转载于:https://www.cnblogs.com/byd ...

  4. 前后端分离解析(1)前端与后端

    经过2期,10次,分别介绍了selenium的UI自动化,和接口自动化基础. 这期换个角度,我们介绍一下前后端分离. 前后端分离解析(一):前端与后端 前端.后端,名字上比较好理解,两者都是开发,只是 ...

  5. 利用JWT安全验证(前后端分离,单点登录,分布式微服务)

    JWT官网: https://jwt.io/ JWT(Java版)的github地址:https://github.com/jwtk/jjwt JWT请求流程 用户使用账号和面发出post请求: 服务 ...

  6. 前后端分离项目实现登录(登录)

    现在大部分的项目都是前后端分离项目,在前后端分离项目中的身份验证我们经常采用JWT认证.关于JWT认证的详细内容,请移步上一篇博客. 最近做的项目恰好用到了JWT的身份验证,今天拿出来说一下: log ...

  7. [转] 前后端分离之JWT用户认证

    [From] http://www.jianshu.com/p/180a870a308a 在前后端分离开发时为什么需要用户认证呢?原因是由于HTTP协定是不储存状态的(stateless),这意味着当 ...

  8. SpringSecurity-02-基于前后端分离和JWT载体的认证授权

    文章目录 1:基本概念 1:什么是认证 2:什么是会话 3.什么是授权 2:准备工作 1:分析基于jwt的登录过程 2:springsecurity原理 3:security登录认证使用 1:认证流程 ...

  9. Django前后端分离1——jwt

    一.前戏 1.base64 方法 作用 参数 返回值 b64encode 将输入的参数转化为base64规则的串 预加密的明文,类型为bytes:例:b'guoxiaonao' base64对应编码的 ...

  10. SpringBoot+Vue+CAS 前后端分离实现单点登录方案

    点击关注公众号,利用碎片时间学习 文章目录 前言 一.CAS是什么? 二.搭建客户端系统 引入CAS 客户端后端搭建 总结 前言 什么是单点登录?单点登录全称Single Sign On(以下简称SS ...

最新文章

  1. 软件架构自学笔记——非功能特性
  2. Microbiome:掠食性粘细菌通过调节土壤微生物群落来控制黄瓜枯萎病
  3. 让我们来开发一种更类似人脑的神经网络吧(三)
  4. python最新版本-官方宣布不再维护Python2,并每年发布一个新版本
  5. python tuple list_草根学Python(三)List 和 Tuple
  6. Qt Creator使用灯光
  7. 一个由正则表达式引发的血案
  8. html评论置顶功能,微信公众号精选留言评论怎么置顶显示?功能在哪里设置?...
  9. 各个图标的意思_冬奥体育图标设计团队负责人林存真:每一个图标要画100稿以上...
  10. Linux添加用户并赋管理员权限
  11. Struts1的实现原理
  12. 树的最大独立集详解(C++)
  13. 阿里云云计算 12 对象存储 Object Storage Service OSS 的概念
  14. 免费不加密:C++基础教程完整版视频(黑马程序员)
  15. 图书管理系统(借还图书)--Java实现(附源码)
  16. openstack平台虚拟机vip设置
  17. j2ee java是什么意思,j2ee是什么
  18. LightOj 1336(Sigma Function)
  19. 串口(串行接口)相关概念
  20. Linux内核内存压缩技术

热门文章

  1. Win10系统耳机插入不起作用_依然外放声音---Windows运维工作笔记053
  2. java date计算年龄_Java中使用LocalDate根据日期来计算年龄
  3. 2.1用户界面设计的八项黄金法则
  4. JAVA计算机毕业设计政府会议管理系统Mybatis+系统+数据库+调试部署
  5. docker 入门优质文章
  6. 微信平台分账产品怎么选?
  7. 上半年晋升 P8 成功,还买了别墅!
  8. Go中的SSRF攻防战
  9. Python读文件的三种方法对比
  10. Doctrine浅析