springboot自定义拦截器,校验token

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: springboot自定义拦截器,校验token

一、自定义拦截器

package com.example.demo.test;
 
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
 
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
 
public class LoginInterceptor implements HandlerInterceptor {
 
    @Autowired
    private RedisService redis;
    @Autowired
    private UserService userService;
 
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws IOException {
        String tokenName = "Authorization";
        // 尝试从header中取token
        String token = request.getHeader(tokenName);
        //尝试从参数中取token
        if (StrUtil.isEmpty(token)) {
            token = request.getParameter(tokenName);
        }
        //尝试从cooke
        if (StrUtil.isEmpty(token)) {
            Cookie[] cookies = request.getCookies();
            for (Cookie cookie : cookies) {
                if (Objects.equals(cookie.getName(), tokenName)) {
                    token = cookie.getValue();
                }
            }
        }
        //如果前端没有携带token返回json数据
        if (StrUtil.isBlank(token)) {
            PrintWriter pw = response.getWriter();
            pw.write(JSON.toJSONString("用户未登录"));
            return false;
        }
        //解析token
        String jwtId = JwtTool.checkJwtToken(token);
        if (jwtId == null) {
            throw new ServiceException(401, "用户未登录");
        }
        //获取用户ID
        String tokenUserId = JwtTool.getUserId(token);
        //token存在,但是redis不存在。要么是失效,要么是强制下线
        JwtInfo jwt = redis.get(RedisKey.getToken(tokenUserId, jwtId));
        if (jwt == null || !jwt.getToken().equals(token)) {
            throw new ServiceException(401, "您当前登录的账号已失效,请重新登录");
        }
        //获取用户ID
        Long userId = jwt.getUserId();
        //查询用户
        User user = userService.selectById(userId);
        //判断用户是否存在
        if (user == null) {
            throw new ServiceException(MsgCode.CODE_UNAUTHORIZED, "用户不存在");
        }
        //根据业务需求增加其他判断条件
        if (user.getStatus() == 1) {
            throw new ServiceException(MsgCode.CODE_UNAUTHORIZED, "用户已禁用!");
        }
        //将登录用户放到ThreadLocal变量变量中,方便业务获取当前登录用户
        CurrentUser currentUser = new CurrentUser();
        currentUser.setId(userId);
        currentUser.setUserName(user.getAccountname());
        currentUser.setNickName(user.getName());
        //当前用户放到ThreadLocal变量变量中
        CurrentUserUtil.set(currentUser);
        return true;
    }
}
 

涉及其他类

public class CurrentUserUtil {
 
    private CurrentUserUtil() {
    }
 
    private static final ThreadLocal<CurrentUser> CURRENT_USER = new ThreadLocal<CurrentUser>();
 
    public static void set(CurrentUser currentUser) {
        CURRENT_USER.set(currentUser);
    }
 
 
    public static CurrentUser currentUser() {
        return CURRENT_USER.get();
    }
 
    public static void remove() {
        CURRENT_USER.remove();
    }
}
package com.example.demo.test;
 
import lombok.Getter;
import lombok.Setter;
 
import java.io.Serializable;
import java.util.List;
 
/**
 * 当前登录用户信息
 */
@Getter
@Setter
public class CurrentUser implements Serializable {
 
    private static final long serialVersionUID = -327159787234887122L;
 
    private Long id;
 
    private String jwtId;
    /**
     * 当前用户姓名或昵称
     */
    private String nickName;
    /**
     * 当前用户账户
     */
    private String userName;
    /**
     * 当前用户电话
     */
    private String phone;
    /**
     * 当前用户部门ID
     */
    private Long deptId;
    /**
     * 当前用户部门名称
     */
    private String deptName;
    /**
     * 当前用户部门级别 0为中心 1位部门
     */
    private Integer level;
    /**
     * 当前用户是否是部门管理员
     */
    private Integer flag;
    /**
     * 当前用户角色
     */
    private List<String> authorityList;
}
package com.example.demo.test;
 
import lombok.Getter;
import lombok.Setter;
 
@Getter
@Setter
public class JwtInfo implements java.io.Serializable {
    /**
     * 唯一凭证
     */
    private String jwtId;
    /**
     * 用户ID
     */
    private Long userId;
    /**
     * token
     */
    private String token;
}
package com.example.demo.test;
 
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.jwt.JWT;
 
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
 
public class JwtTool {
 
    /**
     * 有效期
     **/
    private static int expDays = 7;
 
    /**
     * 签发者
     **/
    private static String issuer = "xxxxxx";
 
    /**
     * 秘钥
     **/
    private static String key = "xxxxx";
 
    /**
     * 有效载荷
     **/
    public interface Payload {
        String userId = "userId";
        String jwtId = "jwtId";
    }
 
    /**
     * 创建token
     *
     * @param userId
     * @return
     */
    public static JwtInfo createJwtToken(Long userId) {
        String jwtId = UUID.randomUUID().toString();
        try {
            String token = JWT.create()
                    .setIssuer(issuer)
                    .setIssuedAt(DateTime.now())
                    .setJWTId(jwtId)
                    .setCharset(Charset.forName("utf-8"))
                    //有效载荷
                    .setPayload(Payload.userId, userId)
                    .setPayload(Payload.jwtId, jwtId)
                    .setKey(key.getBytes("utf-8"))
                    //7天有效期
                    .setExpiresAt(DateUtil.offsetDay(DateUtil.date(), expDays))
                    .sign();
            JwtInfo jwtInfo = new JwtInfo();
            jwtInfo.setUserId(userId);
            jwtInfo.setJwtId(jwtId);
            jwtInfo.setToken(token);
            return jwtInfo;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 验证token
     *
     * @param token
     * @return jwtId
     */
    public static String checkJwtToken(String token) {
        JWT jwt = JWT.of(token);
        //如果验证成功
        boolean status;
        try {
            status = jwt.setKey(key.getBytes("utf-8")).verify();
            if (status) {
                Object jwtId = jwt.getPayload(Payload.jwtId);
                if (jwtId != null) {
                    return jwtId.toString();
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 验证token
     *
     * @param token
     * @return jwtId
     */
    public static String getUserId(String token) {
        JWT jwt = JWT.of(token);
        return jwt.getPayload(Payload.userId).toString();
    }
}
package com.example.demo.test;
 
/**
 * 生成RedisKey
 *
 */
public interface RedisKey { 
    /**
     * token
     */
    String TOKEN = "token:%s:%s";
 
    static String getToken(String id, String jwtId) {
        String format = String.format(TOKEN, id,jwtId);
        return format;
    }
    
}
package com.example.demo.test;
 
 
/**
 * 业务逻辑异常
 *
 */
public class ServiceException extends RuntimeException {
 
    /**
     *
     */
    private static final long serialVersionUID = 5909435651426033878L;
    private Integer code;
 
    public ServiceException(String message) {
        super(message);
        this.code = 201;
    }
 
    public ServiceException(Integer code, String message) {
        super(message);
        this.code = code;
    }
 
    public ServiceException(Throwable cause) {
        super(cause);
    }
 
    public ServiceException(String message, Throwable cause) {
        super(message, cause);
    }
 
    public Integer getCode() {
        return code;
    }
 
    public void setCode(Integer code) {
        this.code = code;
    }
}
package com.example.demo.test;
 
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
 
import java.io.Serializable;
import java.util.Date;
 
@Getter
@Setter
public class User implements Serializable {
    /**
     * 用户id
     */
    private Long id;
    /**
     * 姓名
     */
    private String name;
 
 
    /**
     * 密码
     */
    @JsonIgnore
    private String password;
    
    /**
     * 账户
     */
    private String userName;
    
    
    private Date createTime;
}

二、将拦截器加入系统拦截器

package com.example.demo.config;
 
import com.example.demo.test.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 
@Configuration
public class WebConfig implements WebMvcConfigurer {
 
    @Bean
    public LoginInterceptor loginInterceptor() {
        return new LoginInterceptor();
    }
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/api/admin/common/user/login/**",
                        "/api/admin/common/user/kaptcha/**"
                );
    }
}

三、系统任何位置获取当前登录用户方式

CurrentUser currentUser = CurrentUserUtil.currentUser();
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
1月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
150 0
|
2月前
|
Java 容器
如何在SpringBoot项目中使用过滤器和拦截器
过滤器和拦截器是日常开发中常用技术,用于对特定请求进行增强处理,如插入自定义代码以实现特定功能。过滤器在请求到达 `servlet` 前执行,而拦截器在请求到达 `servlet` 后执行。`SpringBoot` 中的拦截器依赖于 `SpringBoot` 容器,过滤器则由 `servlet` 提供。通过实现 `Filter` 接口并重写 `doFilter()` 方法可实现过滤器;通过实现 `HandlerInterceptor` 接口并重写相应方法可实现拦截器。两者的主要区别在于执行时机的不同,需根据具体场景选择使用。
179 4
如何在SpringBoot项目中使用过滤器和拦截器
|
1月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
162 2
|
2月前
|
Java Spring
springboot静态资源目录访问,及自定义静态资源路径,index页面的访问
本文介绍了Spring Boot中静态资源的访问位置、如何进行静态资源访问测试、自定义静态资源路径和静态资源请求映射,以及如何处理自定义静态资源映射对index页面访问的影响。提供了两种解决方案:取消自定义静态资源映射或编写Controller来截获index.html的请求并重定向。
springboot静态资源目录访问,及自定义静态资源路径,index页面的访问
|
1月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
26 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
1月前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
58 2
|
1月前
|
前端开发 Java 数据库
springBoot:template engine&自定义一个mvc&后端给前端传数据&增删改查 (三)
本文介绍了如何自定义一个 MVC 框架,包括后端向前端传递数据、前后端代理配置、实现增删改查功能以及分页查询。详细展示了代码示例,从配置文件到控制器、服务层和数据访问层的实现,帮助开发者快速理解和应用。
|
2月前
|
前端开发 Java Spring
springboot自定义拦截器的简单使用和一个小例子
本文介绍了如何在Spring Boot中创建和使用自定义拦截器,通过一个登录验证的示例,演示了拦截器在MVC流程中的preHandle、postHandle和afterCompletion三个环节的作用,并说明了如何在Spring Boot配置类中注册拦截器。
|
3月前
|
存储 JSON 前端开发
SpringBoot 如何实现无感刷新Token
【8月更文挑战第30天】在Web开发中,Token(尤其是JWT)作为一种常见的认证方式,被广泛应用于身份验证和信息加密。然而,Token的有效期问题常常导致用户需要重新登录,从而影响用户体验。为了实现更好的用户体验,SpringBoot可以通过无感刷新Token的机制来解决这一问题。以下将详细介绍SpringBoot如何做到无感刷新Token。
131 2