springboot自定义拦截器,校验token

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,经济版 1GB 1个月
简介: 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
目录
相关文章
|
4天前
|
缓存 NoSQL Java
案例 采用Springboot默认的缓存方案Simple在三层架构中完成一个手机验证码生成校验的程序
案例 采用Springboot默认的缓存方案Simple在三层架构中完成一个手机验证码生成校验的程序
50 5
|
2天前
|
消息中间件 Java Maven
深入理解Spring Boot Starter:概念、特点、场景、原理及自定义starter
深入理解Spring Boot Starter:概念、特点、场景、原理及自定义starter
|
7天前
|
XML 前端开发 Java
SpringBoot参数校验@Validated、@Valid(javax.validation)详解
SpringBoot参数校验@Validated、@Valid(javax.validation)
22 4
|
1天前
|
安全 Java 机器人
Spring Boot中的自定义过滤器
Spring Boot中的自定义过滤器
|
4天前
|
Java
springboot自定义log注解支持EL表达式
springboot自定义log注解支持EL表达式
13 0
|
5天前
|
IDE Java Maven
Spring Boot启动失败问题:hile scanning for the next token found character '@'
Spring Boot启动失败问题:hile scanning for the next token found character '@'
|
7天前
|
前端开发 JavaScript Java
【Spring Boot】 深入理解Spring Boot拦截器:自定义设计与实现全攻略
【Spring Boot】 深入理解Spring Boot拦截器:自定义设计与实现全攻略
11 0
|
1月前
|
前端开发 Java 程序员
Spring Boot统一功能处理(拦截器, 统一数据返回格式, 统一异常处理)
Spring Boot统一功能处理(拦截器, 统一数据返回格式, 统一异常处理)
33 1
|
1月前
|
Java
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
|
1月前
|
Java 数据库连接 数据安全/隐私保护
springBoot集成token认证,最全Java面试知识点梳理
springBoot集成token认证,最全Java面试知识点梳理