CRM项目解决Spring-Security拦截Filter并验证token合法性时线程不一致导致的错误------CRM项目

package com.alatus.config.filter;

import com.alatus.constant.Constants;
import com.alatus.model.TUser;
import com.alatus.result.R;
import com.alatus.service.RedisService;
import com.alatus.util.JSONUtils;
import com.alatus.util.JWTUtils;
import com.alatus.util.ResponseUtils;
import com.alatus.result.CodeEnum;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Component
public class TokenVerifyFilter extends OncePerRequestFilter {

    @Resource
    private RedisService redisService;
    @Resource
    //    springboot框架提供的线程池,ioc容器内已经存在
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (request.getRequestURI().equals(Constants.LOGIN_URI)) { //如果是登录请求,此时还没有生成jwt,那不需要对登录请求进行jwt验证
            //验证jwt通过了 ,让Filter链继续执行,也就是继续执行下一个Filter
            filterChain.doFilter(request, response);
        } else {
            String token = request.getHeader(Constants.TOKEN_NAME);
            if(!StringUtils.hasText(token)){
//                没拿到token,将失败这个枚举传回去,解析并取出常量拼接
                R result = R.FAIL(CodeEnum.TOKEN_IS_EMPTY);
//                封装
                String resultJSON = JSONUtils.toJSON(result);
//                返回
                ResponseUtils.write(response,resultJSON);
                return;
            }
//            验证token有没有被篡改过,也是验证token合法性
            if (!(JWTUtils.verifyJWT(token))){
//                token不合法
                R result = R.FAIL(CodeEnum.TOKEN_IS_NONE_MATCH);
//                封装
                String resultJSON = JSONUtils.toJSON(result);
//                返回
                ResponseUtils.write(response,resultJSON);
                return;
            }
            TUser tUser = JWTUtils.parseUserFromJWT(token);
            String redisToken = (String) redisService.getValue(Constants.REDIS_JWT_KEY + tUser.getId());
            if(!StringUtils.hasText(redisToken)){
//                没有获取到内容说明token过期了
                R fail = R.FAIL(CodeEnum.TOKEN_IS_EXPIRED);
                String json = JSONUtils.toJSON(fail);
                ResponseUtils.write(response,json);
                return;
            }
            if (!redisToken.equals(token)) {
//                登陆失败token错误
                R result = R.FAIL(CodeEnum.TOKEN_IS_ERROR);
//                把R对象转为JSON
                String json = JSONUtils.toJSON(result);
                ResponseUtils.write(response,json);
                return;
            }
//            jwt验证通过了
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(tUser,tUser.getLoginPwd(),tUser.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//            刷新一下token
//            做异步执行
//            new Thread(new Runnable() {
//                @Override
//                public void run() {
                    这里刷新token即可
                    从请求头中获取
//                    String rememberMe = request.getHeader("rememberMe");
//                    if (!Boolean.parseBoolean(rememberMe)) {
//                        redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
//                    }
//                }
//            }).start();
//            最好使用线程池的方式去执行
//            threadPoolTaskExecutor.execute(() -> {
//                    这里刷新token即可
//                    从请求头中获取
//                    String rememberMe = null;
//                    if(StringUtils.hasText(request.getHeader(Constants.REMEMBER_ME))){
//                        rememberMe = request.getHeader(Constants.REMEMBER_ME);
//                    }
//                    if (!Boolean.parseBoolean(rememberMe)) {
//                        redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
//                    }
//            });
//            验证jwt通过了,让filter链继续执行
            
            
//            经过实验,线程池有概率导致请求和我们的service走的线程不是同一个导致请求头里面获取这个rememberMe报错
//            还是不用线程为好,慢点就慢点吧
            String rememberMe = null;
            if(StringUtils.hasText(request.getHeader(Constants.REMEMBER_ME))){
                rememberMe = request.getHeader(Constants.REMEMBER_ME);
            }
            if (!Boolean.parseBoolean(rememberMe)) {
                redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
            }
            filterChain.doFilter(request,response);
        }
    }
}
package com.alatus.config.filter;

import com.alatus.constant.Constants;
import com.alatus.model.TUser;
import com.alatus.result.R;
import com.alatus.service.RedisService;
import com.alatus.util.JSONUtils;
import com.alatus.util.JWTUtils;
import com.alatus.util.ResponseUtils;
import com.alatus.result.CodeEnum;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Component
public class TokenVerifyFilter extends OncePerRequestFilter {

    @Resource
    private RedisService redisService;
    @Resource
    //    springboot框架提供的线程池,ioc容器内已经存在
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (request.getRequestURI().equals(Constants.LOGIN_URI)) { //如果是登录请求,此时还没有生成jwt,那不需要对登录请求进行jwt验证
            //验证jwt通过了 ,让Filter链继续执行,也就是继续执行下一个Filter
            filterChain.doFilter(request, response);
        } else {
            String token = request.getHeader(Constants.TOKEN_NAME);
            if(!StringUtils.hasText(token)){
//                没拿到token,将失败这个枚举传回去,解析并取出常量拼接
                R result = R.FAIL(CodeEnum.TOKEN_IS_EMPTY);
//                封装
                String resultJSON = JSONUtils.toJSON(result);
//                返回
                ResponseUtils.write(response,resultJSON);
                return;
            }
//            验证token有没有被篡改过,也是验证token合法性
            if (!(JWTUtils.verifyJWT(token))){
//                token不合法
                R result = R.FAIL(CodeEnum.TOKEN_IS_NONE_MATCH);
//                封装
                String resultJSON = JSONUtils.toJSON(result);
//                返回
                ResponseUtils.write(response,resultJSON);
                return;
            }
            TUser tUser = JWTUtils.parseUserFromJWT(token);
            String redisToken = (String) redisService.getValue(Constants.REDIS_JWT_KEY + tUser.getId());
            if(!StringUtils.hasText(redisToken)){
//                没有获取到内容说明token过期了
                R fail = R.FAIL(CodeEnum.TOKEN_IS_EXPIRED);
                String json = JSONUtils.toJSON(fail);
                ResponseUtils.write(response,json);
                return;
            }
            if (!redisToken.equals(token)) {
//                登陆失败token错误
                R result = R.FAIL(CodeEnum.TOKEN_IS_ERROR);
//                把R对象转为JSON
                String json = JSONUtils.toJSON(result);
                ResponseUtils.write(response,json);
                return;
            }
//            jwt验证通过了
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(tUser,tUser.getLoginPwd(),tUser.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//            刷新一下token
//            做异步执行
//            new Thread(new Runnable() {
//                @Override
//                public void run() {
                    这里刷新token即可
                    从请求头中获取
//                    String rememberMe = request.getHeader("rememberMe");
//                    if (!Boolean.parseBoolean(rememberMe)) {
//                        redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
//                    }
//                }
//            }).start();
//            最好使用线程池的方式去执行
//            threadPoolTaskExecutor.execute(() -> {
//                    这里刷新token即可
//                    从请求头中获取
//                    String rememberMe = null;
//                    if(StringUtils.hasText(request.getHeader(Constants.REMEMBER_ME))){
//                        rememberMe = request.getHeader(Constants.REMEMBER_ME);
//                    }
//                    if (!Boolean.parseBoolean(rememberMe)) {
//                        redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
//                    }
//            });
//            验证jwt通过了,让filter链继续执行
            
            
//            经过实验,线程池有概率导致请求和我们的service走的线程不是同一个导致请求头里面获取这个rememberMe报错
//            还是不用线程为好,慢点就慢点吧
            String rememberMe = null;
            if(StringUtils.hasText(request.getHeader(Constants.REMEMBER_ME))){
                rememberMe = request.getHeader(Constants.REMEMBER_ME);
            }
            if (!Boolean.parseBoolean(rememberMe)) {
                redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
            }
            filterChain.doFilter(request,response);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值