基于Token的JWT认证

JWT:Json web token 是为了在网络应用环境间传递声明而执行的一种基于JSON传输格式的开放标准,可实现无状态、分布式的Web应用授权。
缺点:用户主动注销,服务器不能让token主动失效。

认证过程大致如下:
用户登陆服务器,服务端验证用户账号密码,使用secret生成JWT令牌【一般还会设置过期时间,在生成token的时候指定】,然后将令牌返回给客户端。
客户端访问服务端的时候,在请求头中带上这个令牌,服务端使用secret去验证令牌是否合法,合法则让用户访问服务器接口,不合法则拒绝。
服务器并不保存token,生成的JWT令牌是按照某种规则生成的,它可以包含用户信息,比如我们把用户id或带有权限标识的用户JSON数据[做权限校验]存到JWT令牌,他第二次带着JWT令牌登录时,解析JWT令牌得到他的用户id,此时拿着用户id去查询那个用户信息,得到账号密码进行校验。

项目依赖

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

自定义注解

跳过验证注解

//用来跳过验证的PassToken
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {boolean required() default true;
}

需要验证注解

//需要登录才能进行操作的注解LoginToken
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginToken {boolean required() default true;
}

用户实体类

@Data
@ApiModel("登陆用户信息")
public class TSBaseUser {@ApiModelProperty("id")private String id;@ApiModelProperty("用户名")private String userName;@ApiModelProperty("密码")@JsonIgnoreprivate String password;}

用户service

public interface IBaseUserService extends IService<TSBaseUser> {TSBaseUser login(TSBaseUser loginUser);TSBaseUser getUser(String id);}
@Service
public class BaseUserServiceImpl extends ServiceImpl<BaseUserMapper, TSBaseUser> implements IBaseUserService {@Overridepublic TSBaseUser login(TSBaseUser loginUser) {LambdaQueryWrapper<TSBaseUser> wrapper = new LambdaQueryWrapper<>();wrapper.eq(TSBaseUser::getUserName , loginUser.getUserName()).eq(TSBaseUser::getPassword , loginUser.getPassword());TSBaseUser baseUser = baseMapper.selectOne(wrapper);if (ObjectUtils.isNotEmpty(baseUser)){return baseUser;}return null;}@Overridepublic TSBaseUser getUser(String id) {return baseMapper.selectById(id);}
}

JWT生成Token

@Service
public class TokenService {/*** 过期时间5分钟*/private static final long EXPIRE_TIME = 5 * 60 * 1000;public String getToken(TSBaseUserVo user) {Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);String token="";token= JWT.create().withAudience(user.getId()) // 将 user id 保存到 token 里面.withExpiresAt(date) //五分钟后token过期.sign(Algorithm.HMAC256(user.getPassword())); // 以 password 作为 token 的密钥return token;}
}

用户信息存储

public class BaseUserInfo {private static final ThreadLocal<Map<String, String>> THREAD_LOCAL = new ThreadLocal<>();//判断线程map是否为空,为空就添加一个mappublic static Map<String, String> getLocalMap() {Map<String, String> map = THREAD_LOCAL.get();if (map == null) {map = new HashMap<>(10);THREAD_LOCAL.set(map);}return map;}//把用户信息添加到线程map中public static void set(String key, String name) {Map<String, String> map = getLocalMap();map.put(key, name);}//获得线程map中的数据public static String get(String key) {Map<String, String> map = getLocalMap();return map.get(key);}//把用户信息添加到线程map中public static void setUser(String username, String userId) {Map<String, String> map = getLocalMap();map.put("username", username);map.put("userId", userId);}// 获取登陆用户名public static String getUserName() {Map<String, String> map = getLocalMap();return map.get("username");}// 获取登陆用户idpublic static String getUserId() {Map<String, String> map = getLocalMap();return map.get("userId");}
}

拦截器

public class JwtInterceptor implements HandlerInterceptor{@Autowiredprivate IBaseUserService 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(LoginToken.class)) {LoginToken loginToken = method.getAnnotation(LoginToken.class);if (loginToken.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");}TSBaseUser user = userService.getUser(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");}// 存储登陆用户信息BaseUserInfo.setUser(user.getUserName() , userId);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 {}
}

注册拦截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer{@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor()).addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录//注册TestInterceptor拦截器
//        InterceptorRegistration registration = registry.addInterceptor(jwtInterceptor());
//        registration.addPathPatterns("/**");                      //添加拦截路径
//        registration.excludePathPatterns(                         //添加不拦截路径
//            "/**/*.html",            //html静态资源
//            "/**/*.js",              //js静态资源
//            "/**/*.css",             //css静态资源
//            "/**/*.woff",
//            "/**/*.ttf",
//            "/swagger-ui.html",
//            "/doc.html/**"
//        );}@Beanpublic JwtInterceptor jwtInterceptor() {return new JwtInterceptor();}
}

全局异常捕获

package com.qyzb.handle;import com.qyzb.base.result.R;
import com.qyzb.base.result.ResultCode;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;/*** @title: GlobalExceptionHandler* @Author gjt* @Date: 2020-12-22* @Description:*/
@RestControllerAdvice
public class GlobalExceptionHandler {@ResponseBody@ExceptionHandler(Exception.class)public R<?> handleException(Exception e) {return R.failure(ResultCode.ERROR);}
}

controller层

@RestController
@RequestMapping("/login")
@Api(value = "登陆", tags = "登陆")
public class LoginController {@Resourceprivate IBaseUserService baseUserService;@Resourceprivate TokenService tokenService;@Resourceprivate RedisCache redisCache;@RequestMapping(value = "" , method = {RequestMethod.GET,RequestMethod.POST})@ApiOperation("登陆")@PassTokenpublic R<?> login(TSBaseUser loginUser){// 获取登陆用户信息TSBaseUser user = baseUserService.login(loginUser);// 从jwt存储的用户信息获取 BaseUserInfo.getUserName()if (ObjectUtils.isEmpty(user)){return R.failure(ResultCode.of("用户名或密码错误"));}else {Map<String,Object> data = new HashMap<>();data.put("user",user);data.put("token",tokenService.getToken(user));return R.success(data);}}}

自定义结果返回类

@Data
@ApiModel("返回结果")
public class R<T> {/*** 错误码*/@ApiModelProperty("错误码")private Integer code;/*** 错误消息*/@ApiModelProperty("错误消息")private String msg;/*** 内容*/@ApiModelProperty("内容")private T data;public static <U> R<U> toResult(int rows) {return rows > 0 ? R.success() : R.failure();}public static <U> R<U> success() {return of(SUCCESS, null, null);}public static <U> R<U> success(U data) {return of(SUCCESS, null, data);}public static <U> R<U> failure() {return of(FAILURE, null, null);}public static <U> R<U> failure(ResultCode resultCode) {return of(resultCode, null, null);}public static <U> R<U> failure(ResultCode resultCode, String detail) {return of(resultCode, detail, null);}public static <U, E extends CodeException> R<U> exception(E ex) {return failure(ex.getCode(), ex.getDetail());}public static <U> R<U> of(ResultCode resultCode, String detail, U data) {R<U> result = new R<>();result.code = resultCode.code();if (StringUtils.isEmpty(detail)) {result.msg = resultCode.message();} else {//覆盖消息result.msg = detail;}result.data = data;return result;}
}

返回结果枚举

public enum ResultCode {SUCCESS(200,"成功"),FAILURE(500,"失败"),ERROR(10000,"未知原因出错"),SERVICE_ERROR(50000,"服务器异常");ResultCode(Integer code, String message) {this.code = code;this.message = message;}private Integer code;private String message;public Integer code() {return this.code;}public String message() {return this.message;}public static ResultCode of(String message) {if (ObjectUtils.isEmpty(message)) {throw new RuntimeException("参数错误");}for (ResultCode resultCode : values()) {if (resultCode.message.equals(message)) {return resultCode;}}return ERROR;}}

参考文章:SpringBoot集成JWT实现token验证

SpringBoot+JWT实现登陆token验证并存储用户信息相关推荐

  1. python实现用户登陆(sqlite数据库存储用户信息)

    目录 创建数据库数据库管理简单登陆 有些地方还未完善. 创建数据库 import sqlite3 #建一个数据库 def create_sql():sql = sqlite3.connect(&quo ...

  2. SpringBoot+jwt+shiro实现登录验证及接口权限校验

    SpringBoot+jwt+shiro+token实现对接口权限校验 最近在一个项目上实现登录模块,就想到了权限验证功能,了解了Spring Security和Shiro之后,决定使用Shiro来实 ...

  3. 使用ASP.NET 2.0 Profile存储用户信息

    概要:许多ASP.NET应用程序需要跨访问的用户属性跟踪功能,在ASP.NET1.1中,我们只能人工实现这一功能.但如今,使用 ASP.NET 2.0的Profile对象,这个过程变得异常简单.Ste ...

  4. 微信授权登录及存储用户信息(fastadmin开发项目)

    最近做了一个微信投票系统,这是第一次开发微信项目,所以特此记录一下 微信投票系统,最优先想到的就是授权登录,以及获取用户信息 1.首先需要申请微信公众号(此次使用的是服务号) 2.在微信公众平台基本配 ...

  5. jwt token 附加用户信息_SpringBoot+JWT实现token验证并将用户信息存储到@注解内

    springboot集成jwt实现token验证 1.引入jwt依赖 io.jsonwebtoken jjwt 0.9.0 com.auth0 java-jwt 3.9.0 2.自定义两个注解 /** ...

  6. java jwt刷新_基于springboot+jwt实现刷新token过程解析

    前一段时间讲过了springboot+jwt的整合,但是因为一些原因(个人比较懒)并没有更新关于token的刷新问题,今天跟别人闲聊,聊到了关于业务中token的刷新方式,所以在这里我把我知道的一些点 ...

  7. springboot+h5页面+微信公众号获取微信用户信息

    springboot项目,h5页面通过微信公众号获取微信用户信息 最近本人有一个项目需求,微信公众号里点击一个菜单进入一个商城购物系统. 对于在微信公众号还是小白的我来说难度有点大,但是做完后发现也就 ...

  8. java和redis统计在线,在SpringBoot中使用Redis的zset统计在线用户信息

    统计在线用户的数量,是应用很常见的需求了.如果需要精准的统计到用户是在线,离线状态,我想只有客户端和服务器通过保持一个TCP长连接来实现.如果应用本身并非一个IM应用的话,这种方式成本极高. 现在的应 ...

  9. cas服务器中如何存储用户信息,CAS3.5.2 Server登录后返回用户信息详细解决方案

    单点登录(Single Sign-On, 简称SSO)是目前比较流行的服务于企业业务整合的解决方案之一,SSO使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.大家在使用时CA ...

最新文章

  1. 1、时间、FHS 学习笔记
  2. Ubuntu 中python 2 升级 (切换)3
  3. 图解netstat命令实例
  4. linux桌面旋转了180度,[多图]回顾每一款默认Ubuntu壁纸
  5. 【 HRBUST - 1055】Single(模拟,dp,打表)(总结)
  6. STM32那点事(4)_DMA(上)
  7. C语言中字母转换问题
  8. lwip路由实现_TCP超时与重传《LwIP协议栈源码详解——TCP/IP协议的实现》
  9. maven笔记(2)
  10. apktool 反编译 java_APK文件使用ApkTool解包反编译和重新打包及签名
  11. RDD之一:总体介绍
  12. Jquery_artDialog对话框弹出
  13. 以太坊分叉的缘由:著名的The DAO事件
  14. 电脑右下角自动弹出窗口(弹窗)
  15. Ubuntu使用总结二
  16. 学习-Java包装类之Double类(9)
  17. Python之Pandas文本处理
  18. 量化新手初识基金绩效分析
  19. Activity生命周期走向分析
  20. 7-2 寻找大富翁 (25 分)

热门文章

  1. scal开发环境搭建
  2. 期初余额录入功能暂时不能执行
  3. css3:图片实现无缝滚动的效果(走马灯)
  4. python中input是什么_Python中input函数的用法是什么?_后端开发
  5. 普通相片打印纸如何长时间保存
  6. 《20个月赚130亿-YouTube创始人陈士骏自传》- 简评
  7. AOJ-AHU-OJ-6 Hero in Maze
  8. 网络加密主要有以下几种方式_针对脱发的治疗主要有以下几种方法
  9. jquery 文件管理
  10. 学习手扎20190313——dick操作(增删改查)