文章目录

  • 一、JWT是什么?
  • 二、使用步骤
    • 1.项目结构
    • 2.相关依赖
    • 3.数据库
    • 4.相关代码
  • 三、测试结果

一、JWT是什么?

在介绍JWT之前,我们先来回顾一下利用token进行用户身份验证的流程:

1、客户端使用用户名和密码请求登录

2、服务端收到请求,验证用户名和密码

3、验证成功后,服务端会签发一个token,再把这个token返回给客户端

4、客户端收到token后可以把它存储起来,比如放到cookie中

5、客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带

6、服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据

这种基于token的认证方式相比传统的session认证方式更节约服务器资源,并且对移动端和分布式更加友好。其优点如下

支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题
无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力
更适用CDN:可以通过内容分发网络请求服务端的所有资料
更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御

而JWT就是上述流程当中token的一种具体实现方式,其全称是JSON Web Token,官网地址:https://jwt.io/

通俗地说,JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。JWT的认证流程如下:

1、首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探

2、后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串
3、后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可

4、前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)

5、后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等

6、验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

最后:说白了,JWT:JSON Web Token,其实token就是一段字符串,由三部分组成:Header,Payload,Signature

二、使用步骤

1.项目结构

2.相关依赖

    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.22</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.21</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.79</version></dependency><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.4</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.1</version><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.3.8.RELEASE</version></dependency><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.18.3</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency></dependencies>

3.数据库

这里进行测试,所以用户类只有用户名密码,自行创建

4.相关代码

1、annotation包
PassToken:

package com.geesun.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author :Mr.ZJW* @date :Created 2022/2/28 10:26* @description:用来跳过验证的 PassToken*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {boolean required() default true;
}

UserLoginToken:

package com.geesun.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author :Mr.ZJW* @date :Created 2022/2/28 10:26* @description:用于登录后才能操作的token*/
/*RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,
所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {boolean required() default true;
}

2、common包
CodeMsg:

package com.geesun.common;/*** @author :Mr.ZJW* @date :Created 2022/2/28 10:26* @description:返回提示*/
public class CodeMsg {private int retCode;private String message;// 按照模块定义CodeMsg// 通用异常public static CodeMsg SUCCESS = new CodeMsg(0,"success");public static CodeMsg SERVER_EXCEPTION = new CodeMsg(500100,"服务端异常");public static CodeMsg PARAMETER_ISNULL = new CodeMsg(500101,"输入参数为空");// 业务异常public static CodeMsg USER_NOT_EXSIST = new CodeMsg(500102,"用户不存在");public static CodeMsg ONLINE_USER_OVER = new CodeMsg(500103,"在线用户数超出允许登录的最大用户限制。");public static CodeMsg SESSION_NOT_EXSIST =  new CodeMsg(500104,"不存在离线session数据");public static CodeMsg NOT_FIND_DATA = new CodeMsg(500105,"查找不到对应数据");public static CodeMsg USER_OR_PASS_ERROR = new CodeMsg(500102,"账号或者密码错误,请重试!");private CodeMsg(int retCode, String message) {this.retCode = retCode;this.message = message;}public int getRetCode() {return retCode;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}
}

Result:

package com.geesun.common;/*** @author :Mr.ZJW* @date :Created 2022/2/28 10:26* @description:返回统一结果集*/
public class Result<T> {private String message;private int retCode;private T data;private Result(T data) {this.retCode = 200;this.message = "成功";this.data = data;}private Result(CodeMsg cm) {if(cm == null){return;}this.retCode = cm.getRetCode();this.message = cm.getMessage();}/*** 成功时候的调用* @return*/public static <T> Result<T> success(T data){return new Result<T>(data);}/*** 成功,不需要传入参数* @return*/@SuppressWarnings("unchecked")public static <T> Result<T> success(){return (Result<T>) success("");}/*** 失败时候的调用* @return*/public static <T> Result<T> error(CodeMsg cm){return new Result<T>(cm);}/*** 失败时候的调用,扩展消息参数* @param cm* @param msg* @return*/public static <T> Result<T> error(CodeMsg cm,String msg){cm.setMessage(cm.getMessage()+"--"+msg);return new Result<T>(cm);}public T getData() {return data;}public String getMessage() {return message;}public int getRetCode() {return retCode;}
}

3、config包
InterceptorConfig:

package com.geesun.config;import com.geesun.Interceptor.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
import java.util.List;/*** @author :Mr.ZJW* @date :Created 2022/2/28 10:25* @description:新建Token拦截器*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录}@Beanpublic AuthenticationInterceptor authenticationInterceptor() {return new AuthenticationInterceptor();}@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> arg0) {// TODO Auto-generated method stub}@Overridepublic void addCorsMappings(CorsRegistry arg0) {// TODO Auto-generated method stub}@Overridepublic void addFormatters(FormatterRegistry arg0) {// TODO Auto-generated method stub}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry arg0) {// TODO Auto-generated method stub}@Overridepublic void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> arg0) {// TODO Auto-generated method stub}@Overridepublic void addViewControllers(ViewControllerRegistry arg0) {// TODO Auto-generated method stub}@Overridepublic void configureAsyncSupport(AsyncSupportConfigurer arg0) {// TODO Auto-generated method stub}@Overridepublic void configureContentNegotiation(ContentNegotiationConfigurer arg0) {// TODO Auto-generated method stub}@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer arg0) {// TODO Auto-generated method stub}@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {// TODO Auto-generated method stub}@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> arg0) {// TODO Auto-generated method stub}@Overridepublic void configurePathMatch(PathMatchConfigurer arg0) {// TODO Auto-generated method stub}@Overridepublic void configureViewResolvers(ViewResolverRegistry arg0) {// TODO Auto-generated method stub}@Overridepublic void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {// TODO Auto-generated method stub}@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> arg0) {// TODO Auto-generated method stub}@Overridepublic MessageCodesResolver getMessageCodesResolver() {// TODO Auto-generated method stubreturn null;}@Overridepublic Validator getValidator() {// TODO Auto-generated method stubreturn null;}}

4、Interceptor包
AuthenticationInterceptor:

package com.geesun.Interceptor;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.geesun.annotation.PassToken;
import com.geesun.annotation.UserLoginToken;
import com.geesun.pojo.User;
import com.geesun.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;/*** @author :Mr.ZJW* @date :Created 2022/2/28 10:24* @description:拦截器*/
public class AuthenticationInterceptor implements HandlerInterceptor {@AutowiredUserService userService;@Overridepublic 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;}}//检查有没有需要用户权限的注解if (method.isAnnotationPresent(UserLoginToken.class)) {UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);if (userLoginToken.required()) {// 执行认证if (token == null) {throw new RuntimeException("无token,请重新登录");}// 获取 token 中的 user idString userId;try {userId = JWT.decode(token).getAudience().get(0);} catch (JWTDecodeException j) {throw new RuntimeException("401");}User user = userService.findUserById(userId);if (user == null) {throw new RuntimeException("用户不存在,请重新登录");}// 验证 tokenJWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();try {jwtVerifier.verify(token);} catch (JWTVerificationException e) {throw new RuntimeException("401");}return true;}}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 {}
}

5、utils包
TokenUtil:

package com.geesun.utils;import com.auth0.jwt.JWT;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;/*** @author :Mr.ZJW* @date :Created 2022/2/28 10:24* @description:*/
public class TokenUtil {public static String getTokenUserId() {String token = getRequest().getHeader("token");// 从 http 请求头中取出 tokenString userId = JWT.decode(token).getAudience().get(0);return userId;}/*** 获取request* @return*/public static HttpServletRequest getRequest() {ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();return requestAttributes == null ? null : requestAttributes.getRequest();}}

6、pojo包
User:

package com.geesun.pojo;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "`user`")
public class User implements Serializable {@TableId(value = "id", type = IdType.NONE)private String id;@TableField(value = "username")private String username;@TableField(value = "password")private String password;private static final long serialVersionUID = 1L;
}

7、controller包
UserController:

package com.geesun.controller;import cn.hutool.json.JSONObject;
import com.geesun.annotation.UserLoginToken;
import com.geesun.common.CodeMsg;
import com.geesun.common.Result;
import com.geesun.pojo.User;
import com.geesun.service.UserService;
import com.geesun.service.impl.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;/*** @author :Mr.ZJW* @date :Created 2022/2/26 10:47* @description:*/
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate TokenService tokenService;/*** 查询用户信息* @return*/@UserLoginToken@GetMapping("/list")public Result<Object> list(){return Result.success(userService.list());}/*** 登录验证* @param user* @param response* @return*/@RequestMapping(value = "/login" ,method = RequestMethod.GET)public Result<Object> login(User user, HttpServletResponse response) {JSONObject jsonObject = new JSONObject();//获取用户账号密码User userForBase = new User();userForBase.setId(userService.findByUsername(user).getId());userForBase.setUsername(userService.findByUsername(user).getUsername());userForBase.setPassword(userService.findByUsername(user).getPassword());//判断账号或密码是否正确if (!userForBase.getPassword().equals(user.getPassword())) {return Result.error(CodeMsg.USER_OR_PASS_ERROR);} else {String token = tokenService.getToken(userForBase);jsonObject.put("token", token);Cookie cookie = new Cookie("token", token);cookie.setPath("/");response.addCookie(cookie);return Result.success(jsonObject);}}}

8、service包
UserService接口:

package com.geesun.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.geesun.pojo.User;public interface UserService extends IService<User> {int deleteByIds(Long[] ids);int addUser(User user);User findByUsername(User user);User findUserById(String userId);
}

UserServiceImpl实现类:

package com.geesun.service.impl;import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.geesun.mapper.UserMapper;
import com.geesun.pojo.User;
import com.geesun.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.Arrays;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Autowiredprivate UserMapper userMapper;/*** 判断用户名* @param user* @return*/public User findByUsername(User user){return userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername,user.getUsername()));}public User findUserById(String userId) {return userMapper.selectById(userId);}}

TokenService:

package com.geesun.service.impl;import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.geesun.pojo.User;
import org.springframework.stereotype.Service;
import java.util.Date;/*** @author :Mr.ZJW* @date :Created 2022/2/28 10:20* @description:*/
@Service
public class TokenService {public String getToken(User user) {Date start = new Date();long currentTime = System.currentTimeMillis() + 60* 60 * 1000;//一小时有效时间Date end = new Date(currentTime);String token = "";token = JWT.create().withAudience(user.getId()).withIssuedAt(start).withExpiresAt(end).sign(Algorithm.HMAC256(user.getPassword()));return token;}
}

9、mapper包

package com.geesun.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.geesun.pojo.User;public interface UserMapper extends BaseMapper<User> {}

三、测试结果

1、登录验证

2、查询用户信息
这个方法加上了@UserLoginToken,所以要token才能查询


3、不加上Token进行测试就会出错提示

出错提示:

Java实现Token登录验证(基于JWT的token认证实现)相关推荐

  1. jwt重放攻击_【干货分享】基于JWT的Token认证机制及安全问题

    一步一步教你基于JWT的Token认证机制实现,以及如何防范XSS攻击.Replay攻击和中间人攻击. 文章目录 一.几种常用的认证机制 1.1 HTTP Basic Auth HTTP Basic ...

  2. java token生成和验证_Java Token登录验证 生成解析Token

    借鉴参考 Java Token登录验证 使用jjwt生成和解析JWT java基于token验证之登陆验证 等 什么是Token? 我的理解来说 token就是你访问服务器的口令,只要token合法, ...

  3. 【java】基于JWT的token身份认证方案

    1.概述 转载:基于JWT的token身份认证方案 2.使用JSON Web Token的好处 2.1 性能问题 验证信息可以由前端保存,后端不需要为保存token消耗内存.JWT方式将用户状态分散到 ...

  4. 前后端分离中使用基于jwt的token进行身份认证

    基于jwt的Token认证机制可以看之前的文章: 基于JWT的Token认证机制实现 在前后端分离中,我们与前端约定一种身份认证机制.当用户登录的时候,我们会返回给前端一个token,前端会将toke ...

  5. 基于JWT的Token认证机制实现

    一.基于JWT的Token认证机制实现 1.什么是JWT JSON Web Token(JWT)是一个非常轻巧的规范.这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息. 2.JWT组成 ...

  6. ASP.NET WebApi 基于JWT实现Token签名认证

    一.前言 开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NET WebService服务中可以通过SoapHead验证机制 ...

  7. token登录验证机制

    jwt: json web token 数据 前端 秘钥 前端 请求数据的时候. 需要通过秘钥生成命令令牌. Token token是oAuth Token,提供的是认证和授权,认证针对用户,授权是针 ...

  8. flask token 登录验证

    flask token 登录验证 视频 https://www.youtube.com/watch?v=J5bIPtEbS0Q 代码 #!/usr/bin/env python # -*- encod ...

  9. Java Token登录验证 使用jjwt生成和解析JWT

    Java jjwt生成和解析Token 参考 依赖 流程 生成和解析Jwt 生成jwt 解析Jwt 实例 后端 前端 刚学会了点使用Jwt来验证登录,记录下来 参考 JSON Web Tokens官网 ...

最新文章

  1. 项目学生:带有Jersey的Web服务服务器
  2. 【渝粤教育】电大中专幼儿园课程论 (7)作业 题库
  3. oracle控制文件全备失败,Oracle数据库案例整理-恢复数据库失败-主备机控制文件所在目录不同...
  4. java dom创建xml文件_Java 如何使用dom方式读取和创建xml文件
  5. 计算机专用英语词汇pdf,计算机专用英语词汇1500词.pdf
  6. Centos 6\7下yum安装R
  7. PostgreSQL数值类型--浮点类型和序列
  8. 2021李宏毅机器学习课程笔记——Explainable AI
  9. python最全画地图,可视化数据
  10. 1~3年产品经理经典面试题
  11. Android基础之批量发送短信
  12. 父亲有过目不忘的本事
  13. 接待员如何向客人upsell_前厅部接待员办理入住操作步骤
  14. android apk sign,apk文件的sign以及align命令
  15. 「技术架构」10个提升应用程序性能的倚天剑和屠龙刀
  16. Flexbox 布局简明教程-姜威-专题视频课程
  17. 最全选型考量 + 剖析经典AOP开源库实践
  18. vtd xml java_java – 如何使用VTD-XML获取ONE元素的所有命名空间声明?
  19. 电子电路分析与设计:数字电子技术_电子电路分析浅谈
  20. php tp3.2 添加表内容,数据创建 · ThinkPHP3.2.3完全开发手册 · 看云

热门文章

  1. [BZOJ4826][HNOI2017]影魔(主席树)
  2. 需求说明定义一个超市会员类属性名字会员号余额积分打印名字 会员号 余额和积分充值的方法余额 要累加 输出一下余额消费的方法余额减 积分累加 每消费10元
  3. 身份证最后一位检验算法
  4. 便宜的谷歌Android新EKEN M009平板电脑7寸无线摄像头重力感应
  5. 抖音运营干货:抖音橱窗、抖音购物车、抖音小店、抖音直播的区别
  6. 12306订票失败,退款指南(银联建行版)
  7. 使用 data-* 属性来嵌入自定义数据:
  8. 计算机英语是啥意思啊,玩电脑英语是什么意思
  9. 做80后的生意:妈咪“十月”装
  10. JavaScript基础知识补习(4)